Skip to content

Location Stack & Geofencing โ€‹

Status: Active (pilot ready)
Updated: 2026-01-11

Primary Stack โ€‹

  • Maps UI: MapLibre GL JS with OSM/MapTiler tiles; lazy-load map view only when requested. Google map tiles remain optional.
  • Proximity/geofencing: geohash storage with geofire-common; Haversine distance filter client-side; optional server-side recheck for fraud.
  • Business data: Google Places Autocomplete + Place Details during merchant onboarding only; cache placeId, address, and coordinates in Firestore.
  • Data model: merchants/offers store lat, lng, geohash, placeId, radius (meters), source, and canonical address/name; helper lives in src/lib/placesService.js.

Fallbacks & Resilience โ€‹

  • If Places quota/cost is a concern: allow manual entry or swap to an OSS geocoder (Photon/Nominatim) while keeping the same payload shape.
  • If geohash index is missing or empty: getNearbyVenues falls back to a full scan with distance filtering.
  • If GPS spoofing is a risk: add a Cloud Function to recompute distance server-side before redemption and flag mismatches.
  • If map tiles fail/rate-limit: show list view only; MapLibre can switch tile providers without code churn.

Implementation Checklist โ€‹

  • Onboard merchants with Places Autocomplete โ†’ fetch Place Details โ†’ map to payload via buildVenuePayloadFromPlace() (see src/lib/placesService.js) โ†’ save via createVenueFromPlace().
  • Store geohash on create/update (handled in venueService); include placeId for dedupe and scheduled refreshes.
  • Query nearby offers/venues using geohashQueryBounds(center, radius) then filter by actual distance; keep radius tight (3โ€“5 km) to reduce reads.
  • Cache Place Details in Firestore and refresh on merchant edits or a 90-day cadence; avoid repeated Google calls at runtime.
  • Lazy-load Maps JS SDK only if/when Google basemap is needed; default to MapLibre tiles for cost control.

Backfill & Indexing โ€‹

  • Backfill legacy venues: npm run backfill:geohash (requires GOOGLE_APPLICATION_CREDENTIALS with Firestore access). Updates missing/incorrect geohash values based on existing lat/lng.
  • Indexing: queries order by geohash only, which uses Firestore single-field indexes by default. If an index warning appears, create a single-field index on venues.geohash (and offers.geohash if offers add geohash later).

Testing Notes โ€‹

  • Use dev Firestore for testing (lantern-app-dev) - no emulator needed. The project has separate dev and production databases.
  • Backfill dev venues: GOOGLE_APPLICATION_CREDENTIALS=/path/to/dev-service-account.json npm run backfill:geohash
  • Verify Firestore index on geohash exists once data is populated; fall back path keeps dev environments unblocked.
  • Test both branches: geohash-enabled collections and full-scan fallback with mock venues.
  • Validate payloads that lack coordinates (should skip geohash and still save) and ensure radius defaults are respected.

See Also โ€‹

Built with VitePress