Geolocation
How RideChain turns a fuzzy rural address into a deliverable point, streams live partner location battery-cheaply, fences every handover, and keeps that location data honest and private.
1. Overview β geolocation is the spine
Almost every interesting thing RideChain does leans on knowing where something is. Geolocation underpins matching (which partners are near this pickup?), routing (how do we get there cheapest?), live tracking (where is my parcel right now?), geofenced handovers (one of the four custody checks), and fraud detection (is this GPS even real?). It is less a feature and more the coordinate system the rest of the platform is drawn on.
Two stores carry the geo load, with a deliberate split of duties:
PostgreSQL + PostGIS
The durable, authoritative store. Holds Points, Block Hubs, saved addresses, geofence polygons, partner service areas and resolved booking coordinates as geography columns. Answers nearest-eligible and containment queries (KNN, ST_DWithin) with GiST indexes. The source of truth that survives a restart.
Redis geo sets
The live, hot layer. Online partner positions are written to GEOADD sorted sets and queried with GEOSEARCH for sub-millisecond radius lookups, plus cached ETAs and last-known fixes. Volatile by design β a partner who goes offline simply ages out; it never has to be the system of record.
flowchart TB
subgraph Apps["Edge apps (Flutter / PWA)"]
PT["π΅ Partner app
streams location"]
BK["π± Book app
live track + ETA"]
end
PT -->|"WebSocket fixes"| GEO["Go geo module"]
GEO -->|"GEOADD / GEOSEARCH"| RDS[("Redis geo sets
live positions Β· ETAs")]
GEO -->|"durable writes / KNN"| PG[("PostgreSQL + PostGIS
Points Β· geofences Β· addresses")]
GEO -->|"nearest online partners"| MATCH["π― Matching"]
GEO -->|"snapped path"| ROUTE["π§ Routing (OSRM)"]
GEO -->|"fence + timestamp events"| CUST["π Custody / handover"]
GEO -->|"plausibility signals"| FRAUD["π¨ Fraud module"]
RDS -->|"pushed fixes + ETA"| BK
classDef hot fill:#fff3e0,stroke:#f4920b,color:#8a5200;
classDef dur fill:#e8f0fb,stroke:#1f5fae,color:#143d6e;
class RDS hot; class PG dur;
2. The rural addressing problem (and how we resolve it)
The hardest geo problem in the Indian interior is not GPS accuracy β it is that most homes have no usable postal address. "Ramu's house behind the temple, near the big neem tree" is a real, deliverable address to a local and a useless one to a database. RideChain cannot demand a pin-perfect lat/long from a first-time rural sender, so it accepts fuzzy input and resolves it down to something a partner can actually reach.
Map pin
Drag a pin on a map tile. Works when there is data and the user is map-literate; we snap and store the resulting geography(Point).
Saved addresses
Once resolved, an address is saved (home, shop, in-laws' village) so the fuzzy work is done only once per place.
Voice address
The user speaks the address in their language; it is transcribed and parsed into village + landmark candidates for confirmation.
"Near landmark X"
Free-text landmark ("behind Hanuman mandir") matched against a curated landmark gazetteer for that village.
3-word / grid code
A what3words-style three-word or grid landmark code resolves to a ~3 m square β easy to read aloud over a phone, no spelling needed.
RideChain Point anchor
The strongest signal: a known RideChain Point (kirana / CSC / panchayat / chai stall) is a surveyed, fixed coordinate. "Deliver to the Point near X" sidesteps door ambiguity entirely.
The resolver tries these in order of confidence, falling back gracefully, and always offers the Point as a known anchor when door-level resolution is too uncertain. Crucially, much of this works offline: a packaged village + landmark gazetteer lets the app suggest a deliverable area code even with no data, deferring the precise pin until sync.
flowchart TB
IN["Fuzzy address input"] --> Q{"Have a map pin?"}
Q -- "yes" --> SNAP["Snap pin to road / building"]
Q -- "no" --> SAVED{"Matches a saved address?"}
SAVED -- "yes" --> USE["Reuse stored coordinate"]
SAVED -- "no" --> CODE{"3-word / grid code given?"}
CODE -- "yes" --> DECODE["Decode to ~3 m square"]
CODE -- "no" --> LAND{"Landmark in gazetteer?"}
LAND -- "online" --> GEOCODE["Geocode village + landmark"]
LAND -- "offline" --> OFFLINE["Offline fallback:
village centroid + landmark note"]
LAND -- "ambiguous" --> CLARIFY["Operator / voice clarifies"]
SNAP --> SCORE["Confidence score"]
USE --> SCORE
DECODE --> SCORE
GEOCODE --> SCORE
OFFLINE --> SCORE
CLARIFY --> SCORE
SCORE --> LOW{"Confidence below threshold?"}
LOW -- "yes" --> ANCHOR["Offer nearest RideChain Point as anchor"]
LOW -- "no" --> SAVE["Persist geography(Point) + save address"]
ANCHOR --> SAVE
3. Live tracking architecture
Live tracking has to feel real-time to the booker while costing the partner almost no battery or data β non-negotiable on a budget Android phone in a low-signal village. The Partner app streams fixes over a WebSocket to the Go geo module, which writes the live position into the Redis geo index and pushes derived position + ETA on to the Book app.
sequenceDiagram
autonumber
participant PT as "Partner app"
participant GEO as "Go geo module"
participant RDS as "Redis geo index"
participant ETA as "ETA / routing"
participant BK as "Book app"
PT->>GEO: "WS connect (job-scoped, authed)"
loop "Adaptive sampling"
PT->>GEO: "Location fix (lat, lng, accuracy, speed, ts)"
GEO->>RDS: "GEOADD live position"
GEO->>ETA: "Recompute ETA if moved enough"
ETA-->>GEO: "ETA + remaining distance"
GEO-->>BK: "Push position + ETA"
end
Note over PT,GEO: "Near pickup/drop β sample faster; idle β slower"
PT-->>GEO: "Offline: buffer fixes locally"
PT->>GEO: "On reconnect: replay buffered fixes (deduped by ts)"
Adaptive sampling is the battery trick: the app raises the GPS sampling rate when the partner is close to a pickup or drop (where precision matters and the booker is watching), and drops it right down when the partner is idle, far away, or stationary. The geo module can also push the desired cadence to the device based on job state.
| Partner state | Sampling cadence | Why |
|---|---|---|
| Idle / no active job | Very slow (or off) | No one is tracking; save battery and data. |
| Assigned, en route, far away | Low | Coarse position is enough for a far-off ETA. |
| Approaching pickup / drop geofence | High | Booker is watching; geofence entry must be precise. |
| Stationary > N seconds | Throttle down | Position is not changing β stop spending power. |
| Low battery (see edge cases) | Reduced everywhere | Protect the partner's ability to finish the job. |
4. Geofencing & the custody check
A geofence is a region β a circle (centre + radius) or a polygon β drawn around a place that matters: a RideChain Point, a pickup door, a drop door, or a Block Hub. Entering the right geofence, together with a trusted server timestamp, is one of the four custody checks a handover must pass β alongside the assignment lock, the unique parcel-ID/QR scan, and the two-sided handover OTP (see Last-Mile Delivery).
flowchart LR
FIX["Partner location fix"] --> CHK{"Inside the handover
geofence?"}
CHK -- "no" --> WAIT["Hold: handover blocked,
guide partner closer"]
CHK -- "yes" --> ACC{"Accuracy within
threshold?"}
ACC -- "no" --> WAIT
ACC -- "yes" --> STAMP["Server stamps entry time"]
STAMP --> GATE["Geofence custody check PASSED"]
GATE --> NEXT["Proceed to QR scan + OTP"]
Containment is evaluated server-side with PostGIS: ST_DWithin(point, fence_centre, radius) for circular fences and ST_Contains(polygon, point) for polygons, both backed by GiST indexes. Radii are tunable per Point because a tight 30 m fence that works in a town is hopeless beside a sprawling rural Point with poor GPS β see the edge-case note on fence sizing.
5. Nearest queries β who is closest?
The question matching keeps asking is: "which eligible, online partners are nearest to this pickup, right now?" Geolocation answers it from both stores. Redis gives the fast first cut over live positions; PostGIS refines with durable eligibility and exact geo predicates.
- Redis
GEOSEARCHβ pull all online partners within an expanding radius of the pickup from the live geo set, ordered by distance. Sub-millisecond, no disk. - PostGIS KNN refine β for the candidate set, apply durable eligibility (vehicle type, service area, rating, capacity) and exact distance with the
<->KNN operator over a GiST index. - Hand to matching β the ranked, eligible shortlist feeds the dispatch cascade in Nearest-Partner Matching.
flowchart TB
PICK["Pickup location"] --> RDS[("Redis GEOSEARCH
radius, online only")]
RDS --> CAND["Candidate partners
(by distance)"]
CAND --> PGQ[("PostGIS KNN refine
eligibility + exact distance")]
PGQ --> SHORT["Ranked eligible shortlist"]
SHORT --> MATCH["π― Matching cascade"]
SHARD["Geo-sharding by region / block"] -.->|"scopes the search space"| RDS
SHARD -.-> PGQ
6. Coordinate handling & accuracy
A GPS fix is never a point β it is a point plus an accuracy radius, and in rural / forest / low-signal terrain that radius can balloon. RideChain treats accuracy as a first-class field on every fix and refuses to pretend a Β±200 m fix is a doorstep.
| Condition | Typical accuracy | How we handle it |
|---|---|---|
| Open rural road, clear sky | ~5β15 m | Trust the fix; snap to road network for ETA. |
| Dense forest / canopy | ~30β100 m | Widen tolerances; lean on the Point anchor near handover. |
| Deep valley / hill shadow | ~50β200 m | Hold last-known + ETA; do not auto-pass geofence. |
| 2G-only / weak signal area | Coarse, intermittent | Buffer fixes; use cell-tower / Wi-Fi assist where available. |
| Tunnel / building interior | No fix | Freeze to last-known with a staleness flag; resume on reacquire. |
Two corrections run on the stream. Snapping projects a raw fix onto the nearest plausible road segment (via the OSRM road network used in Fastest-Route Finding) so a track looks like a road journey, not a drunkard's walk across fields. Jitter / drift smoothing rejects physically impossible jumps and lightly filters scatter while a device is stationary, so a parked partner does not appear to wander.
7. Anti-spoofing & fraud
Because geofence + timestamp is a money-releasing custody check, a faked location is an attack on the ledger. The geo module feeds the fraud module a set of plausibility signals and flags fixes that cannot be trusted; a flagged fix can block escrow release until a human confirms.
flowchart TB
FIX["Incoming location fix"] --> MOCK{"Android mock-location
flag set?"}
MOCK -- "yes" --> FLAG["Flag + block release"]
MOCK -- "no" --> SENSOR{"Sensors agree?
(accelerometer Β· cell tower)"}
SENSOR -- "implausible" --> FLAG
SENSOR -- "ok" --> SPEED{"Speed plausible?
(no teleport jump)"}
SPEED -- "impossible" --> FLAG
SPEED -- "ok" --> ROUTEP{"On a plausible route?"}
ROUTEP -- "no" --> SOFT["Soft-flag: extra review"]
ROUTEP -- "yes" --> TRUST["Accept fix"]
FLAG --> FRAUD["π¨ Fraud module:
require manual confirm"]
SOFT --> FRAUD
- Mock-location flag β Android exposes when a fix comes from a mock provider; that is an immediate hard flag.
- Sensor cross-check β accelerometer movement and the serving cell tower / Wi-Fi must be roughly consistent with the reported position; a "moving" device whose accelerometer is dead is suspicious.
- Impossible-speed / teleport β two fixes that imply 900 km/h between them cannot both be real.
- Route plausibility β a fix far off any road the partner could have taken to get there earns at least a soft flag.
8. Location privacy
Live location is sensitive personal data, and RideChain handles it under India's DPDP Act with explicit consent and tight scoping. The guiding principle: a partner's whereabouts are shared only as much as a live job requires, only while it is live.
Masked sharing
The booker sees an approximate live position and ETA, not a raw high-precision stream of the partner's exact GPS log.
Active-job only
The partner's location is exposed to a booker only during that booker's active job. Job done, the live link closes β a partner is never trackable between jobs.
Retention limits
Live position trails are retained only as long as needed for tracking, disputes and audit, then aged out. Redis live fixes are inherently short-lived.
DPDP consent
Location collection is consented at onboarding/booking with a clear purpose; consent and retention follow DPDP obligations.
SOS live-share
A safety override: triggering SOS broadcasts a precise live-location share to the safety/ops team for the duration of the incident.
9. Edge cases & failure modes
Geolocation in the interior is unreliable by default, so every failure mode below has a defined behaviour that degrades gracefully rather than blocking a delivery. The full cross-component catalogue lives in the Edge-Case Catalog.
| Scenario | Mitigation |
|---|---|
| GPS denied / location permission revoked | Degrade gracefully: block GPS-based auto-confirm and fall back to operator manual confirm at a RideChain Point; prompt to re-grant. |
| No signal / 2G-only area | Buffer fixes in the local outbox and replay on reconnect; SMS-fallback status updates so the booker still gets progress. |
| GPS drift / low accuracy fix | Enforce an accuracy threshold before any geofence pass; anchor to the surveyed Point coordinate instead of a wide, drifting fix. |
| Mock-location / GPS spoof | Detect via the OS mock flag + sensor/speed plausibility, flag the fix, and block escrow release until manual confirm. |
| Battery low on partner device | Reduce sampling cadence everywhere to protect the partner's ability to finish the job; warn ops if it risks the SLA. |
| Device clock skew | Use the trusted server timestamp for all custody and ordering β the device clock is never authoritative. |
| Geofence too small at a rural Point | Radii are tunable per Point; widen the fence where GPS is poor so legitimate arrivals are not held forever. |
| Landmark address ambiguous | Operator- or voice-assisted clarification narrows it; otherwise route to the nearest Point anchor as the deliverable point. |
| Offline during handover | Queue the geofence/OTP/POD events locally and reconcile on reconnect, deduped by timestamp; custody holds until confirmed. |
| Tunnel / forest dead zone | Freeze to last-known position with a staleness flag and hold the ETA rather than showing a stale dot as live; resume on reacquire. |
| Partner leaves the geofence before OTP | Block the handover and require re-confirmation inside the fence β the geofence custody check must hold at the moment of the OTP. |
| Spoofed "arrival" to release money early | Geofence + server timestamp + accuracy gate must all pass; a fraud-flagged fix cannot satisfy the custody check, so escrow stays held. |