Guide
Lat/Lon vs Lon/Lat: which order does each format use?
Half of geospatial software wants longitude first; the other half wants latitude first. Nobody agrees, nothing errors when you get it wrong, and your points quietly end up in the ocean. This page is the definitive order table — one row per format and API, each sourced from its spec — plus how to recognise a swap on the map and fix it in one click, Python, JavaScript, or SQL.
Why two conventions exist
The disagreement is old and it is honest — both orders are "correct" for their world.
Mathematics uses (x, y). Longitude is the horizontal axis (east–west, the
x) and latitude is the vertical axis (north–south, the y). Anything descended from computer
graphics, computational geometry, or the OGC's Simple Features model inherits this:
a coordinate is an (x, y) pair, so it is (longitude, latitude).
GeoJSON, WKT, WKB, KML and Turf.js all live here.
Navigation uses (latitude, longitude). Sailors, pilots, and mapping APIs built for humans read a position out loud the way it appears on a chart — "forty-one point eight degrees north, eighty-seven point six west" — latitude first. Google Maps, Leaflet, the H3 and S2 libraries, and Google's encoded-polyline algorithm all speak this dialect.
Neither camp is wrong; they are answering different questions ("where is it on the plane?" versus "where is it on the globe?"). The problem is only that a bare pair of numbers carries no label, so the moment data crosses from one world to the other, the two values can silently trade places.
The definitive order table
Every row below is the order that format or library actually expects, per its own
specification or API reference. When the "long" convention shows x y, remember
x = longitude and y = latitude.
| Format / API | Order | Detail & source |
|---|---|---|
| GeoJSON | lon, lat |
RFC 7946 §3.1.1: a position is [longitude, latitude] in WGS84 decimal
degrees. Longitude first, always.
|
| WKT / WKB | x y = lon, lat |
OGC Simple Features. A point is written POINT(x y) where
x is easting/longitude and y is northing/latitude:
POINT(-87.63 41.88).
|
| PostGIS constructors | lon, lat | ST_MakePoint(x, y) and ST_Point(x, y) take
(longitude, latitude). The classic trap is not the API — it is user code
that writes ST_MakePoint(lat, lon) by habit. Order matters; the function
won't warn you.
|
| Google Maps JS API | lat, lng | new google.maps.LatLng(lat, lng) — latitude first, as the class name
says. Same for { lat, lng } literals.
|
| Leaflet | lat, lng | L.latLng(lat, lng), L.marker([lat, lng]),
map.setView([lat, lng], z) — latitude first everywhere.
|
| MapLibre / Mapbox GL JS | lng, lat | new LngLat(lng, lat); map.setCenter([lng, lat]). The GL
stack is longitude-first — the opposite of Leaflet, a frequent migration bug.
|
| Turf.js | lon, lat |
It is GeoJSON under the hood: turf.point([lon, lat]), longitude first.
|
| Encoded polyline | lat, lng pairs |
Google's polyline algorithm encodes each vertex as
(latitude, longitude) — latitude first. Feed it lon/lat pairs and the
whole line transposes.
|
| H3 (h3-js) | lat, lng | latLngToCell(lat, lng, res) (v4; geoToH3(lat, lng, res) in
v3) — latitude first.
|
| S2 | lat, lng | S2LatLng.FromDegrees(lat, lng) — latitude first, matching the class
name.
|
| EPSG:4326 axis order | lat, lon (officially) | The pedantic one. The EPSG registry defines the authoritative axis order of 4326 as latitude, longitude. Almost all software — GeoJSON, most WMS/tile clients, nearly every library above — ignores that and uses lon/lat anyway. The gap between "what the standard says" and "what everyone ships" is a long-running GIS in-joke; it mostly bites you in strict OGC WFS/GML services and some CRS-aware pipelines. |
| KML | lon, lat | <coordinates>lon,lat,alt</coordinates> — longitude first,
comma-separated, optional altitude third.
|
| CSV | no standard |
The danger zone. There is no convention — columns may be lat,lng or
lng,lat, sometimes headerless. Never assume; read the header, and if
there isn't one, sanity-check a known point before trusting the whole file.
|
Rule of thumb: interchange & math formats are lon-first (GeoJSON, WKT,
KML, Turf, MapLibre, PostGIS); human/navigation APIs are lat-first (Google
Maps, Leaflet, H3, S2, polyline). The odd ones out — MapLibre being lon-first while Leaflet is
lat-first — cause most real bugs.
What a swap looks like on the map
Swapping the two numbers reflects the point across the lon = lat
diagonal. That single geometric fact explains every "my points are in the ocean"
report.
Take Chicago: latitude 41.88, longitude -87.63. Correct GeoJSON is
[-87.63, 41.88]. Write it the navigation way — [41.88, -87.63] —
and a lon-first renderer reads longitude 41.88, latitude -87.63:
a spot in the Southern Ocean off Antarctica, south of the Indian Ocean,
roughly antipodal to where you meant. A northern-hemisphere city becomes a southern-ocean
nowhere; that mirror-image relocation is the fingerprint of a swap.
The insidious part is the confusion band. When both numbers are
≤ 90 in magnitude, both orderings are geographically legal — every
latitude is a valid longitude — so nothing throws, nothing warns, and the point just lands
somewhere plausible-but-wrong. Chicago's swap falls squarely in this band:
-87.63 is a perfectly valid latitude, so no validator on earth can be
certain the pair is reversed. You only get a hard signal when a value exceeds
±90 — which can only be a longitude, proving the pair is out of order.
How our inspector's swap detector works (honestly)
The universal inspector runs a deliberately conservative swap heuristic. It
flags a pair as possibly-swapped only when it can prove it: a coordinate whose
latitude slot exceeds ±90° (geographically impossible) while its
longitude slot would be a valid latitude (≤ 90°). That is the classic
[lat, lon]-written-as-[lon, lat] mistake, and when it's found the
validator raises a coordinates-possibly-swapped warning with a one-click
swapCoords fix:
Coordinates look lon/lat-swapped: some latitudes exceed ±90° but would be valid longitudes.
GeoJSON order is [longitude, latitude].
What it can catch: any swap where the mistaken latitude lands above 90 or
below −90 — which covers every point whose true longitude is beyond ±90° (most of Asia,
Oceania, the Pacific, the far Atlantic).
What it cannot catch: the confusion-band swaps like Chicago, where the
reversed latitude is still a legal number. There is no way to know for sure, so the inspector
warns rather than silently swapping — silently "fixing" an ambiguous pair would be
worse than leaving it, because it might have been right all along. It surfaces the ambiguity
and lets you decide.
See it flag a swap
Here's a Tokyo point written the wrong way round as GeoJSON —
[35.68, 139.65] instead of [139.65, 35.68]. Because Tokyo's
longitude (139.65) is well past 90°, the swapped latitude slot is impossible, so
the inspector catches it with certainty:
Open in the Inspector — watch it flag the swap →
The link carries the geometry in the URL fragment only, so nothing is uploaded — the
inspector decodes it in your browser, parses it, and shows the possibly-swapped warning with
the swapCoords fix button. You can also paste either snippet in yourself:
// Swapped (wrong) — inspector flags it, Tokyo latitude 139.65 is impossible
{"type":"Point","coordinates":[35.68, 139.65]}
// Correct — longitude first
{"type":"Point","coordinates":[139.65, 35.68]}
How to fix swapped coordinates
Once you know a dataset is reversed, flipping it is mechanical. Pick the tool you're already in:
In the inspector (one click)
When the validator raises coordinates-possibly-swapped, press its
swapCoords fix. It transposes every position's first two values in place and
re-validates, so you see the corrected geometry (and its map render) immediately.
Python (Shapely)
from shapely.ops import transform
# Swap x/y (lon/lat) for any geometry, holes and all.
flipped = transform(lambda x, y: (y, x), geom)
JavaScript (Turf / GeoJSON)
import { coordEach } from '@turf/meta';
// Mutates in place; works for points, lines, polygons, collections.
coordEach(geojson, (coord) => {
const tmp = coord[0];
coord[0] = coord[1];
coord[1] = tmp;
});
PostGIS
-- Swap X and Y for a column of geometries.
UPDATE my_table SET geom = ST_FlipCoordinates(geom);
Flip once, then verify a known landmark before you trust the whole batch — a double-swap puts
you right back where you started.
Sources and further reading
The table uses each format specification or library's current API reference. Where ecosystems disagree, the page reports the concrete function or serialized position order instead of assuming a universal convention.
- RFC 7946 — The GeoJSON Format — GeoJSON longitude/latitude position order
- OGC Simple Feature Access — Common Architecture — WKT/WKB x-y geometry model
- Google Maps JavaScript API — Coordinates — LatLng latitude-first API
- Leaflet API — LatLng — latitude/longitude factory and array order
- MapLibre GL JS — LngLat — longitude/latitude order aligned with GeoJSON
- H3 — Indexing functions — latLngToCell latitude-first arguments
- S2Geometry — Basic types — S2LatLng::FromDegrees latitude-first arguments
- Google Encoded Polyline Algorithm Format — latitude then longitude coordinate deltas