feat(map): in-app Site Planner coverage estimate round-trip (#2058)#2081
Conversation
…ic#2058) Adds the outbound half of the Site Planner integration: a user can kick off a coverage run from inside the app and get the styled GeoJSON back on the map without manually round-tripping through the browser + share sheet. - SitePlannerParameters: the flat query-contract model + URL builder (run=1, bridge=1), validation bounds, and radio prefill — device dBm→W, preset→rx sensitivity table, and firmware-accurate frequency via LoRaChannelCalculator. - CoverageEstimateRunner: a headless WKWebView + WKUserScript __meshtasticNative shim forwarding to a WKScriptMessageHandler (WKWebView has no object injection), attached full-size/alpha-0 so WebGL/WASM gets a GL context; 45s timeout, deterministic teardown, publishes state + importedResult. - CoverageEstimateForm: SwiftUI sheet mirroring the planner's collapsible panels (Transmitter / Receiver / Simulation / Display) with device/node/map-center location shortcuts, progress + cancel, and error handling. - MapDataManager.importFromString: import an in-memory GeoJSON string via the existing processUploadedFile pipeline (reuses meshtastic#2056/meshtastic#2037 validation + styling). - Entry points: map-toolbar button (MeshMapMK) and an "Estimate Coverage" action on node detail (Logs section) via a new .coverageEstimate(nodeNum) map state, consumed from onChange(mapState) / onChange(selectedTab) / onAppear so a cold Map tab handoff isn't dropped. On import: enable the overlay, add its config, and recenter on the transmitter. Tests: query-URL builder, validation, prefill math, and importFromString. Docs: docs/user/map.md (+nodes.md) regenerated into the bundled docs.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds a Site Planner coverage-estimate flow with validated parameters, radio-prefilled settings, hidden WebView execution, GeoJSON overlay import, map and node entry points, tests, and user documentation. ChangesSite Planner coverage workflow
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related issues
Possibly related PRs
Sequence Diagram(s)sequenceDiagram
participant NodeDetail
participant MeshMapMK
participant CoverageEstimateForm
participant CoverageEstimateRunner
participant SitePlanner
participant MapDataManager
NodeDetail->>MeshMapMK: request coverage estimate for node
MeshMapMK->>CoverageEstimateForm: present prefilled form
CoverageEstimateForm->>CoverageEstimateRunner: start with parameters
CoverageEstimateRunner->>SitePlanner: load autorun planner URL
SitePlanner-->>CoverageEstimateRunner: send coverage GeoJSON
CoverageEstimateRunner->>MapDataManager: import GeoJSON string
MapDataManager-->>CoverageEstimateRunner: return overlay metadata
CoverageEstimateRunner-->>MeshMapMK: publish imported result
MeshMapMK->>MeshMapMK: enable overlay and recenter map
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Meshtastic/Resources/docs/index.json (1)
358-390: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAdd the coverage terms to the Nodes keywords.
Search matchespage.keywordsand translated keywords, so this page won’t surface for queries likecoverage,estimate,planner, orcellularbarsunless those terms are added to the index.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Meshtastic/Resources/docs/index.json` around lines 358 - 390, Add the missing coverage-related terms “coverage”, “estimate”, “planner”, and “cellularbars” to the keywords array for the Nodes page in index.json, preserving the existing keyword entries and structure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Meshtastic/Helpers/SitePlanner/CoverageEstimateRunner.swift`:
- Around line 122-127: Update CoverageEstimateRunner.cancel() to set didFinish =
true when cancellation occurs, matching the terminal-state handling in fail()
and handleCoverage(_:). Keep the existing timeout cancellation, teardown, and
state reset behavior unchanged.
In `@Meshtastic/Views/Nodes/Helpers/Map/CoverageEstimateForm.swift`:
- Around line 130-137: The numeric fields in CoverageEstimateForm, especially
the bindings used by labeledNumber for latitude, longitude, and receiver
sensitivity, should use text-backed bindings instead of TextField(value:format:
.number). Add manual string-to-number parsing and number-to-string formatting
while preserving the existing parameter updates and support for intermediate “-”
and “.” input.
---
Outside diff comments:
In `@Meshtastic/Resources/docs/index.json`:
- Around line 358-390: Add the missing coverage-related terms “coverage”,
“estimate”, “planner”, and “cellularbars” to the keywords array for the Nodes
page in index.json, preserving the existing keyword entries and structure.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 103ac2ce-6409-43be-91f0-bbcca4928510
📒 Files selected for processing (18)
Meshtastic.xcodeproj/project.pbxprojMeshtastic/Helpers/MapDataManager.swiftMeshtastic/Helpers/SitePlanner/CoverageEstimateRunner.swiftMeshtastic/Helpers/SitePlanner/SitePlannerParameters.swiftMeshtastic/Resources/docs/index.jsonMeshtastic/Resources/docs/markdown/user/map.mdMeshtastic/Resources/docs/markdown/user/nodes.mdMeshtastic/Resources/docs/user/map.htmlMeshtastic/Resources/docs/user/nodes.htmlMeshtastic/Router/NavigationState.swiftMeshtastic/Views/Nodes/Helpers/Map/CoverageEstimateForm.swiftMeshtastic/Views/Nodes/Helpers/NodeDetail.swiftMeshtastic/Views/Nodes/MeshMapMK.swiftMeshtasticTests/MapDataManagerTests.swiftMeshtasticTests/SitePlannerParametersTests.swiftMeshtasticTests/SwiftUIViewSnapshotTests.swiftdocs/user/map.mddocs/user/nodes.md
Replace the wrapping/hyphenating location shortcut labels with compact equal-width icon-over-label cells under a 'Set coordinates from' header, so 'My Location' / 'Map Center' stay on one line.
817a82c to
2069157
Compare
…ge fields Address CodeRabbit feedback on meshtastic#2081: - CoverageEstimateRunner.cancel() now sets didFinish so an in-flight bridge message can't import stale coverage after teardown (matches fail()/handleCoverage). - Latitude/Longitude/Sensitivity use a text-backed DecimalField so intermediate '-'/'.' input parses reliably instead of resetting the bound value to nil.
…erage-2058 # Conflicts: # Meshtastic/Resources/docs/index.json
Closes #2058.
What changed?
Implements the outbound half of the Site Planner integration (Option A — headless WKWebView bridge): a user can start a coverage run from inside the app and get the styled GeoJSON back onto the map without manually round-tripping through the browser + share sheet. Builds on the already-merged styling (#2037) and import (#2056) work.
SitePlannerParameters— the flat query-contract model + URL builder (run=1,bridge=1, percent-encoded name), validation bounds mirroring the planner's ranges, and radio prefill: device dBm→W (W = 10^((dBm−30)/10), guarding the firmware "0 = region max" case), a preset→receiver-sensitivity table (per the planner'sparameters.md, e.g. LONG_FAST → −139 dBm), and firmware-accurate frequency via the existingLoRaChannelCalculator.CoverageEstimateRunner— a headlessWKWebViewthat injects a__meshtasticNativeshim at.atDocumentStart(WKWebView has no object injection, unlike Android'saddJavascriptInterface) forwarding coverage GeoJSON to aWKScriptMessageHandler. Attached full-size /alpha 0behind the progress UI so WebGL/WASM gets a GL context and the planner's autorun completes. 45 s timeout, deterministic + idempotent teardown (removes the message handler → no retain cycle), publishesstate+importedResult.CoverageEstimateForm— a SwiftUI sheet mirroring the planner's collapsible panels (Site/Transmitter → Receiver → Simulation Options → Display), with My Location / Node / Map Center shortcuts, palette picker, live validation, a cancelable "Estimating coverage…" overlay, and graceful failure.MapDataManager.importFromString(_:name:)— imports an in-memory GeoJSON string via the existingprocessUploadedFilepipeline (reuses fix(map): import GeoJSON handed to the app via Open In / Share Sheet #2056/Render GeoJSON overlays that style with color/rgb() #2037 validation + render + styling for free), with name sanitization and the 10 MB cap.MeshMapMK) and an Estimate Coverage action on node detail (Logs section, gated on a valid position) via a new.coverageEstimate(nodeNum)map state, consumed fromonChange(mapState)/onChange(selectedTab)/onAppearso a cold Map-tab handoff isn't dropped. On import: the overlay master toggle is enabled, the imported file's config is added toenabledOverlayConfigs, and the map recenters on the transmitter.Why did it change?
Issue #2058 asks for the missing outbound estimate: letting a user kick off a coverage run from the app (map control or node detail), have the planner compute it, and get the styled GeoJSON back onto the map — instead of manually round-tripping through the browser + share sheet. This matches the Android reference (meshtastic/Meshtastic-Android#6136) for a fully in-app flow.
How is this tested?
SitePlannerParametersTests— query-URL builder (keys / omission / percent-encoding), validation bounds, high-res range clamp, dBm→W conversion, the sensitivity table, and prefill (incl. US LongFast slot 20 → 906.875 MHz and the region-maxtxPower==0fallback).MapDataManagerImportFromStringTests— valid import,.geojsonextension enforcement + name sanitization, invalid-JSON and oversized-string rejection.xcodebuild -scheme Meshtastic -destination 'generic/platform=iOS Simulator'builds clean; sourcekit-lsp reports no diagnostics on the new/changed files.Targets the hosted
site.meshtastic.org— the native bridge + full flat query contract (planner #74) are merged + deployed, so there is no deploy gate.Screenshots/Videos (when applicable)
Checklist
docs/user/ordocs/developer/, and updated accordingly (added a "Coverage Estimate (Site Planner)" section todocs/user/map.md, updateddocs/user/nodes.md, and regenerated the bundled docs).Summary by CodeRabbit
.geojsonenforcement, and size limits.