Skip to content

fix(map): anchor neighbor-info lines to the merged node marker position (#3642)#3648

Merged
Yeraze merged 1 commit into
mainfrom
fix/3642-unified-map-neighbor-lines
Jun 22, 2026
Merged

fix(map): anchor neighbor-info lines to the merged node marker position (#3642)#3648
Yeraze merged 1 commit into
mainfrom
fix/3642-unified-map-neighbor-lines

Conversation

@Yeraze

@Yeraze Yeraze commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #3642.

On the Unified (multi-source) map, a node's marker renders at its merged / override-aware position, but neighbor-info lines were drawn from the source-specific coordinates embedded in each neighbor record (ni.nodeLatitude/Longitude, ni.neighborLatitude/Longitude). When the same node is reported at different positions by different sources, the line floats away from the marker it should connect to.

Fix

NodesTab.tsx now resolves each neighbor-line endpoint to the rendered marker positionnodePositions (keyed by nodeNum), the same memoized merged-position map the markers use — via a new pure helper resolveMapEndpoint. It falls back to the record's embedded coords only when the node isn't currently on the map. The resolved coords are used consistently for the line, the distance readout, the bearing, and the direction arrows.

Testing

  • New unit tests for resolveMapEndpoint (prefer marker, fall back to embedded, return null when neither) — nodeHelpers.test.ts (62 passed).
  • tsc clean on the edited files.

🤖 Generated with Claude Code

…on (#3642)

On the Unified (multi-source) map, a node's marker renders at its merged /
override-aware position, but neighbor-info lines were drawn from the
source-specific coordinates embedded in each neighbor record. When the two
diverge (the same node reported at different positions by different sources),
the line floats away from the marker it should connect to.

Resolve each neighbor-line endpoint to the rendered marker position
(`nodePositions`, keyed by nodeNum — the same map the markers use), falling
back to the record's embedded coords only when the node isn't currently on the
map. Applied to the line, distance, bearing, and direction arrows so they stay
consistent.

Adds a pure `resolveMapEndpoint` helper in nodeHelpers with unit tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011JEaCGwY9Wz8jeV4e22GW4
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


PR Review: fix(map): anchor neighbor-info lines to the merged node marker position (#3642)

  • Read changed files
  • Review code quality and best practices
  • Check for potential bugs
  • Check performance considerations
  • Check test coverage
  • Post final review

Summary

This is a clean, well-scoped fix. The root cause was clearly identified and the solution is minimal and correct: neighbor-info line endpoints now resolve to the rendered marker position via the existing nodePositions map rather than the source-specific embedded coordinates in the neighbor record.


Code Quality

nodeHelpers.tsresolveMapEndpoint (src/utils/nodeHelpers.ts:122-132)

The helper is small, pure, and well-documented. The JSDoc block thoroughly explains the problem context, which is appreciated for future maintainers.

One minor nit: the function signature uses number | null | undefined for the embedded coord params. Since null and undefined are both handled by != null, this is correct. No issues.

NodesTab.tsx (src/components/NodesTab.tsx:2449-2456)

The replacement is clean. The two resolveMapEndpoint calls mirror each other symmetrically, and the early return on !nodeEndpoint || !neighborEndpoint (line 2452) correctly replaces what was previously four separate null checks. The downstream uses of nodeLat, nodeLng, neighborLat, neighborLng (lines 2455-2456) are unchanged from before, which is good — only the source of truth changed, not the consumption pattern.

One observation: nodePositions is keyed by nodeNum (numeric) and ni.nodeNum/ni.neighborNodeNum must also be numeric for Map.get to work. Worth confirming these types match — though since this code was already working for the marker rendering path, it's almost certainly fine.


Potential Bugs

No bugs found. The fallback path (embeddedLat != null && embeddedLng != null) correctly handles the case where a node isn't currently rendered on the map (e.g., it's outside the max-age filter). This was the previous behavior and is preserved.

One edge case that the fix doesn't address (and likely doesn't need to): nodes where hideFromMap is true are excluded from nodePositions (line 1344), so their lines would fall back to embedded coords. This is correct behavior — a hidden node has no marker to anchor to, so falling back to embedded coords is the best option.


Performance

No regressions. resolveMapEndpoint is O(1) (Map.get). It's called twice per neighbor-info record, same as the previous coordinate lookups. nodePositions was already memoized (useMemo, line 1349).


Security

No security concerns. This is purely a UI coordinate resolution fix.


Test Coverage

The three unit tests in nodeHelpers.test.ts (lines 6-25) cover the three meaningful cases:

  1. Marker position preferred over embedded coords ✓
  2. Fallback to embedded coords when node not on map ✓
  3. Returns null when neither is available ✓

The mixed-null test on line 23 (resolveMapEndpoint(markers, 1, 40.5, undefined)) is a good edge case — it verifies that having one coord but not the other returns null.

One optional improvement would be a test where embeddedLat is valid but embeddedLng is null (the reverse of line 23), but this is already implicit from the implementation and not strictly necessary.


Minor Suggestions (non-blocking)

  1. nodeHelpers.test.ts:23 — The test description says "returns null when neither a marker nor embedded coords are available" but the second assertion (resolveMapEndpoint(markers, 1, 40.5, undefined)) actually tests "one coord present but not the other." Consider splitting this into a separate it block with a more accurate description for clarity.

  2. The existing useMemo dependency array at line 1358 uses a .join(',') string of node positions — this already picks up position changes, so the new resolveMapEndpoint calls will automatically see updated positions when nodes move. No action needed, just confirming the dependency tracking is correct.


Verdict

Approved. The fix is correct, minimal, well-tested, and follows the existing patterns in the codebase. The new helper function is properly extracted into nodeHelpers.ts where it belongs, and the JSDoc explains the multi-source context clearly.

@Yeraze Yeraze merged commit e2c35c4 into main Jun 22, 2026
19 checks passed
@Yeraze Yeraze deleted the fix/3642-unified-map-neighbor-lines branch June 22, 2026 15:14
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.

Unified Map: neighbor info lines don't follow merged node marker position

1 participant