Short answer

Coordinates look reversed because the GIS world never fully agreed on whether the first number is latitude or longitude. The EPSG registry formally defines geographic CRSs like EPSG:4326 with axis order latitude, longitude. But the formats most of us use every day — GeoJSON, shapefiles, KML, and the bulk of web mapping APIs — store longitude, latitude (that is, X then Y). When data crosses a boundary between a tool that honors the authority order and one that assumes X = longitude, the two values get swapped, and your features land somewhere absurd.

The root of the confusion

There are two competing conventions, and both are "correct" within their own world:

  • Mathematical / cartographic convention: coordinates are (X, Y), and on a geographic CRS X = longitude, Y = latitude. This is what shapefiles, GeoJSON (RFC 7946 mandates longitude-first), and GDAL's "traditional" order use.
  • Geodetic authority convention: the EPSG database specifies an axis order per CRS, and for EPSG:4326 that order is latitude first, longitude second. OGC web services historically followed this, which is why WFS 1.0 returned lon/lat but WFS 1.1+ and WMS 1.3.0 switched to the authority-defined order.

GDAL 3 / PROJ 6 made this explicit. By default GDAL 3 now respects the authority axis order, so reading a raw EPSG:4326 stream can give you lat/lon. To force the familiar X = longitude behavior you set the axis mapping strategy:

# GDAL / OGR Python: force traditional lon/lat order
srs.SetAxisMappingStrategy(osr.OSR_TRADITIONAL_GIS_ORDER)

This single change is responsible for a large share of "my coordinates flipped after we upgraded" tickets.

How to recognize a swap on sight

You usually do not need tooling — the numbers betray themselves:

  • Latitude is bounded by ±90; longitude by ±180. If a value labelled "latitude" exceeds 90 (say, 151.2 for Sydney), the columns are swapped.
  • Points cluster near Null Island (0°, 0°) in the Gulf of Guinea off West Africa. That is the signature of nulls, dropped values, or swaps that pushed coordinates toward zero.
  • A whole layer is rotated/mirrored about the line y = x. Plot a recognizable shape; a swapped coastline looks transposed, not just shifted.
  • Features sit on land that should be sea (or vice versa) and the longitude/latitude magnitudes are suspiciously similar — common in mid-latitude data where both values are in a comparable numeric range and the error is subtle.

Worked example — diagnosing CSV import

Suppose a partner sends stations.csv with columns lat, lon and a row 2.35, 48.85 for a station that should be in Paris (48.85° N, 2.35° E). They have already swapped it: latitude 48.85 belongs in the first column under most conventions, but they put longitude there. When QGIS imports with X field = lon, Y field = lat mapped to the literal column names, the point lands at 2.35° N, 48.85° E — in Somalia, in the Indian Ocean.

The fix is at import time: map the X (longitude) role to the column that actually holds longitude, regardless of its header name. Never trust the header; trust the value ranges.

Worked example — fixing in PostGIS

If a table loaded with axes reversed, PostGIS has a dedicated function:

-- Swap X and Y for every geometry in place
UPDATE stations
SET geom = ST_FlipCoordinates(geom);

-- Verify a known feature now reads lon, lat
SELECT ST_X(geom) AS lon, ST_Y(geom) AS lat
FROM stations WHERE name = 'Paris';

ST_FlipCoordinates swaps X and Y for every vertex. Do this only once, and only after confirming the data really is reversed — running it on correct data simply breaks it the other way.

Worked example — fixing in ogr2ogr

For a file-based fix, GDAL can re-emit with the desired axis behavior. When the source is mislabeled lat/lon and you need lon/lat output:

ogr2ogr -f GPKG fixed.gpkg input.gpkg \
  -oo X_POSSIBLE_NAMES=lon -oo Y_POSSIBLE_NAMES=lat

For genuinely swapped geometry (not just CSV column roles), use a vrt with swapXY or apply ST_FlipCoordinates after loading. The important discipline is to fix the geometry once and re-export a clean, correctly-ordered dataset rather than compensating downstream.

Why WFS and GeoServer trip people up

Request the same layer over WFS 1.0.0 and WFS 2.0.0 and you can get opposite axis orders, because the spec changed which convention governs. GeoServer exposes settings to override this per service. When a web map shows points in the right place but a script consuming the WFS shows them swapped, the version-dependent axis rule is almost always the cause.

Validation

  • After any fix, plot 3–5 features whose true location you know and confirm they land correctly.
  • Add a guard in ingestion: reject or flag any row where abs(latitude) > 90 or abs(longitude) > 180.
  • Record the axis convention of every source in your metadata, alongside the EPSG code. "EPSG:4326" alone does not tell the next person whether the file is lat/lon or lon/lat.

Common pitfalls and why they happen

  • Assuming EPSG:4326 implies lon/lat. It does not, by the authority definition — even though most files behave that way. The ambiguity is the trap.
  • Double-swapping. Two well-meaning fixes cancel out, or worse, one fix plus one already-correct hop leaves data reversed. Always verify against ground truth.
  • Relying on headers. A column called lat may hold longitude. Range-check the values, not the names.
  • Mixed pipelines after a GDAL 3 / PROJ 6 upgrade. Older code assumed traditional order; the new default is authority order. Set the axis mapping strategy explicitly so behavior is deterministic.

Bathyl perspective

Axis order is metadata, and like all metadata it is only useful if it travels with the data and is verified against reality. We range-check coordinates on ingestion and pin an explicit axis-mapping strategy in every pipeline, so a CRS upgrade never silently transposes a dataset. The cheapest place to catch a swap is the moment the data arrives.

Related reading

Sources