Skip to content

fix(map): restore last-viewed region on launch (fixes (0,0) cold-open)#2115

Open
bruschill wants to merge 1 commit into
meshtastic:mainfrom
bruschill:fix/mesh-map-region-persistence
Open

fix(map): restore last-viewed region on launch (fixes (0,0) cold-open)#2115
bruschill wants to merge 1 commit into
meshtastic:mainfrom
bruschill:fix/mesh-map-region-persistence

Conversation

@bruschill

@bruschill bruschill commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

What & why

Follow-up to the "path forward" from @garthvh's review of #2018.

The Mesh Map's one-shot frameInitialRegionIfNeeded() centers on phone GPS → connected device position → node centroid, guarded by didInitialFrame. A location-denied user with no connected device and no positioned nodes hits none of the three, so the frame never fires and MKMapView falls back to its default world view centered near (0,0) — the "South Atlantic / Null Island" cold-open — on every launch, since nothing was ever persisted.

Per the agreed path forward, this is the region-persistence-only fix — no pending-camera-command plumbing, no follow-the-user floor, no new camera machinery:

  • Persist the last-viewed MKCoordinateRegion via @AppStorage (four Doubles, since MKCoordinateRegion isn't Codable).
  • Restore it as the first choice in frameInitialRegionIfNeeded(), before the GPS/device/centroid path. Anyone who has ever moved the map reopens exactly where they left it, independent of GPS/device/node data. It consumes the one-shot, so the user still owns the camera immediately after.

Not persisting the bug

Saving happens on pan/zoom settle (the existing regionRefreshKey onChange), guarded so the degenerate default is never written back:

  • Whole-world guard — a region must span < 90° latitude and < 180° longitude; you can't fit the entire world under half-extent on both axes, so MapKit's settled default is always rejected.
  • Null-Island guard — a broad span still centered within 1° of (0,0) is rejected too (the intermediate pre-interaction default), while a genuine small-span local view near (0,0) still saves.
  • Restore re-validates finiteness, coordinate range, and positive span; a non-positive saved span is the "nothing saved yet" sentinel.

Behavior

  • Fresh install (nothing saved): unchanged — frames current location → connected radio → node centroid.
  • After you've moved the map: reopens your last-viewed area and zoom on next launch.

Analogous to the already-documented "Filters are remembered between launches" (nodes.md).

Docs

Documented the behavior in docs/user/map.md and regenerated the bundled docs (scripts/build-docs.sh); generated HTML/JSON not hand-edited.

Testing

  • SourceKit-LSP diagnostics clean for MeshMapMK.swift; SwiftLint clean for the new code (only the pre-existing type_body_length on the struct).
  • Reviewed end-to-end through the ClusterMapView camera coordinator: the one-shot cameraCommand id-dedup + isApplyingExternalRegion feedback guard mean restore correctly consumes the one-shot.
  • Manually walked the persist/restore guards for NaN / ±inf / out-of-range / whole-world inputs — all rejected on both write and read.

Note: the persist/restore validation currently lives as private methods on the view; a small extraction into a testable helper (to pin the accept/reject boundaries in MeshtasticTests) is a reasonable follow-up if desired.

Summary by CodeRabbit

  • New Features

    • The map now reopens to the last viewed area and zoom level between app launches.
    • On a fresh install, the map frames the current location, connected radio, or node center in that order.
  • Documentation

    • Updated Map & Waypoints documentation to describe map restoration and initial framing behavior.

The Mesh Map's one-shot frameInitialRegionIfNeeded() centers on phone GPS
-> connected device -> node centroid. A location-denied user with no
connected device and no positioned nodes hits none of the three, the frame
never fires, and MKMapView shows its default world view centered near (0,0)
-- the "South Atlantic / Null Island" cold-open -- on every launch.

Persist the last-viewed MKCoordinateRegion via @AppStorage and restore it as
the first choice in the one-shot, so anyone who has ever moved the map reopens
where they left it, independent of GPS / connected device / node data. No new
camera machinery, per the path forward in meshtastic#2018.

- Save on pan/zoom settle, guarding against the degenerate (0,0)/near-global
  default MapKit reports before the first real frame so the bug is never
  persisted (whole-world span guard + a Null-Island center guard).
- Round-trip validation (finite, in-range, positive span) on restore.
- Document the behavior in docs/user/map.md and regenerate the bundled docs.
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d3678604-9cd8-43f8-ade2-0d615cd3b61a

📥 Commits

Reviewing files that changed from the base of the PR and between e531f15 and 8cff513.

📒 Files selected for processing (5)
  • Meshtastic/Resources/docs/index.json
  • Meshtastic/Resources/docs/markdown/user/map.md
  • Meshtastic/Resources/docs/user/map.html
  • Meshtastic/Views/Nodes/MeshMapMK.swift
  • docs/user/map.md

📝 Walkthrough

Walkthrough

The map now saves a validated camera region after movement and restores it on launch. Documentation describes this behavior and the fresh-install framing fallback order, with updated search metadata.

Changes

Map camera persistence

Layer / File(s) Summary
Camera storage and restore
Meshtastic/Views/Nodes/MeshMapMK.swift
Stores validated map center and span values, persists settled region changes, and restores the saved region before applying fresh-install framing fallbacks.
Map behavior documentation
Meshtastic/Resources/docs/markdown/user/map.md, Meshtastic/Resources/docs/user/map.html, docs/user/map.md, Meshtastic/Resources/docs/index.json
Documents remembered map area and zoom, describes fresh-install framing order, and updates map search metadata and character count.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: garthvh

Poem

I hopped through the map at dawn,
Saved the view before moving on.
The camera remembers each zoomed-in trail,
Then frames fresh nodes when memories fail.
“Back to your burrow!” the bunny sings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the map-region restore fix and matches the main change.
Description check ✅ Passed The description covers the change, rationale, testing, and docs; only template formatting and checklist details are missing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

1 participant