Skip to content

[PARENT] [ALIGNMENT]: Waypoint GeofencesΒ #114

Description

@jamesarich

πŸ“ Waypoint geofences β€” client spec

Important

A waypoint can carry a region, not just a pin. It defines a circle (radius) and/or a rectangle (bounding box), and any client can raise a local notification when a tracked node crosses the boundary β€” on enter, on exit, optionally limited to favorites. Geofences travel with the waypoint across the mesh β€” there is no app-local geofence model. Every receiver evaluates the node positions it sees against the geofences it knows, and decides its own notifications. Crossing is judged against other nodes' reported positions over the mesh, not the device's own location β€” so this is point-in-region math on each incoming position, not OS region monitoring.

Tracking: design#114 Β· Protobufs: protobufs#962 (2.7.26 / 06d729a) Β· Reference build: Meshtastic-Apple#2001 (open, builds clean)

Proto surface (source of truth)

Waypoint gains five fields (all coordinates degrees Γ—1e-7, same as Position):

# Field Type Meaning
9 geofence_radius uint32 Circular geofence radius in meters, centered on the waypoint's lat/lon. 0 = no circle.
10 bounding_box BoundingBox Optional rectangular geofence. May be used instead of, or in addition to, the radius.
11 notify_on_enter bool Notify when a tracked node enters the geofence (circle and/or box).
12 notify_on_exit bool Notify when a tracked node exits.
13 notify_favorites_only bool Gate enter/exit notifications to nodes the receiver has marked favorite. Resolved locally per receiver.

BoundingBox is an axis-aligned WSEN box (longitude_west_i, latitude_south_i, longitude_east_i, latitude_north_i), field order matching GeoJSON / PMTiles so the same box can drive an offline-tile extract.

Behavior spec (all clients)

  • Inside test: a point is inside the geofence if it's within the circle (distance ≀ geofence_radius) or inside the bounding box (south ≀ lat ≀ north and west ≀ lon ≀ east). Either shape present counts; both may be set.
  • Crossing β†’ notification: on each received node position, test it against every waypoint that has notify_on_enter or notify_on_exit set. Keep per-pair inside/outside state keyed (waypointId, nodeNum). The first sighting of a pair only sets a baseline (no notification) β€” so a fresh launch can't fire spurious alerts. Only a genuine inside↔outside transition notifies: enter fires if notify_on_enter, exit fires if notify_on_exit.
  • Favorites gate: when notify_favorites_only is set, suppress notifications for nodes the receiver hasn't marked favorite (favorite status is local to each device).
  • Notification action: deep-link to the waypoint on the map.
  • State is in-memory / not persisted. Crossing state need not survive a restart β€” the baseline rule handles relaunch.

UX affordances (all clients)

  • Geofence section in the waypoint editor: a radius selector (Off + presets β€” present in the user's locale units, the wire value is always meters), and Set / Edit / Remove Bounding Box that opens a drag-to-define rectangle on the map. Show Notify on Enter / Notify on Exit toggles only once a radius or box exists, and a Favorites Only toggle only when one of those is on.
  • Map rendering: draw the circle and/or rectangle as an overlay on the waypoint.

Reference implementation β€” Apple (#2001)

First client to implement; use as the behavioral reference.

  • Model + migration β€” WaypointEntity.swift adds the geofence columns; SwiftData schema bumped V1β†’V2 with a lightweight migration (schema had already shipped).
  • Receive β€” applyGeofence(from:) copies the proto fields on waypoint upsert (WaypointEntityExtension.swift); contains(location:) is the inside test.
  • Authoring β€” Geofence section in WaypointForm.swift; drag-to-define rectangle in GeofenceBoundsSelectorView.swift (modeled on the offline-map region selector).
  • Map β€” MKCircle + MKPolygon overlays per waypoint in MeshMapMK.swift.
  • Alert engine β€” MeshPackets+Geofence.swift: the (waypointId, nodeNum) crossing store + baseline-first logic described above.
  • ⚠️ Don't copy verbatim: Apple's radius presets are imperial-only (0.1–10 mi). Other clients should present locale-aware units β€” the wire value is meters regardless.

Per-client tracking (sub-issues)

  • Meshtastic-Apple β€” #2005 Β· reference build #2001
  • Meshtastic-Android β€” #5968 Β· shipped via #6014 (editor, map overlays, alert engine β€” engine frozen pending firmware-vs-client crossing decision)
  • Meshtastic Web β€” #1227
  • Firmware / device UI β€” #10811
  • Standalone UI (MUI) β€” #37

Notes / open items

  • An earlier Apple app-local prototype (GeofenceEntity / GeofenceListView) was dropped in favor of this proto-backed, mesh-shared model. A "monitored-nodes" allow-list from it was not carried over β€” current behavior notifies for all (or favorites-only) nodes. Clients should match: no per-node allow-list for now.
  • Clients should confirm notify_favorites_only (field 13) is present in their pinned 2.7.26-06d729a protobuf artifact before wiring.
  • No debounce/hysteresis beyond the inside/outside baseline is implemented β€” a node parked on the boundary could flap. Worth considering across clients.

Metadata

Metadata

Labels

Type

No type

Projects

Status
Ready

Relationships

None yet

Development

No branches or pull requests

Issue actions