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 insrc/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:
getNearbyVenuesfalls 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()(seesrc/lib/placesService.js) โ save viacreateVenueFromPlace(). - Store geohash on create/update (handled in
venueService); includeplaceIdfor 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(requiresGOOGLE_APPLICATION_CREDENTIALSwith Firestore access). Updates missing/incorrect geohash values based on existinglat/lng. - Indexing: queries order by
geohashonly, which uses Firestore single-field indexes by default. If an index warning appears, create a single-field index onvenues.geohash(andoffers.geohashif 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
geohashexists 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 โ
- Venue Import Guide - Import real venues from OSM/Google Places
- Database Scaling - Cost & scale considerations
- Tech Stack - Full technology overview
- Environment Setup - Dev/prod configuration