What Is GeoJSON? The Definitive Guide
GeoJSON is a plain-JSON text format for geographic features, standardised as RFC 7946 in 2016, and its coordinates are always longitude first, then latitude, on WGS 84. Someone sent you a .geojson and it opens as a wall of numbers. Or you exported one from QGIS and it draws in the North Atlantic. Or a web map rejects it as "not valid GeoJSON" when every JSON linter says it is fine. Every command and number below was run on 15 September 2026 against one real dataset, IBGE's 2022 state boundaries of Brazil, and every claim about our viewer was measured on the live tool.
What is GeoJSON?
GeoJSON is a text format for geographic features, written as plain JSON and standardised as RFC 7946 in August 2016. A file is usually one FeatureCollection holding an array of Feature objects, and each Feature has a geometry (the shape) and a properties object (the attributes, any JSON you like). There is no schema, no sidecar file and no binary section, which is why every web map library speaks it natively.
The smallest useful file is a single feature, and this one is a real fixture from our lab: {"type":"Feature","properties":{"name":"Brasília"},"geometry":{"type":"Point","coordinates":[-47.8825,-15.7942]}}. Read the coordinates carefully, because this is the sentence people skip and then lose an afternoon to: a GeoJSON position is written longitude first, then latitude. Brasília is [-47.8825, -15.7942], not the -15.79, -47.88 you would type into a search box. The spec says [longitude, latitude]; a swapped pair parses without complaint and draws in the wrong hemisphere.
The seven geometry types, and the eighth that holds them
Every geometry has a type and a coordinates array; only the nesting depth changes.
- Point — one position,
[lon, lat]. - MultiPoint — an array of positions.
- LineString — two or more positions, in drawing order.
- MultiLineString — an array of LineString coordinate arrays.
- Polygon — an array of closed rings; the first is the outer boundary, the rest are holes.
- MultiPolygon — an array of Polygon coordinate arrays. Brazil's 27 states are 12
Polygonand 15MultiPolygon, because a coastal state with islands is several rings that are not holes in each other. - GeometryCollection — the eighth type: a
geometriesarray of any of the above, with nocoordinatesof its own. It turns up where you did not ask for it, as we found below.
What RFC 7946 changed, and why your file may be older than the spec
Before 2016 the format was the 2008 "GeoJSON specification", a community document, and many exporters still write that flavour by default, GDAL included. RFC 7946 tightened five things, and each explains a file that "should work" and does not.
- WGS 84 only, and the CRS member is gone. An RFC 7946 file is always longitude and latitude in decimal degrees on WGS 84. The 2008 spec allowed a top-level
crsobject naming any coordinate system; the RFC removed it. A file with acrsmember is a 2008-spec file, and if it names anything but WGS 84 you have the problem in the next section. - The right-hand rule for rings. An outer ring runs counter-clockwise, holes clockwise. Shapefiles store exterior rings clockwise, so every polygon converted without the RFC flag is "backwards"; most readers do not care.
- The bounding box got a fixed order.
bboxalready existed in 2008; the RFC pins its meaning to[west, south, east, north], still optional on any object. - Three dimensions, no more. A position is
[lon, lat]or[lon, lat, elevation]; a fourth number should not be there; the RFC says SHOULD NOT, not MUST NOT. - Cut at the antimeridian. A shape crossing 180° longitude should be split in two. A recommendation for writers; readers vary.
What the flag actually did to a real file
We converted the same Shapefile twice, in the default 2008 mode and with -lco RFC7946=YES, and compared the outputs.
- The CRS member disappeared. The 2008-mode file opens with
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }on its fourth line; the RFC file goes straight from"name": "BR_UF_2022"to"features". Neither has abbox, which is opt-in. - Coordinates were rounded to 7 decimals. Acre's first vertex went from
[-68.792817347,-10.999569268]to[-68.7928173,-10.9995693]. The driver defaults to 15 decimals in 2008 mode and 7 in RFC mode; 7 decimals is about a centimetre. - Rings were rewound. Acre's outer ring, 41,498 vertices in both files, starts at the same vertex and walks the other way: clockwise in the 2008 file, counter-clockwise in the RFC file.
- The file shrank by a quarter. 39,274,442 bytes became 29,108,343, almost entirely from the shorter numbers.
- Three polygons changed type, silently. The 2008 file has 12
Polygonand 15MultiPolygon; the RFC file has 11Polygon, 13MultiPolygonand threeGeometryCollectionfeatures, for Alagoas, Paraná and Goiás. A second run with-lco RFC7946=YES -lco COORDINATE_PRECISION=15gave back 12 and 15, so the cause is the rounding: at 7 decimals tiny rings in Alagoas and Goiás collapse to fewer than four distinct points and one Paraná ring crosses itself; GDAL's RFC mode repairs them withMakeValid(), and the collapsed parts come out asLineStringandMultiLineStringmembers packed with the polygon into a collection, 36 vertices lost. GDAL says nothing about it unless you run with--debug on, which prints oneRunning MakeValid()line per repaired state. The 2008 mode with-lco COORDINATE_PRECISION=7takes a different route,SetPrecision(), and keeps 12Polygonand 15MultiPolygon. Precision is a geometry decision, and a validator run after conversion is cheap.
The classic wrong place: metres in a format that means degrees
The complaint reads "I exported my layer to GeoJSON and it is in the ocean" or "it is nowhere at all". The cause is almost always coordinates that are not [longitude, latitude] in degrees: swapped axes, or projected metres.
Our dataset ships in SIRGAS 2000 geographic coordinates, so to reproduce the bug we exported it to SIRGAS 2000 / UTM zone 23S: ogr2ogr -f GeoJSON utm.geojson BR_UF_2022.shp -t_srs EPSG:31983. GDAL does not refuse. It writes a 55,908,110-byte file, 1.4 times the 2008-mode degrees file (1.9 times the RFC file, which also rounds to 7 decimals), and Acre's first vertex becomes [-2171693.959168141, 8673426.088929612] where the correct file has [-68.7928173, -10.9995693]. The easting is seven digits and negative because Acre sits four UTM zones west of zone 23. GDAL also does what the 2008 spec allowed: it writes "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::31983" } } at the top, honest about the numbers but not GeoJSON in the RFC 7946 sense. GDAL honours that member on the way back in, ogrinfo reports the file as SIRGAS 2000 / UTM zone 23S, and QGIS reads vector files through GDAL, which is why such a file "works in QGIS and breaks on the web".
What happens next depends on the reader, so we measured ours. Our free GeoJSON viewer reads files with OpenLayers, which honours a crs member only when it names a coordinate system OpenLayers already knows: the WGS 84 aliases, including urn:ogc:def:crs:OGC:1.3:CRS84, and Web Mercator. EPSG:31983 is not on that list. Six fixtures, each dropped into the live tool:
- A CRS naming EPSG:31983, in either spelling. The viewer shows no error. The layers panel says
27 featuresand27 Polygon, everything looks loaded, and the exported GeoJSON puts Acre's first vertex at[-19.5086591263066, 61.20664023081724]: the North Atlantic between Iceland and Norway, about 9,000 km from Acre. The UTM metres were taken as Web Mercator metres and turned back into degrees. GDAL's ownurn:ogc:def:crs:EPSG::31983spelling gave the identical pair. - The same metres with no CRS at all. Still no error, still
27 features. With nocrsthe reader assumes degrees, so-2171693.96is a longitude of two million degrees. The exported first vertex is[-2171693.959168141, -53.911070387188786]: the latitude wrapped through the Mercator formula to −53.9°, the longitude came back unchanged, and the shape is nowhere you can pan to. Two silent failures from one file: "Brazil off Iceland" and "nothing on any map". - A 2008-style file declaring CRS84. Draws correctly. Acre's first vertex exported as
[-68.7928173712301, -10.999569659337354], the same pair the RFC file gave; the extra digits are the round trip through Web Mercator. - A CRS whose type OpenLayers does not recognise. Both the 2008 spec's
"type":"link"form and a made-up"type":"bogus"stop the parse with theError processing file. Make sure it is a valid GeoJSON file.banner. A malformedcrsfails loudly; a well-formedcrsnaming the wrong system fails quietly. That asymmetry is the interoperability problem RFC 7946 §4 cites when it removes the member.
The fix is on the writer's side and it is one flag: add -t_srs EPSG:4326 to the export so the file contains degrees. Do not edit the crs line by hand; the numbers inside are still metres, and no declaration turns them into [longitude, latitude]. Reproject with ogr2ogr -t_srs EPSG:4326; in the default 2008 mode GDAL still writes a CRS84 declaration, which is harmless (the viewer honours it, as the third fixture above showed), and -lco RFC7946=YES drops it entirely.
Converting to GeoJSON with ogr2ogr
The command that produces a file every reader agrees on is ogr2ogr -f GeoJSON out.geojson in.shp -t_srs EPSG:4326 -lco RFC7946=YES. GDAL's GeoJSON driver page lists every layer-creation option; these four change the file in ways you can see.
- RFC7946=YES — everything in the section on what the flag did to a real file: no
crs, rings rewound, 7 decimals, and possibly a geometry type change where rounding invalidates a ring. The default isNO. - COORDINATE_PRECISION=6 — one decimal fewer than the RFC default. A millionth of a degree is about 0.11 m at the equator (111,320 m per degree, divided by a million), still below the accuracy of most vector data. It took our file from 29,108,343 bytes to 26,839,688, a 7.8 % saving, and Acre's first vertex became
[-68.792817,-10.999569]. Five decimals is roughly a metre. - WRITE_BBOX=YES — adds a
bboxto the collection and to every feature: our file gained 28 of them,[ -73.9904500, -33.7511780, -28.8476399, 5.2718411 ]at the top and one per state, for 1,626 extra bytes. - ID_FIELD=CD_UF — promotes an attribute to the feature-level
"id"and removes it fromproperties. Our states came out as"id":"12","id":"13","id":"15", strings because the Shapefile field was text. Useful when a web map needs a stable key.
Three more things the lab settled.
- Elevation survives, despite the warning. Converting a
[-47.8825,-15.7942,1172]point with-dim XYZprintsWarning 1: Attempt to write Z geometries to layer pz that does not support them. Z component will be discarded, then writes[-47.8825,-15.7942,1172.0], which reads back as a3D Point. Do not take the warning at its word. - GeoJSONSeq is the streaming form.
ogr2ogr -f GeoJSONSeq out.geojsonl in.shp -t_srs EPSG:4326writes oneFeatureper line with no wrapping collection: 27 lines, 7-decimal coordinates, nocrs, 29,108,905 bytes. A line-oriented tool can stream it; a reader that expects oneFeatureCollectioncannot, because the whole file is not one JSON value. - TopoJSON is not GeoJSON. It encodes shared arcs once and is often far smaller, but its top-level object is a
Topology. GDAL reads it and cannot write it:ogr2ogr -f TopoJSONanswersERROR 1: TopoJSON driver does not support data source creation.Our viewer rejects it two ways depending on the name: a.topojsonnever reaches the parser and getsUnsupported file type. Use GeoJSON (.geojson or .json).; the same bytes renamed.jsonreach the parser and getError processing file. Make sure it is a valid GeoJSON file.
Size, measured
GeoJSON is big uncompressed and the smallest of the three common vector formats once zipped, because text deflates far better than binary shapes. These numbers are from our format-comparison lab of 15 September 2026 on the same dataset: IBGE's BR_UF_2022.zip, 13,717,460 bytes as downloaded, SHA-256 282ec7f0f0beeeead45e6609f4ffffce161bda04cb8ee0afcad2316d1c841bcb, 27 states, 1,138,650 vertices.
- Shapefile — 18,227,115 bytes for the five files together, the
.shpand its four sidecars; 13,794,222 bytes re-zipped at maximum compression. - GeoPackage — 18,386,944 bytes, slightly larger than the Shapefile because SQLite's pages and the spatial index cost more than the
.shxthey replace; 13,828,105 bytes zipped. - GeoJSON at the RFC default of 7 decimals — 29,108,343 bytes, the biggest on disk by a wide margin; 8,457,365 bytes zipped, the smallest of the three by about 5.1 MiB.
- GeoJSON at 6 decimals — 26,839,688 bytes; 7,132,808 bytes zipped.
So on disk and in a browser's memory, GeoJSON costs 1.6 times what a Shapefile does; zipped, it costs 61 %, and the size argument against it mostly disappears. The full eight-criterion comparison, including browser load times, is in our Shapefile vs GeoPackage vs GeoJSON post.
How to open a GeoJSON file
You do not need a desktop GIS to look at one. Drop the file on our GeoJSON viewer and it draws the features on a base map, lists them in a layers panel with a count per geometry type, and opens a sortable attribute table. The step-by-step is in how to open a GeoJSON file online; the facts below were measured on the live tool with the files from this guide.
- What it accepts. The file picker filters on extension,
.geojson,.json,.kmland.kmz, but a dragged file skips that filter and is judged by its suffix in the parser: a GeoJSON renamed.txtgetsUnsupported file type. Use GeoJSON (.geojson or .json).There is no size limit and no feature limit; the 56-megabyte UTM file above loaded in under two seconds, and the ceiling is your browser's memory, for which we will not invent a number. - One file per drop, ten per session. Dropping two files at once loads the first and ignores the second. After the tenth file the Add button in the layers panel simply disappears; there is no "too many files" message.
- What the counts mean. The RFC 7946 file shows
27 featuresand24 Polygon, because the threeGeometryCollectionstates belong to no geometry group; the 2008-mode file shows27 featuresand27 Polygon. A collection is labelledCollectionin the attribute table's Type column and in the feature panel, not in the layers row. - A bare geometry works. A file whose top level is a single
Feature, or even a nakedPoint, loads as one feature; the naked point shows0 fields. - Rings are not checked. A clockwise outer ring draws normally and exports still clockwise; the viewer neither validates nor repairs winding.
- Styling in properties is applied and then hidden. The simplestyle keys,
fill,fill-opacity,stroke,stroke-width,marker-color,marker-size,marker-symbol, style the map and are hidden from the attribute table: a feature with seven style keys plusnameandplainlists2 fields. - Lines and polygons are de-jittered by 20 cm. Every non-point geometry is simplified with a 0.2-metre tolerance before drawing and before export. A test line whose middle vertex sat 5 cm off the straight path lost it; one 1 m off kept all 3 vertices. For state boundaries that is 15,249 vertices out of 1,138,614, invisible at any zoom; where 5 cm matters, keep the original and treat the export as a picture.
As general advice for QGIS, not something this guide measured: drag the file onto the map canvas. If the layer properties show anything other than EPSG:4326, export it again with -t_srs EPSG:4326 before publishing.
Test files you can use
Every URL here was requested on 15 September 2026 with a browser user agent, recording status, Accept-Ranges and the CORS header. A list like this rots; read it as a snapshot of one day.
- Natural Earth 1:110m countries, the file to start with. is 838,726 bytes, answers
206withaccept-ranges: bytesandaccess-control-allow-origin: *, and is small enough to download and drop. Licence: the repository's LICENSE.md opens with "Everything here is public domain." - Brazil's outline from IBGE's own API. returns GeoJSON directly: 65,655 bytes, one
MultiPolygonfeature, nocrsmember, read by GDAL as WGS 84. It answers200withaccess-control-allow-origin: *but noaccept-ranges, so it is alive and open for a download-then-drop, not a range request. The response carries no licence text; it is IBGE's public data, so cite the source. - The 27 states we used.
BR_UF_2022.zipfromgeoftp.ibge.gov.br, named in the size section, is a Shapefile; convert it with the command above and you have our exact 29,108,343-byte file. - One that is dead, so you stop looking. The OKFN data-portal copy of the world boundaries at
data.okfn.org/data/datasets/geo-boundaries-world-110m/data/countries.geojsonis still linked from older tutorials and its hostname no longer resolves. When a sample URL fails at DNS, the data usually moved to GitHub; search for the file name.
Where Geodocs fits
We build Geodocs, a platform for field data and GIS teams, and we wrote this guide because GeoJSON is the format our users hand us most often, and the crs member is what breaks most often.
Working with the other formats? Our complete guide to the Shapefile format covers the sidecar files and the .prj problem this guide's wrong-place section inherits; the GeoPackage guide is the one-file alternative; and the KML and KMZ guide covers the other text format the same viewer opens. To look at a file right now, the GeoJSON viewer linked above is free and runs in your browser.
Spotted an error, or a gotcha we missed? Tell us. We re-test this page rather than copy-pasting it.
Last verified: 15 September 2026.