Skip to content

GeoJSON File Size Optimization

One public-domain dataset taken from 55 MB down to 4 MB. What each optimisation is actually worth once the file is compressed.

One dataset, all the way down this page

Every number here was measured on the same file: Natural Earth 1:10m Admin 0 Countries, which is public domain. It is a useful subject because it is bad in all four of the ways a GeoJSON file is usually bad. It carries 548,471 positions across 258 features, 15 decimal places of coordinate precision, and 168 properties on every feature, because it ships country name translations for dozens of languages.

As ogr2ogr writes it, that is 55.09 MB. By the end of this page it is 4.16 MB, a reduction of 92%, and it still renders as world countries.

Using one file throughout means the steps are comparable. A percentage quoted from one dataset and a percentage quoted from another cannot be added together, which is how optimisation advice ends up promising more than it delivers. You can run the same steps on your own data with the optimizer, and the numbers behind this page are published as a dataset of their own.

Compress first, because nothing else comes close

Before optimising anything, turn on gzip or Brotli. Serving the untouched file compressed takes it from 55.09 MB to 7.40 MB, which is 87% for a configuration change and no loss of anything.

This matters for a second reason. Compression changes which of the four optimisations is worth doing, and most advice about GeoJSON file size is written as though it does not exist. Every table below gives both numbers.

Minifying: the big number that is not there

Stripping indentation takes the file from 55.09 MB to 21.44 MB. That is 61% off, and it is the most impressive-looking step on this page.

Compressed, the same change is 7.40 MB to 6.53 MB, or 12%. Whitespace is the most compressible thing in a text file, so gzip had already removed nearly all of it. If you serve compressed, minifying buys you very little.

It is still worth doing, because the file also sits on disk and passes through memory uncompressed. Just do not budget for it. Everything below is measured on the minified file, so no later step is credited with removing whitespace it never touched.

Properties: it depends entirely on your feature count

This dataset carries 168 properties per feature. Keeping only 4 of them, NAME, ISO_A3, CONTINENT, POP_EST, takes it from 21.44 MB to 20.53 MB. That is only 4%.

Dropping 164 of 168 fields and saving 4% looks like a poor trade, and for this file it is. Property weight scales with the number of features, and 258 is a small number. On a file of twenty thousand points carrying the same fields, this step would dominate and simplification would barely register.

So the honest rule is that there is no rule. Check which case you are in before you spend effort: the optimizer reports what each step saved on your file specifically, which is the only number that applies to you.

Simplification: usually the largest single win

Simplification removes positions that do not change the shape at the zoom level you care about. It is the only step here that alters your geometry, and on this dataset it is worth more than the other three together.

ToleranceRoughlyPositions keptSizeCompressed
0.0001°11 m533,201 (97%)19.98 MB6.05 MB
0.0005°56 m523,235 (95%)19.60 MB5.94 MB
0.001°111 m490,344 (89%)18.37 MB5.58 MB
0.005°557 m282,393 (51%)10.60 MB3.27 MB
0.01°1,113 m190,479 (35%)7.17 MB2.25 MB
0.05°5,566 m63,357 (12%)2.42 MB0.78 MB
0.1°11,132 m40,873 (7%)1.58 MB0.50 MB

The curve is not linear and that is the useful part. A tolerance of 0.0001 degrees, about 11 metres, removes only 3% of the positions, because a coastline drawn at survey precision has almost no vertices that close together to discard. At 0.01 degrees, about 1,113 metres, it keeps 35% of the positions and the file is 7.17 MB.

Pick the tolerance from the zoom level you render at, not from the file size you want. A tolerance near the ground distance of one screen pixel at your maximum zoom is invisible to a reader and everything beyond it is waste.

The cost of this step is shared borders. Each polygon is simplified on its own, so a border between two countries is simplified twice, once from each side, and the two results do not have to agree. Gaps and overlaps appear along shared edges. Where that matters, TopoJSON stores shared edges once and simplifies them once.

Coordinate precision: free, and better after compression

Every coordinate in the source carries 15 decimal places. That is a resolution of nanometres, describing borders that were surveyed in metres. The extra digits are not precision, they are the residue of a floating point number printed in full.

Decimal placesWorst case errorSizeCompressed
15,566 m6.74 MB0.44 MB
2557 m7.68 MB1.58 MB
356 m8.72 MB2.52 MB
46 m9.76 MB3.14 MB
50.56 m10.81 MB3.73 MB
60.06 m11.85 MB4.24 MB
7under a centimetre12.90 MB4.70 MB
9under a centimetre14.98 MB5.43 MB
12under a centimetre15.00 MB5.44 MB
15under a centimetre20.53 MB6.25 MB

Six decimal places is about eleven centimetres at the equator and is finer than almost any source data deserves. Going from 15 places to six takes this file from 20.53 MB to 11.85 MB, or 42%.

Compression makes this change worth more, not less. 6.25 MB to 4.24 MB, which is 32%. Shorter numbers repeat more, and repetition is what compression exploits. This is the exact opposite of how minifying behaves, and it is why precision reduction is the step most worth doing and the one most often skipped.

Four places is about 5.6 metres and takes it to 9.76 MB. Two places is 557 metres, which is too coarse for anything but a world map, and it reaches 7.68 MB. Choose from the accuracy your data actually has, not from the size you want.

All four, in order

Applied in the order the optimizer uses, which is the order that keeps each step from doing work the next one discards:

StepSizeCompressedPositions
As exported by ogr2ogr55.09 MB7.40 MB548,471
Minified21.44 MB6.53 MB548,471
Kept 4 of 168 properties20.53 MB6.25 MB548,471
Simplified at 0.01 degrees7.17 MB2.25 MB190,479
Coordinates at 6 decimal places4.16 MB1.54 MB190,479

55.09 MB to 4.16 MB on disk, and 7.40 MB to 1.54 MB over the wire. The compressed figure is the one your readers experience, and it is a smaller multiple than the headline: 92% uncompressed against 79% compressed.

The order changes the numbers along the way and leaves the end result the same. Stripping properties first means simplification is not carrying fields it is about to lose, and simplifying before rounding means precision runs over 35% as many positions. Reverse the order and you reach a similar place, having done more work to get there.

Try it on your own data

The GeoJSON optimizer runs all four steps in your browser and reports what each one saved on your file, as a running total. Nothing is uploaded.

Start by switching on precision reduction alone, because it is free and reversible in the sense that nothing about the shape changes. Then add simplification and watch the original outline, drawn in grey behind the result, until the difference starts to show. That tolerance minus one step is the one to ship.

When to switch formats instead

Past a point, the format is the problem. GeoJSON stores every coordinate as text and every shared border twice.

TopoJSON for shared borders

Stores each shared edge once as an arc that both features point at. For administrative boundaries, where almost every edge is shared, this removes duplication that no amount of simplification can reach, and it keeps borders aligned when simplified. The comparison guide covers the trade.

Vector tiles for large datasets

A tiled format sends only the area in view at only the detail that view needs. For anything a reader pans and zooms around, this beats every optimisation on this page, because the winning move is not sending the data at all.

FlatGeobuf for random access

A binary format with a spatial index, readable a feature at a time over HTTP range requests. Useful when a program needs part of a large file and no person needs a picture of all of it.

Reproducing these numbers

Every measurement on this page was taken by one script, run against Natural Earth 1:10m Admin 0 Countries, which is public domain. It pins that URL so a rerun measures the same bytes, and two consecutive runs produce a byte-identical result.

Compressed sizes are gzip at level 9, and every one was measured. Your server will use a different level and may use Brotli, so treat the compressed column as the shape of the answer and not the exact figure you will see.