feat(map): add polar grid overlay to Map Analysis and Unified maps (#3971)#3983
Conversation
…3971) Extend the polar grid overlay (range rings + azimuth sectors centered on the own-node position, #2307) from NodesTab to two more map surfaces, reusing the existing `PolarGridOverlay` component and `getPolarGridRings()` utility. Map Analysis: - New `PolarGridLayer` draws a theme-colored grid centered on each active source's own-node position (resolved per `sourceId`). - Toggle added to the toolbar, disabled when no active source has a known own-node position. Unified / Dashboard map: - Renders one grid per source that has an own-node position, each colored to match that source's per-source color (via `resolveSourceColor`), with a legend so overlapping grids stay distinguishable. - Toggle reuses the existing `MapContext.showPolarGrid` (unified across map surfaces), disabled when no source has a position. Own-node positions are resolved from each source's local `nodeNum` (surfaced by `GET /api/sources/:id/status`) paired with that node's position in the shared node list — see new `getOwnNodePositions` util and `useOwnNodePositions` hook. MeshCore sources (no meshtastic nodeNum) naturally yield no grid. `PolarGridOverlay` gains an optional `color` prop; when set it recolors the whole grid at reduced opacity (theme palette otherwise, unchanged for NodesTab). Tests: new ownNodePositions unit tests, PolarGridOverlay color-override test, and a Map Analysis toolbar disabled-state test. Full suite green (0 failures). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015AFBA76hsjqhsXe1BdnYub
|
Claude finished @Yeraze's task —— View job PR Review: feat(map): add polar grid overlay to Map Analysis and Unified maps (#3971)
Overall this is a clean, well-architected feature addition. The reuse of Potential Bugs1. Stale checked={showPolarGrid && hasOwnNode}The checkbox visual state is correctly gated on The same pattern is used identically in 2.
3. // eslint-disable-next-line react-hooks/exhaustive-deps
}, [nodes, activeSourceIds.join(','), allSourceIds.join(','), statusKey(statuses)]);
Code Quality4.
5. Legend The polar grid legend is positioned at 6. If Performance7. Color sort re-computes on every render ( const colorSourceIds = [...allSourceIds].sort();This spread-and-sort runs on every render of Test Coverage8. No test for the legend rendering or the polar grid toggle in The new polar-grid legend HTML ( 9. useSourceStatuses: () => new Map(),This is the right choice to avoid touching existing assertions, but it means Minor / Nits
Summary
Code quality is high, the architecture correctly avoids the singleton anti-pattern, multi-source scoping is respected, and the |
Closes #3971.
Extends the polar grid overlay (range rings + azimuth sectors centered on the own-node position, from #2307) — previously only in
NodesTab— to two more map surfaces. Reuses the existingPolarGridOverlaycomponent andgetPolarGridRings()utility rather than duplicating logic.1. Map Analysis view
PolarGridLayer(src/components/MapAnalysis/layers/PolarGridLayer.tsx) draws a polar grid centered on each active source's own-node position, resolved persourceId. Uses the theme-awareoverlayColors.polarGridpalette (matching NodesTab).MapAnalysisToolbar, disabled when no active source has a known own-node position (LayerToggleButtongaineddisabled/titleprops).PaneinMapAnalysisCanvasso its labels sit below node markers.polarGridlayer in the localStorage-backedMapAnalysisConfig.2. Unified / Dashboard map
resolveSourceColor), with a legend so overlapping grids aren't confused.MapContext.showPolarGrid(already shared across map surfaces and round-tripped to/api/user/map-preferences), disabled when no source has a position.Shared plumbing
getOwnNodePositionsutil +useOwnNodePositionshook resolve each source's own node from its localnodeNum(surfaced byGET /api/sources/:id/statusvia the manager'sgetStatus()) paired with that node's position in the shared node list. MeshCore sources (no meshtasticnodeNum) naturally yield no grid.PolarGridOverlaygains an optionalcolorprop; when set it recolors the whole grid at reduced opacity. Omitted ⇒ unchanged theme behavior (NodesTab / Map Analysis). The Dashboard passes a resolved literal color because Leaflet paints SVG strokes via the presentation attribute, which does not evaluate CSSvar().Tests
ownNodePositions.test.ts(8 cases: nested/flat position, multi-source, null/MeshCore skip, Null Island, string nodeNum coercion).PolarGridOverlay.test.tsxwith a color-override case.tscadds no new errors, lint clean.🤖 Generated with Claude Code
https://claude.ai/code/session_015AFBA76hsjqhsXe1BdnYub