Notes on PostGIS
Before Pumply, I mostly thought about a map as a visual thing: you have latitude and longitude, you put a marker there, you move the camera and show the user what is nearby. PostGIS An extension for PostgreSQL that adds geographic data types and functions, so a database can answer questions about location, distance and shapes. Think of it like PostgreSQL already knows how to work with rows and numbers. PostGIS teaches it how to understand maps. Example Instead of downloading every station and calculating distance in the app, the database can answer 'which stations are within 2 km of this user?' changed that for me because it made location feel less like presentation and more like something the database can reason about. The useful shift was simple: latitude and longitude A pair of numbers used to describe a position on Earth: latitude says how far north or south, longitude says how far east or west. Think of it like They are like a global address made from two numbers. Example Accra can be represented roughly around latitude 5.6 and longitude -0.2. are not just coordinates to draw; they are data you can query spatially.
Latitude and longitude are not metres
A coordinate such as 5.6037, -0.1870 tells me where something is, but it does not directly tell me how many metres away another coordinate is. Latitude and longitude are angular coordinates on the earth, so subtracting them in application code and treating the result like a normal physical distance is not a good model. PostGIS gives PostgreSQL spatial types that understand those coordinates. For Pumply, geography A PostGIS data type that treats longitude and latitude as positions on the curved Earth, making many real-world distance calculations convenient. Think of it like Instead of measuring on a flat sheet of paper, it remembers that the planet is a globe. Example With geography values, a distance check can naturally be expressed in metres between a driver and a fuel station. is often useful because PostGIS can treat WGS84 longitude/latitude as locations on the earth and return supported distance operations in metres.
That made rules like “the reporter must be within 200 metres of this station” much easier to express correctly, and it also moved the rule into a place where every client can be held to the same standard.
ST_DWithin is the function I keep reaching for
A basic Pumply question appears everywhere: is point A within X metres of point B? PostGIS has a function for exactly that, ST_DWithin(a, b, distance). With geography values, the distance is in metres, so a simplified station-proximity check can look like this:
SELECT ST_DWithin(
user_location,
station_location,
200
);
The interesting part is not the syntax. It is that the trust rule can live in PostgreSQL instead of only inside the React app. The client can still perform its own check to give the user immediate feedback, but if somebody bypasses the interface or an old client survives longer than expected, the database can enforce the same geographic rule.
Why not calculate every distance?
Because “calculate the exact distance from every station to this point” becomes expensive as the table grows. This is where spatial index A data structure that helps the database quickly narrow down which locations are worth checking instead of comparing every place with every other place. Think of it like A library catalogue tells you which shelf to search before you inspect individual books. Example To find nearby stations, the database can first narrow the search to plausible locations and then do the exact distance calculation. clicked for me. PostGIS can use a GiST spatial index to reduce the search space for spatial relationships, and functions such as ST_DWithin are spatial-index aware. A typical index looks like:
CREATE INDEX stations_location_gix
ON stations
USING GIST (location);
Then a nearby-station query can use ST_DWithin as a filter without treating every row as equally plausible. “Find nearby stations” stopped feeling like a special map trick once I understood that it is mostly a spatial query with the right representation and the right index.
Geometry and geography are not interchangeable words
This confused me at first. PostGIS has both geometry and geography. Geometry treats coordinates on a plane and is powerful when you are working in an appropriate projected coordinate system; geography works directly with geodetic longitude/latitude and is convenient when the product’s rules are naturally described in metres around real-world locations. The PostGIS documentation is explicit that geography is convenient but can be slower, while geometry supports a much wider set of spatial functions. So the decision is not “geography is more correct.” It is which representation matches the operation I need? For Pumply’s proximity rules, geography is convenient because the product rule itself is written in metres.
The backend should own the final distance decision
One pattern I now use repeatedly is to calculate on the client for responsiveness and calculate again on the backend for authority. If the user is too far from a station, I want the interface to say so before they waste time filling out a report, but I do not want the browser to be the final judge. The browser is controlled by the user, requests can be replayed or changed, and old clients can survive. The database is where the invariant A rule that must remain true no matter which screen, device or request tries to change the system. Think of it like A bank rule saying an account cannot spend money it does not have should hold whether you use the app, website or ATM. Example If Pumply requires a reporter to be within 200 metres of a station, that rule should still hold even if someone bypasses the normal app interface. should live. That principle applies well beyond maps, but PostGIS made it concrete for me.
Distance is not the only spatial question either. When a user moves the map, Pumply does not need every station in Ghana; it needs the stations inside the visible area. The map camera can therefore become query input — north, south, east and west — and the backend can return the stations that intersect that spatial envelope. The client then turns those rows into GeoJSON A standard JSON format for describing geographic things such as points, lines and areas. Think of it like It is a common packaging format that lets map software understand 'this is a point here' or 'this is an area with these boundaries.' Example A station can be represented as a GeoJSON point with longitude and latitude plus properties such as its name and brand. and gives them to MapLibre. I like this architecture because the map is not downloading the world and hiding most of it; the database answers the spatial question the interface is currently asking.
Spatial rules are product rules
The most useful thing PostGIS taught me is that spatial logic is not just GIS plumbing. A 200 m reporting radius is a trust rule, a duplicate radius is a data-quality rule, a visible-bounds query is a performance rule, and a nearest-station query is a product-behaviour rule. They happen to be expressed through geography. Once I started thinking that way, PostGIS stopped feeling like an advanced mapping extension and started feeling like a normal part of the application model.
The note I want to remember is simple: if location changes what the product is allowed to do, location belongs in the backend model — not only on the map.
References
PostGIS — Should I use the geometry type or the geography type? ↗
PostGIS — ST_DWithin ↗
PostGIS — How do I use spatial indexes? ↗