Skip to content

Conversation

@sandrahoang686
Copy link
Contributor

@sandrahoang686 sandrahoang686 commented Jul 1, 2025

Related Ticket: #19

@sandrahoang686
Copy link
Contributor Author

sandrahoang686 commented Jul 1, 2025

@nathanielrindlaub here is an update on current state:

Screenshot 2025-07-01 at 11 24 22 AM

Changes:

  • map added using react-map-gl to statistics modal
  • DonutMarker component and logic created and am able to display on map but currently using some mockdata (and hardcoded values) from a random earthquakes endpoint

Todo:

  • Replace the mockdata location coordinates with the camera deployment location coordinates which is already being passed into MapView
  • Connect the stats in modal (label counts at Object, Image, Burst, and Independent Detection levels) to visualize on donut markers
  • Styling updates/refinements
  • Clean up

@nathanielrindlaub
Copy link
Member

@sandrahoang686 - totally open to suggestions and alternative approaches, but I did some thinking and outlined some steps for one way we could wire this up to the backend.

Summary of the current Stats Dashboard dataflow:

When a user opens the Stats Dashboard, the objects Panel opens by default , and the ObjectPanel.jsx fetches the stats at both the object-level and image-level aggregations (actual aggregations are performed in the animl-api repo here). When we get a response, the tasks.imageStats slice of Redux state is updated to look like this:

tasks: {
  imagesStats: {
      imageCount: 7939,
      imageReviewCount: {
        reviewed: 56,
        notReviewed: 7883
      },
      imageLabelList: {
        fox: 17,
        empty: 4300,
        person: 40,
        ...
      },
      objectCount: 7808,
      objectReviewCount: {
        reviewed: 56,
        notReviewed: 7752
      },
      objectLabelList: {
        fox: 17,
        empty: 4300,
        person: 43,
        ...
      },
      imageReviewerList: <reviewer_list>,
      multiReviewerCount: 1
    },
    burstStats: null,
    independentDetectionStats: null,
    ...
}

If users then toggle the "Burst" or "Independent Detections" tabs, we perform new fetches and flesh out tasks.imageStats.burstStats and tasks.imageStats.independentDetectionStats with data that looks like:

tasks: {
    ...
    burstsStats: {  // after clicking burst tab, this data is enriched 
      burstCount: 2308,
      burstLabelList: {
        person: 22,
        empty: 1528,
        fox: 12,
        ...
      }
    }
    ...
}

For the maps, we primarily care about modifying the <image|object|burst|independentDetection>LabelLists to provide label counts at the deployment-level.

Where we need to go:

I think in order to break out the Label counts by deployment and at each different aggregation level, we could do the following:

  1. Refactor to support returning label counts by deployment:
  • Backend & frontend: this is just housekeeping, but I don't love the "List" part of imageLabelList property names. Start by renaming "imageLabelList", "obejctLabelList", etc. to objectLevelStats / imageLevelStats, and update the frontend to use those new prop names too.
  • Backend: refactor getImageAndObjectStats() to iterate over and process deployments one-by-one (like getBurstStats() and getIndependentDetectionStats() already do).
  • Backend: return <image|object|burst|independentDetection>LevelStats payloads to the frontend with this shape:
imageLevelStats: {
	<deploymentId1>: { <labelId1>: 1, <labelId2>: 2, ... },
	<deploymentId2>: { <labelId1>: 1, <labelId2>: 2, ... },
	...
}
objectLevelStats: {
	<deploymentId1>: { <labelId1>: 1, <labelId2>: 2, ... },
	<deploymentId2>: { <labelId1>: 1, <labelId2>: 2, ... },
	...
}
  • Frontend: create Redux Selectors (kind of like this one), named something like selectObjectLevelStatsTotals, that will roll up the totals of all the deployment data to use for all of the other stats dashboard components. Use these selectors to get current stats dashboard components working again.
  1. Integrate the deployment-level label count data into the Maps:
  • Frontend: Make the MapView.jsx component(s) children of each aggregation-level Panel and use data from tasks.imageStats, tasks.burstStats, etc. to populate them (in the same pattern used for all of the other Panel components)
  • Frontend: prep the data for use in the map, probably by a combination of enriching the deployment-level label count data with the deployment's locations (which can already be found in Redux in the projects.camConfigs state) and filtering out "default" deployments.
  • Frontend: figure out how to warn users if some of their data isn't showing up in the maps b/c (1) they need to create custom deployments for their cameras or (2) the deployments they did create don't have locations.
  • Frontend: in the SaveDeploymentForm.jsx, improve validation on lat/long inputs to make sure they are valid decimal degrees lat/long figures. But also lat/long should be optional.

@sandrahoang686
Copy link
Contributor Author

@nathanielrindlaub thank you for the detailed outline of thoughts! 😮 💯 . This all does make sense to me and currently i can't think of another way of doing this better. Might be able to refine or have thoughts to optimize when in progress. But just to clarify on the shape of the api responses more...

for example ...getImageAndObjectStats() currently returns this and we want to update the response to include the LabelList breakdown by deployment. So do we expect/want the update to look something like this...

  imagesStats: {
      imageCount: 7939,
      imageReviewCount: {
        reviewed: 56,
        notReviewed: 7883
      },
>>>>>>>>>>>>>>>>ORIGINAL>>>>>>>>>>>>>>>>>>>>
      imageLabelList: {
        fox: 17,
        empty: 4300,
        person: 40,
        ...
      },
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>UPDATE>>>>>>>>>>>>>>>>>>>>>>
      imageLabelList: {
	<deploymentId1>: { <labelId1>: 1, <labelId2>: 2, ... },
	<deploymentId2>: { <labelId1>: 1, <labelId2>: 2, ... },
	...
      },
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

      objectCount: 7808,
      objectReviewCount: {
        reviewed: 56,
        notReviewed: 7752
      },
>>>>>>>>>>>>>>>>ORIGINAL>>>>>>>>>>>>>>>>>>>>
      objectLabelList: {
        fox: 17,
        empty: 4300,
        person: 43,
        ...
      },
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>UPDATE>>>>>>>>>>>>>>>>>>>>>>
      objectLabelList: {
	<deploymentId1>: { <labelId1>: 1, <labelId2>: 2, ... },
	<deploymentId2>: { <labelId1>: 1, <labelId2>: 2, ... },
	...
      },
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

      imageReviewerList: <reviewer_list>,
      multiReviewerCount: 1
    },
    burstStats: null,
    independentDetectionStats: null,
    ...
}

Then we get just get the count by each label regardless of deployment by sorting logic in the frontend?

OR

Do we want to keep the original response as is and just add the label list by deployment level on top of it...

tasks: {
  imagesStats: {
      imageCount: 7939,
      imageReviewCount: {
        reviewed: 56,
        notReviewed: 7883
      },
      imageLabelList: {
        fox: 17,
        empty: 4300,
        person: 40,
        ...
      },
>>>>>>>>>>>>>>ADDED>>>>>>>>>>>>>>>>>>>>>
      imageLevelStats: {
	<deploymentId1>: { <labelId1>: 1, <labelId2>: 2, ... },
	<deploymentId2>: { <labelId1>: 1, <labelId2>: 2, ... },
	...
      },
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      objectCount: 7808,
      objectReviewCount: {
        reviewed: 56,
        notReviewed: 7752
      },
      objectLabelList: {
        fox: 17,
        empty: 4300,
        person: 43,
        ...
      },
      imageReviewerList: <reviewer_list>,
      multiReviewerCount: 1
    },
    burstStats: null,
    independentDetectionStats: null,
    ...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants