Skip to main content
What Is GeoJSON? Format, Geometry Types, and Production Pitfalls
Guides

What Is GeoJSON? Format, Geometry Types, and Production Pitfalls

GeoJSON is the JSON-based format every modern map library reads. The geometry types, the Feature pattern, how many decimals you need, how much line simplification saves, and the pitfalls that bite in production.

Brent van der HeidenLast updated 11 min read
#geojson#geojson format#geojson file#rfc 7946#coordinate precision#douglas peucker#line simplification#geospatial#maps api

GeoJSON is the JSON-based format that every modern web map can read. If you have ever drawn a polygon on a Leaflet map, styled a vector layer in MapLibre, or stored a service area in PostGIS, you have been working with GeoJSON, even if the file extension said something else.

This guide explains what GeoJSON actually is, what its geometry types mean, where it shows up in real systems, and the pitfalls that bite teams when GeoJSON files leave a developer's laptop and hit production.

What GeoJSON Really Is

GeoJSON is an open format for encoding geographic data structures, standardised by the IETF as RFC 7946 in August 2016. It is a strict subset of JSON: every GeoJSON document is valid JSON, but the spec adds rules about which keys are allowed, how geometry is structured, and how coordinates are ordered.

The format is intentionally small. There are only nine top-level object types in the entire spec, and a competent developer can hold all of them in their head. That smallness is why GeoJSON has become the lingua franca of web mapping: it is easy to read, easy to write, and easy to validate.

Coordinates are always written as [longitude, latitude] (with optional altitude as a third value), and the coordinate reference system is fixed to WGS84, the same datum your phone's GPS uses. There is no negotiation, no projection metadata, no axis order debate. One datum, one ordering, everywhere.

Geometry Types

GeoJSON defines seven geometry types. The first three are the primitives:

  • Point: a single coordinate, used for pins, addresses, and individual sensors
  • LineString: an ordered list of two or more coordinates, used for routes, roads, and rivers
  • Polygon: a closed ring of coordinates (first and last point identical), used for buildings, parcels, and admin boundaries

The next three are simply collections of those primitives:

  • MultiPoint: many points in one geometry, useful for representing a chain of stores as a single feature
  • MultiLineString: many line strings, used for fragmented routes or river systems
  • MultiPolygon: many polygons, the right choice for any country with islands (so, almost all of them)

The seventh, GeometryCollection, can hold a mix of any of the above. The spec asks implementations to avoid nested GeometryCollections, and many consumers handle the type poorly, so you almost never need it. If you reach for GeometryCollection, it is usually a sign that the model wants to be a FeatureCollection instead.

Feature and FeatureCollection

A geometry alone is rarely useful. You need to know what it represents. That is what Feature is for: a Feature wraps a geometry with a properties object that holds arbitrary metadata.

{
  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [4.8952, 52.3702]
  },
  "properties": {
    "name": "Amsterdam Centraal",
    "type": "station",
    "platforms": 15
  }
}

A FeatureCollection is a list of Features. Almost every real GeoJSON file you will encounter in the wild is a FeatureCollection, because real datasets have many things in them.

The crucial design choice is that properties is open. The spec says nothing about what keys it can contain or what types those values should be. That is a feature, not a bug: it means GeoJSON works for street furniture, electoral boundaries, delivery zones, and AIS ship tracks without having to extend the format. It also means consumers have to be careful: if you depend on properties.population existing, validate it yourself, because RFC 7946 will not.

Where GeoJSON Shows Up

Once you start looking, GeoJSON is everywhere in the geospatial stack:

  • Map libraries: MapLibre GL, Mapbox GL, Leaflet, and OpenLayers all consume GeoJSON natively as a data source for layers
  • Vector tile pipelines: tools like Tippecanoe take GeoJSON in and produce MVT (Mapbox Vector Tile) sets out
  • Map style specs: the Mapbox Style Specification, used by MapLibre, references GeoJSON as a first-class source type
  • Spatial databases: PostGIS, BigQuery GIS, and Snowflake all import and export GeoJSON via dedicated functions
  • API outputs: most modern geocoding, isochrone, and routing APIs return GeoJSON Features for results, so the response can be dropped straight onto a map
  • Route polylines: navigation responses commonly include a LineString geometry for the route, ready to render
  • Data sharing: open data portals and government boundary releases commonly offer GeoJSON when they target web users

If a tool can speak any geospatial format at all, it almost certainly speaks GeoJSON.

Coordinate precision: how many decimals you need

JSON writes numbers as text, so every extra decimal place costs bytes in every coordinate. RFC 7946 addresses this in its section on coordinate precision: 6 decimal places "amounts to about 10 centimeters, a precision well within that of current GPS systems."

