Open a food-delivery app. It shows you ten restaurants "near you," sorted by distance, in the time it takes the screen to paint. Behind that instant list is one of the most quietly powerful pieces of open-source software in the world: PostGIS, the extension that turns an ordinary PostgreSQL database into a spatial engine. Every "drivers near me," every "is this address in our delivery area," every surge-pricing zone and "notify me when I arrive" is, underneath, a PostGIS query. This is a deep dive into how it actually works — the data types, the index, the queries — grounded in the real-world services you use every day.
Step 1 — A GPS coordinate becomes a queryable row
A real techdiagrams document — download the JSON and import it into the editor.
Your phone's GPS emits a pair of numbers — latitude and longitude, e.g. 28.61, 77.20. On its own, that pair is just two floats; the database has no idea they describe a place on Earth. PostGIS's first job is to give them meaning by storing them as a spatial type:
CREATE EXTENSION postgis;
CREATE TABLE driver_location (
driver_id bigint PRIMARY KEY,
loc geography(Point, 4326), -- the magic column
updated_at timestamptz DEFAULT now()
);Two things in geography(Point, 4326) matter enormously:
Point— this column holds a single location (PostGIS also hasLineStringfor a route,Polygonfor a zone, and more).4326— the SRID, or Spatial Reference ID.4326is WGS84, the coordinate system your GPS chip uses. Telling PostGIS the SRID is what lets it do correct Earth math; a coordinate without an SRID is a number without units.
A driver's app upserts its position every few seconds:
INSERT INTO driver_location (driver_id, loc)
VALUES (42, ST_MakePoint(77.20, 28.61)::geography)
ON CONFLICT (driver_id)
DO UPDATE SET loc = EXCLUDED.loc, updated_at = now();(Note the order: ST_MakePoint(longitude, latitude) — X before Y, which trips up everyone once.) That's the write path. Millions of these flow in continuously. The read path is where PostGIS earns its reputation.
Step 2 — geometry vs geography: flat earth or round?
Before the queries, the single most important design decision — and the one that produces the most wrong answers when botched. PostGIS offers two spatial types:
geometrytreats coordinates as points on a flat Cartesian plane. Distance is plain Pythagoras:√(Δx² + Δy²). It's fast, and it's correct only if your data is already projected onto a flat local grid. Feed it raw lat/lon and ask for distance, and it happily returns a number measured in degrees — meaningless for "within 3 km."geographytreats coordinates as points on a sphere and does the great-circle math. Distances come back in real meters, correct anywhere on Earth, even across continents.
The rule of thumb: raw GPS coordinates → use geography. It's slightly slower per operation, but it's right, and for location-driven apps "right" is the whole point. Reach for geometry only when your data is pre-projected to a planar coordinate system and you need maximum speed over a small area. Download this diagram.
Step 3 — The GiST index: why "who's nearby?" is instant
Here's the query a ride-hailing app runs the instant you request a ride — find every available driver within 3 km of me:
SELECT driver_id
FROM driver_location
WHERE ST_DWithin(loc, ST_MakePoint(77.20, 28.61)::geography, 3000);With a hundred thousand drivers, checking each one's distance would be a full table scan — far too slow for the hot path. The magic is a spatial index, created in one line:
CREATE INDEX idx_driver_loc ON driver_location USING GIST (loc);GiST (Generalized Search Tree) builds an R-tree — a hierarchy of nested bounding boxes. Every geometry gets a simple rectangular box drawn around it, and boxes are grouped into bigger boxes. A radius query then works in two cheap stages, visible in the diagram:
- Index filter (the bounding-box prune). The query's search area is itself a box. PostGIS walks the R-tree and instantly discards every driver whose bounding box can't possibly overlap the search box — that's ~99% of the table eliminated with cheap rectangle comparisons, no expensive geometry math.
- Exact filter (the refinement). On the tiny surviving candidate set, PostGIS computes true spherical distance and keeps only those genuinely within 3 km.
Cheap approximation first, expensive precision only on survivors — that two-phase pattern is the heart of every spatial index.
The single most common PostGIS mistake hides right here. This looks equivalent and is a performance disaster:
-- ❌ computes distance for EVERY row — the index can't help, full scan
WHERE ST_Distance(loc, :point) < 3000
-- ✅ index-friendly twin — always use this for a radius
WHERE ST_DWithin(loc, :point, 3000)ST_Distance(...) < X forces PostGIS to compute a distance for every single row before comparing; ST_DWithin is written so the planner can use the GiST index to prune first. Same answer, wildly different speed. Download this diagram.
Sorting by distance: the KNN operator
"Within 3 km" gives you a set; a real app wants the N nearest, in order. PostGIS has a dedicated index-assisted operator for that — <->, the K-Nearest-Neighbor distance operator:
SELECT driver_id,
loc <-> ST_MakePoint(77.20, 28.61)::geography AS metres
FROM driver_location
ORDER BY loc <-> ST_MakePoint(77.20, 28.61)::geography
LIMIT 10;Used in ORDER BY, <-> lets the GiST index return rows already sorted by distance, walking the tree nearest-first and stopping after 10 — it never sorts the whole table. This one operator is the literal engine behind "your 10 closest drivers / restaurants / stores."
Step 4 — Geofencing: is this point inside a zone?
Points are half the story; the other half is areas. A delivery zone, a surge-pricing region, a "you've arrived" trigger, a regulatory boundary — all are polygons, and the question is always the same: is this point inside that shape? PostGIS answers with ST_Contains (or ST_Intersects), and the same GiST index makes it fast:
SELECT zone_id, surge_multiplier
FROM pricing_zones
WHERE ST_Contains(area, ST_MakePoint(77.20, 28.61)::geometry);One point-in-polygon primitive powers a startling range of real features: "are you inside our delivery radius?", surge pricing that changes block by block, "notify me when I enter/leave this area," store-catchment analysis, and compliance boundaries ("this content is unavailable in your region"). The polygon can be a hand-drawn neighborhood, a city boundary, or a computed catchment — the query doesn't care. Download this diagram.
Where this fits in a real system — and where it doesn't
PostGIS is the durable, queryable source of truth for location, and it's genuinely fast. But at extreme scale (think: every driver in a megacity updating every 4 seconds), teams often split the workload:
- The hot path — "nearest drivers right now" at massive write volume — sometimes moves to an in-memory geospatial store (Redis
GEOSEARCH) or an H3 hex-grid index, where writes are cheaper and eligibility is a constant-time cell lookup. - PostGIS remains the system of record and the analytical engine: complex polygon queries, joins against business data ("nearby restaurants that are open and do delivery and match this cuisine"), historical trajectories, and reporting. Its superpower is that spatial predicates live right next to your relational data — a single SQL query can combine "within 3 km" with "rating > 4 and accepts_online_payment," which a pure geo-cache cannot.
The honest architecture is usually both: a fast geo-cache for the millisecond hot path, PostGIS for everything that needs to be correct, durable, and joined to real business logic. Our food delivery and ride-hailing templates in the gallery show where the spatial store sits in the whole system.
The takeaway
Location-driven products feel like magic — the list that's always sorted by distance, the delivery area that just knows, the price that changes when you cross a street. Underneath, it's four unglamorous, learnable pieces: store coordinates as geography in SRID 4326, put a GiST index on them, use ST_DWithin (never ST_Distance <) for radius and <-> for nearest-N, and ST_Contains for zones. Master those and you can build the spatial half of an Uber in an afternoon.
Import any of these diagrams into techdiagrams.net and wire in your own services — the point of seeing the pipeline, the index, and the geofence as architecture is that "location feature" stops being a black box and becomes something you can design.