Short answer

GeoJSON and TopoJSON encode the same vector geometries but with opposite priorities. GeoJSON (RFC 7946) stores every feature's coordinates in full and independently — simple, universally supported, and the lingua franca of web mapping. TopoJSON stores shared boundaries only once, as a set of line fragments called arcs, and has each feature reference the arcs it is built from. Because a border between two polygons exists as a single arc rather than being duplicated in both features, TopoJSON is typically smaller for polygon coverages and, crucially, lets you simplify geometry without tearing adjacent features apart.

The practical decision is not "which is better" but "does my data have shared boundaries, and does my consumer support TopoJSON?" For point data or for maximum interoperability, GeoJSON wins. For dense, adjacent polygon layers headed to a web client — administrative units, geological unit polygons, watershed boundaries — TopoJSON often wins decisively.

How topology encoding works

In GeoJSON, two polygons that share a border each contain a full copy of the coordinates along that border. Move one vertex and the two copies disagree, producing a sliver or gap. There is no concept of a shared edge.

TopoJSON breaks all geometry into arcs at the points where lines meet or diverge (junctions). The shared border becomes one arc. Polygon A references that arc in forward direction; polygon B references the same arc in reverse (encoded as a negative index, using one's-complement so ~0 = -1). The benefits follow directly:

  • No duplicated boundaries. A coverage where most edges are shared stores each edge once instead of twice.
  • Guaranteed coincidence. Because there is one physical copy of each border, A and B can never disagree.
  • Topology-aware simplification. Simplifying an arc changes the boundary for every feature that uses it at once, so adjacency survives.

TopoJSON also applies delta-encoding (each coordinate stored as an offset from the previous) and quantization (snapping coordinates to an integer grid). Delta-encoded small integers compress far better than full floating-point coordinates, which is a large part of the size win even before simplification.

When TopoJSON actually saves space

The size advantage scales with how much boundary is shared:

  • High benefit: wall-to-wall polygon coverages — countries, provinces, census tracts, geological map units, soil polygons. Most edges are shared, so deduplication is large.
  • Modest benefit: sparse or isolated polygons with few shared edges (deduplication has little to remove), where the win comes mainly from quantization and delta-encoding.
  • Little or negative benefit: point layers (no arcs to share) and tiny datasets (the topology header overhead can outweigh savings).

Quantization is the lever most people underuse. Quantizing to, say, 1e5 snaps coordinates to a grid roughly fine enough for web display while collapsing spurious precision. Combined with shared-arc deduplication and toposimplify, a national polygon layer that is megabytes as minified GeoJSON frequently drops to a fraction of that as TopoJSON — but the exact ratio depends entirely on adjacency, vertex density, and how aggressively you quantize and simplify, so measure rather than assume a fixed multiple.

The cost is precision: quantization discards coordinate detail, so it is appropriate for display layers, not for survey-grade or analytical data.

RFC 7946 rules you must respect either way

GeoJSON under RFC 7946 mandates WGS84 longitude/latitude (EPSG:4326) in [longitude, latitude] order — easting first. This trips up teams who export projected data or assume [lat, lon]. Reproject before export:

ogr2ogr -f GeoJSON -t_srs EPSG:4326 units.geojson units.gpkg

RFC 7946 also recommends limiting coordinate precision (six decimal places is ~0.1 m at the equator — more than enough for most maps) and right-hand-rule winding for polygon rings. TopoJSON inherits the lon/lat convention through its transform; it stores a transform object (scale and translate) that maps the quantized integers back to real coordinates, so a TopoJSON consumer must apply that transform before treating values as degrees.

Converting between the two

The canonical toolchain is the topojson family (from the d3 ecosystem):

# GeoJSON -> TopoJSON, naming the object "units", quantizing to 1e5
geo2topo -q 1e5 units=units.geojson > units.topojson

# Topology-preserving simplification (planar weight; tune the value)
toposimplify -P 0.05 units.topojson > units_simplified.topojson

# Back to GeoJSON when a consumer needs it
topo2geo units=units_out.geojson < units_simplified.topojson

ogr2ogr also reads and writes TopoJSON, which is handy inside GDAL pipelines:

ogr2ogr -f TopoJSON units.topojson units.gpkg

The order matters: quantize and build topology first, then toposimplify, because topological simplification needs the arc structure to keep shared borders coincident. Simplifying the GeoJSON first with a non-topological tool (or with QGIS's per-feature Simplify) reintroduces the slivers TopoJSON exists to prevent.

Consumer support

GeoJSON is read natively almost everywhere: MapLibre, Leaflet, OpenLayers, QGIS, PostGIS (ST_AsGeoJSON/ST_GeomFromGeoJSON), and every GIS. TopoJSON is not natively rendered by most map libraries — d3 understands it directly, but MapLibre/Leaflet need it converted to GeoJSON client-side (the topojson-client feature() function) before drawing. So TopoJSON is best thought of as a transport and storage format that you decode in the browser, trading a little client CPU for much less bytes over the wire. If your stack cannot decode it, ship GeoJSON (or, better for large data, vector tiles).

Common pitfalls and why they happen

  • Wrong axis order. Exporting [lat, lon] puts features in the wrong hemisphere; RFC 7946 requires lon first.
  • Forgetting the transform. Reading TopoJSON arc integers as if they were degrees yields nonsense; you must apply the transform scale/translate (the client libraries do this for you).
  • Per-feature simplification of shared borders. Simplifying GeoJSON polygons independently tears adjacent edges apart; use toposimplify on TopoJSON instead.
  • Over-quantizing analytical data. Quantization is lossy and fine for display only; never use a quantized TopoJSON as a survey or measurement source.
  • Reaching for TopoJSON on point data or huge datasets. Points share no arcs, and very large data belongs in vector tiles (MVT) or a PostGIS-backed service, not a single JSON blob of either kind.

Validation and QA

After conversion, round-trip back to GeoJSON and overlay both on the original to confirm boundaries still register. Check that adjacent polygons share identical borders (no gaps/slivers) using a topology check in QGIS or ST_IsValid/ST_Overlaps in PostGIS. Verify the CRS is EPSG:4326 and axis order is lon/lat. Compare feature counts and a sample of attribute values before and after, since simplification can occasionally drop tiny features entirely. Finally, measure the actual byte sizes (gzipped, as served) of each candidate format rather than trusting a rule of thumb.

Bathyl perspective

We pick the format from the destination, not habit: TopoJSON when a dense polygon coverage ships to a web client and topology-safe simplification matters, GeoJSON when interoperability or point data dominates, and vector tiles when the data outgrows both. Every delivery records its CRS, quantization, and simplification settings so the recipient knows exactly what precision they are holding.

Related reading

Sources