The ground size of one unit in the last decimal place, computed on the WGS84 ellipsoid:

DecimalsExampleLatitudeLongitude at the equatorLongitude at 52° N (Amsterdam)
252.371.1 km1.1 km681 m
352.370111 m111 m68 m
452.370211 m11 m6.8 m
552.370221.1 m1.1 m0.68 m
652.3702160.11 m0.11 m0.07 m
752.37021571.1 cm1.1 cm0.7 cm

To see what that does to a real file, we took the OpenStreetMap boundary of the municipality of Amsterdam (two outer rings, 7,677 vertices, stored in OSM with 7 decimals) and wrote it out with fewer decimals each time:

DecimalsFile sizeGzippedLargest rounding error
7 (as in OSM)175.0 KB52.8 KB0
6159.6 KB43.5 KB0.07 m
5144.3 KB32.3 KB0.65 m
4129.0 KB15.7 KB6.5 m

Each decimal removed saves about 15 KB of raw text, because JSON syntax and the other digits stay. Compression gains more, since shorter numbers repeat more often. Six decimals cost nothing visible. Five are fine for boundaries on a web map. Four move vertices by several metres, and three would move them by up to 65 m and collapse neighbouring vertices onto the same point, which can leave a polygon with zero-length edges or self-intersections.

Round when you write the file, not when you store the data:

// Round every coordinate of a GeoJSON geometry to n decimal places.
const roundCoords = (c, n) =>
  typeof c[0] === 'number' ? c.map((v) => Math.round(v * 10 ** n) / 10 ** n) : c.map((x) => roundCoords(x, n));

feature.geometry.coordinates = roundCoords(feature.geometry.coordinates, 6);

Simplifying lines and polygons (Douglas-Peucker)

Precision trims digits. Simplification removes whole vertices, and it usually saves far more. The Douglas-Peucker algorithm keeps the first and last point of a line, finds the point farthest from the straight segment between them, keeps it if it is farther than a tolerance, and repeats on both halves. Every dropped vertex lies within the tolerance of the simplified line, so the tolerance is the largest error you accept.

We ran Douglas-Peucker on the same Amsterdam boundary, with the tolerance in metres, and wrote each result with 6 decimals:

ToleranceVerticesFile sizeGzippedLargest deviation, measuredArea change
none7,677159.6 KB43.5 KB00
1 m1,34728.1 KB8.9 KB1.0 m0.000%
5 m59612.5 KB4.2 KB5.0 m+0.006%
10 m4379.2 KB3.1 KB9.9 m+0.008%
25 m2765.8 KB2.1 KB25.0 m-0.04%
100 m1232.7 KB1.0 KB98.4 m-0.06%

A 1 metre tolerance already removes 82 percent of the vertices. By construction, every removed vertex sat within a metre of a straight stretch of the simplified outline, where it added no visible shape. The area barely moves at any tolerance because simplification cuts some corners and fills others.

Choose the tolerance from the zoom level the data is drawn at. One pixel of a 256-pixel tile covers about 23 m in Amsterdam at zoom 12 and 5.8 m at zoom 14 (see the zoom level table in our tiles guide), so a 10 m tolerance is invisible at zoom 12 and a 1 m tolerance is invisible almost everywhere on a web map. Three cautions:

  • Shared borders. Simplifying neighbouring polygons one at a time moves their common border differently on each side and opens slivers between them. Use a topology-aware tool such as mapshaper or TopoJSON for administrative areas.
  • Units. Many libraries take the tolerance in the units of the coordinates, so in degrees for GeoJSON. One metre is about 0.000009 degrees of latitude.
  • Validity. Large tolerances can make a ring cross itself. Validate after simplifying.

The measurements come from scripts/seo/blog-keyword-research/what-is-geojson.mjs, with the output in docs/blog-keyword-research/what-is-geojson-2026-09-30.json. Boundary data © OpenStreetMap contributors (ODbL), OSM relation 47811, fetched on 30 September 2026.

Pitfalls in Production

GeoJSON is forgiving on day one and unforgiving on day ninety. The traps that catch teams:

Longitude first. RFC 7946 fixes the order as [lon, lat]. Humans, addresses, and most documentation write lat, lon. Swapping them is the single most common GeoJSON bug, and your point ends up in the wrong ocean.

Polygon winding order. RFC 7946 specifies that exterior rings must be counter-clockwise and interior rings (holes) clockwise. Many older GeoJSON files ignore this. Some renderers do not care, others (notably Mapbox GL with antimeridian-crossing polygons) render the polygon as the inverse of what you wanted: a tiny island becomes a hole in the ocean covering the rest of the world.

