Coordinate systems
The same point has different numbers in different coordinate reference systems (CRS). Pick the wrong one for the job and distances and areas come out wrong, quietly.
Three systems in this project
| CRS | Units | Used for | Watch out |
|---|---|---|---|
| EPSG:4326 WGS84 longitude / latitude | degrees | GPS and GeoJSON. How this project stores geometry, after reprojecting the source data on import. | A degree of longitude is shorter than a degree of latitude, and shrinks towards the poles. |
| EPSG:3857 Web Mercator | metres (on a stretched sphere) | How OpenStreetMap tiles and Leaflet draw the map on screen. | Distances and areas are inflated by 1/cos(latitude): about 1.6× in length and 2.7× in area in Warsaw. |
| EPSG:2180 PUWG 1992 (ETRF2000-PL / CS92) | metres (easting / northing) | Poland's national grid. What GDOŚ and NID publish the source data in, and where this project measures area. | Only meant for Poland. Its official axis order is northing first, which some services follow and others don't. |
Try it: ST_Transform and distance
Enter two points. The database transforms them with ST_Transform and measures the gap four ways. Try the two presets: 0.01° east is about 680 m here but 0.01° north is about 1,110 m, so a degree is not a unit of length.
Running ST_Transform…
Why measure in 2180 or geography, not degrees
- The source data arrives in EPSG:2180 and is reprojected once, on import, to EPSG:4326: the CRS GeoJSON, GPS and Leaflet's API speak.
ST_AreaandST_Distanceon 4326 return square degrees and degrees. - Distances use
geometry::geography, which measures in metres on the WGS84 ellipsoid and is correct anywhere. - Areas use
ST_Area(ST_Transform(geom, 2180)): a projected, metric CRS designed for Poland, so m² are close to ground truth (within about 0.1 % in Warsaw). - Web Mercator (3857) is for drawing, not measuring. Leaflet draws on it; the database never measures in it.
The lon/lat vs lat/lon pitfall
| Where | Royal Castle, Warsaw | Order |
|---|---|---|
| GeoJSON position (RFC 7946) | [21.0142, 52.2479] | [lng, lat] |
| PostGIS | ST_MakePoint(21.0142, 52.2479) | (x, y) = (lng, lat) |
| Leaflet | L.latLng(52.2479, 21.0142) | [lat, lng] |
| This API | ?lat=52.2479&lng=21.0142 | named, so it cannot be swapped |
Swap them and 52.2479, 21.0142 read as lng/lat is a point in the Arabian Peninsula's Empty Quarter. Both numbers are valid coordinates, so range checks cannot catch it. The app keeps points as { lat, lng } objects and only turns them into arrays in features/map/utils/coords.ts, the one place that knows both conventions.
Projected systems have the same trap. The two services this project downloads from both publish EPSG:2180, yet the GDOŚ request (WFS 1.0) takes its bounding box as easting,northing and the NID request (WFS 2.0, which follows the EPSG definition) as northing,easting. See scripts/lib/sources.ts.