Every time I touch GIS coordinates, I hit the same trap: sometimes coordinates are written as latitude then longitude, and sometimes they are longitude then latitude. Both look valid, both are common, and the mistake often does not throw an error. Instead, the point quietly lands in the wrong country, or worse, in the ocean.
This post is my quick reference: first the mental model (why the order differs), then the technique I used in my own project to stop the confusion from spreading.
A picture of the classic bug
When lat and lon get swapped, a real point often ends up "somewhere in the ocean." (If you are adding an image here, upload it to Blogspot first, then insert it into this section.)
The two worlds that collide
There are two different coordinate traditions that keep clashing:
1) Human geography tradition: (latitude, longitude)
Most people learn latitude and longitude in school as:
- Latitude: north or south (up and down)
- Longitude: east or west (left and right)
So a location is commonly written as:
(lat, lon)
Example for Kuala Lumpur (approx):
(3.1390, 101.6869)
This feels natural because we say "latitude and longitude" in that order.
2) Mapping and GIS tradition: (x, y) which becomes (longitude, latitude)
Most mapping engines and GIS systems think in Cartesian terms:
- X axis is horizontal
- Y axis is vertical
When coordinates are mapped into (x, y):
- X corresponds to longitude
- Y corresponds to latitude
So in GIS engine terms the same point becomes:
(lon, lat)
Same Kuala Lumpur example:
(101.6869, 3.1390)
This is not random. It is the consistent "x first, y second" rule.
The most important practical rule: GeoJSON is strict
If I am dealing with GeoJSON, there is almost no debate:
- GeoJSON Point coordinates are always
[longitude, latitude]
Example:
{
"type": "Point",
"coordinates": [101.6869, 3.1390]
}
If I swap them to [lat, lon], the data is still valid JSON, but the point ends up in the wrong place.
That is why this bug can be so annoying.
Databases and spatial functions usually follow x, y too
Many spatial databases and geometry constructors follow:
(x, y) = (lon, lat)
Even if a library accepts different input types, the underlying geometry model is still "x then y."
Why this confusion keeps happening
- People say "lat/long" in that order for decades, so apps and UIs follow it.
- GIS engines and geometry models prefer "x/y," which becomes lon/lat.
- Some libraries try to be helpful and auto-swap depending on context.
- Some standards define axis order, but older systems may ignore it, so behavior varies.
The technique that stopped it in my codebase
The conceptual explanation is useful, but it does not prevent bugs. What actually helped was enforcing one simple rule: never manually swap coordinate order scattered around the code.
Instead, I introduced a single conversion layer that acts as the boundary between:
- Frontend map UI (Leaflet), which naturally works with
(lat, lng) - Storage and backend models, which I keep aligned to
(lng, lat)/(x, y)
Concept: "Coordinate conversion layer"
In my project I called it CoordinateConversionService, but the important part is not the name.
The idea is: create one dedicated place that converts between formats, and make every other part of the app
call into it. This turns a messy convention problem into a controlled interface.
Typical responsibilities of this conversion layer:
- Convert Leaflet coordinates (lat,lng) to GeoJSON coordinates (lng,lat) for saving.
- Convert GeoJSON (lng,lat) back to Leaflet (lat,lng) for rendering.
- If the backend stores GPS as x,y, keep x=lng and y=lat consistently.
Quick example
// Leaflet UI (lat, lng)
[2.765207, 102.904707]
// GeoJSON storage (lng, lat)
[102.904707, 2.765207]
// Backend GPS model (x=lng, y=lat)
{x: 102.904707, y: 2.765207}
Concept: "Update direction guard" (to avoid feedback loops)
A second practical issue appears in map editors: there are two ways a coordinate can change.
- User drags a marker on the map, and the form fields must update.
- User edits the coordinate fields, and the marker must move.
If both directions trigger each other, the app can end up in a loop or jittery updates. The simplest fix is to track the source of the current update.
In my project I used a flag named isUpdatingFromMapDrag. Again, the name is not the point.
The concept is: when the map drag handler is actively pushing values into the form,
temporarily ignore form-driven map updates (or vice versa).
This guard makes the editor stable and prevents accidental recursion.
A small but important rendering detail
When only a marker position changes, I update the existing marker in place
(for example, Leaflet's setLatLng) instead of clearing and re-adding layers.
This avoids flicker and "marker disappeared" bugs.
My cheat sheet
- Human-friendly display: often
(lat, lon) - GIS engine, GeoJSON, storage: usually
(lon, lat) - Practical rule: keep one conversion boundary and forbid manual swapping elsewhere
- Editor stability: use an update-direction guard to avoid feedback loops
Final note to future me
If a point suddenly appears in a completely wrong place and there are no errors, check coordinate order first. If the codebase has a conversion layer, the bug is usually that some new code bypassed it.