Antimeridian handling. Geometry crossing the 180/-180 meridian needs to be split per the spec. Naive polygons drawn across the Pacific render as a thin strip wrapped the long way around the planet.

No native CRS support beyond WGS84. RFC 7946 deliberately removed the older crs member. If your data is in a national grid (British National Grid, RD New, EPSG:3857), you must reproject to WGS84 before serialising. Non-WGS84 GeoJSON is only valid where all parties have a prior arrangement, so do not expect other tools to read it correctly.

File size. A FeatureCollection with millions of detailed polygons can balloon to gigabytes. For browser delivery, simplify the geometry and round the coordinates as described above, switch to vector tiles, or stream with newline-delimited GeoJSON (.ndjson).

Validation. Use a linter such as GeoJSONLint or the geojson-validation library in CI. Catching invalid winding order or swapped coordinates at build time is cheaper than catching it from a customer ticket.

GeoJSON in MapAtlas

GeoJSON is the default data exchange format across the MapAtlas platform. The Dynamic Maps product accepts GeoJSON sources directly for custom overlays, styling, and interactive layers, with no preprocessing required.

The Geocoding API returns matched addresses as GeoJSON Features, so a forward geocode result can be dropped straight onto a map or stored in PostGIS without remapping fields. The Isochrone API returns travel-time areas as GeoJSON polygons, which means a "places reachable in 15 minutes" query is one fetch and one addSource call away from being on screen.

GeoJSON is not glamorous. It is a small, opinionated subset of JSON with a fixed coordinate order and a handful of geometry types. But it is the format that lets every layer of a modern location stack, from spatial database to map canvas, agree on what a place is. Get the longitude order right, validate the winding, and the rest of the stack tends to take care of itself.

Frequently Asked Questions

What is GeoJSON?

GeoJSON is a JSON-based open format for encoding geographic data, standardised as RFC 7946 by the IETF. It defines a small set of geometry types (Point, LineString, Polygon, and their multi variants) plus Feature and FeatureCollection objects that wrap geometry with arbitrary properties. Coordinates are always written as longitude, latitude on the WGS84 datum, which is what every modern web map library expects.

What is the difference between GeoJSON and JSON?

GeoJSON is a strict subset of JSON with extra rules. Every GeoJSON document is valid JSON, but not every JSON document is valid GeoJSON. The spec fixes the top-level object types, the structure of geometry objects, the coordinate ordering (longitude first), and the coordinate reference system (WGS84). Anything outside those rules is just JSON that happens to contain geographic data.

When should I use GeoJSON instead of a shapefile or KML?

Use GeoJSON whenever the data has to flow through a web stack: APIs, browsers, databases like PostGIS, or any of MapLibre, Leaflet, Mapbox GL, or OpenLayers. Shapefiles are still common in desktop GIS but ship as at least three files and use a 1990s binary format. KML is fine for Google Earth but verbose and XML-based. GeoJSON wins for anything touching JSON APIs.

How many decimal places should GeoJSON coordinates have?

Six is enough for almost everything. One unit in the sixth decimal place is 0.11 m of latitude, and RFC 7946 itself calls 6 decimals about 10 centimetres, well within GPS precision. Five decimals (1.1 m) suit address points and boundaries shown on a web map, and four (11 m) only suit city-level points. On the OpenStreetMap boundary of Amsterdam, going from 7 to 6 decimals shrank the gzipped file by 18 percent with a largest rounding error of 7 centimetres.

How do I make a GeoJSON file smaller?

Simplify the geometry first, then round the coordinates, then serve it compressed. On the 7,677-vertex OpenStreetMap boundary of Amsterdam, Douglas-Peucker simplification with a 1 metre tolerance removed 82 percent of the vertices and cut the file from 160 KB to 28 KB, while no point moved more than 1 metre. Rounding from 7 to 6 decimals and gzip do the rest. For very large datasets, switch to vector tiles.

Why are GeoJSON coordinates longitude first?

RFC 7946 fixes the order as longitude, latitude, optional altitude. This matches the x, y, z convention used in most graphics and GIS systems. It is the opposite of how humans write coordinates (latitude first, as in 51.5, -0.1) and is the single most common bug when hand-rolling GeoJSON: swap the two values and your point lands in the wrong hemisphere. Always longitude first in GeoJSON.

Found this useful? Share it.

About the author

Brent van der Heiden

Written by

Brent van der Heiden

Co-Founder & CEO at MapAtlas

Brent built MapAtlas out of a conviction that developers deserve location APIs with fair pricing and genuine end-user privacy. Before that he co-founded MapMetrics, a community mapping and navigation app. He writes about geospatial infrastructure, map data, AI search visibility, and how location data powers the products people rely on every day.

View all articles →
Back to Blog