Geometry format size comparison, measured
“Which geometry format is smallest?” has a real answer, and it is not the one most people guess. Below are the encoded sizes of the same geometry across a dozen formats — WKT, EWKT, WKB, EWKB, TWKB, GeoJSON, encoded polyline (precision 5 and 6), geohash, H3 and S2 — all produced by this site’s own codecs, so every number is reproducible. Paste your own geometry into the tool and the bars re-sort live. Nothing you paste leaves your browser.
Detected Polygon · parsed as WKT
Not representable for this geometry
- Encoded Polyline (precision 5)A Polygon is not representable as an encoded polyline; it encodes point sequences only (Point, MultiPoint, or LineString).
- Encoded Polyline (precision 6)A Polygon is not representable as an encoded polyline; it encodes point sequences only (Point, MultiPoint, or LineString).
- GeohashGeohash encodes a single point or grid cell, so a Polygon can't be encoded as one. To get a geohash for this geometry, encode its centroid as a point instead (compute the centroid, then encode that Point).
- H3 CellH3 encodes points to cells; polygon fill (polyfill) is a planned feature. Give a Point (such as a cell center) to get an H3 index.
- S2 CellAn S2 cell token represents a single point's cell — a Polygon has no S2 cell. Encode a Point, or parse an S2 cell first to round-trip it.
- Lat/Lng TextLat/lng text can only encode points and paths (Point, MultiPoint, LineString), not Polygon.
The chart starts on a small 6-vertex polygon. Toggle binary as raw bytes vs hex chars to see the “hex tax”, and note which formats can’t represent a given geometry at all — they show the engine’s own error message instead of a bar.
“Binary is smaller” is only half true
The intuition that binary beats text is real for high-precision coordinates and wrong almost everywhere else. WKB spends a fixed 16 bytes per vertex — two 8-byte doubles — no matter how many decimals you actually wrote. Text spends roughly one character per digit. So at low precision, text is already competitive: a 2-decimal point in WKT is 19 characters versus 21 raw bytes of WKB, and the 4-decimal polygon below ties them (112 characters of WKT against 109 bytes of WKB) while halving hex-encoded WKB. WKB only wins clearly at full double precision — around 15+ significant digits — where every one of those 16 fixed bytes is carrying information.
Sample 1 — a point at ~1.1 km precision
Input: POINT (-87.65 41.85) — two decimals, roughly city-block precision.
| Format | Size | Notes |
|---|---|---|
| geohash | 9 chars | cell, ~±2.4 m |
| polyline5 | 10 chars | lossy, ~1 m |
| S2 token | 10 chars | cell; token truncates trailing zeros |
| polyline6 | 12 chars | lossy, ~0.1 m |
| lat,lng text | 13 chars | |
| H3 | 15 chars | cell, resolution 15 |
| WKT | 19 chars | smallest exact text |
| WKB raw | 21 bytes | 1 + 4 + 16 |
| TWKB raw | 10 bytes | smallest exact overall |
| WKB hex | 42 chars | 2× raw — what you pay in JSON/URLs |
| GeoJSON | 45 chars | largest |
Sample 2 — the same point, but 6 decimals
Input: POINT (-87.650052 41.850437) — sub-decimetre precision. Adding four
decimals shows exactly which formats are variable-width and which are fixed. WKT grows by 8
characters to 27, and GeoJSON by the same 8 to 53 — text pays per digit. Raw WKB stays 21
bytes and TWKB stays 10 bytes, because doubles (and TWKB’s declared precision) are
fixed-width. And polyline5/6, geohash, S2 and H3 are unchanged: they are fixed-size by
design, which is the whole trade — you buy a small, predictable footprint by giving up the
ability to store arbitrary precision.
Sample 3 — a 6-vertex polygon (small geofence)
Four decimals, six vertices — the geometry pre-loaded in the tool above. This is the row that breaks the “binary is smaller” reflex: WKT (112) essentially ties raw WKB (109) and is barely half of hex WKB (218), and TWKB’s 32 bytes next to the 218-character hex blob is the single most persuasive number on this page.
| Format | Size | Notes |
|---|---|---|
| TWKB raw | 32 bytes | 64 hex chars |
| WKB raw | 109 bytes | native binary |
| WKT | 112 chars | smallest exact text |
| EWKT | 122 chars | WKT + SRID=4326; |
| GeoJSON | 150 chars | largest |
| WKB hex | 218 chars | 2× raw bytes |
| polyline / geohash / H3 / S2 | n/a | not representable — single-cell / path-only formats |
Sample 4 — a 40-point route
A GPS track: 40 points about 30 m apart, 6 decimals. This is where delta encoding runs away with it. TWKB stores the whole route in 89 bytes — 2.2 bytes per point — and an encoded polyline stores it as 148 URL-safe characters (3.7 per point), while raw text formats spend ~21 characters per point and hex WKB balloons to 1,298.
| Format | Size | Per point |
|---|---|---|
| TWKB raw | 89 bytes | 2.2 B/pt |
| polyline5 | 148 chars | 3.7 c/pt |
| polyline6 | 168 chars | 4.2 c/pt |
| TWKB hex | 178 chars | 4.5 c/pt |
| WKB raw | 649 bytes | 16.2 B/pt |
| WKT | 836 chars | ~21 c/pt |
| lat,lng lines | 864 chars | ~22 c/pt |
| GeoJSON | 942 chars | ~23 c/pt |
| WKB hex | 1,298 chars | ~32 c/pt |
The hex tax
WKB and TWKB are binary. A binary format is only compact while it stays binary — a native
bytea / BLOB column, or a wire protocol. The instant it has to
travel through JSON, a URL, an XML attribute, or a text column, it gets hex-encoded and every
byte becomes two characters. In every single sample above, hex-encoded WKB was the
largest encoding — bigger than the self-describing GeoJSON it is supposed to beat. If
your transport is text, “compact binary WKB” is usually the worst thing you can reach for.
Store WKB as real bytes or don’t use it.
TWKB is the real compact binary
TWKB — Tiny WKB — is what people think WKB is. It combines three ideas: variable-length integers (small numbers take fewer bytes), delta encoding (each vertex stored as an offset from the previous one), and a declared precision so it never stores digits you didn’t ask for. The result was 10 bytes for a point, 32 for the polygon, and 89 bytes for the 40-point route — 7.3× smaller than WKB and 10.6× smaller than GeoJSON on that route. Even hex-encoded, TWKB beat every text format except polyline5. The catch is tooling: TWKB is well supported in the PostGIS/GEOS world but far less ubiquitous than WKB or GeoJSON.
Delta encoding rewards dense paths
Encoded polylines and TWKB share a superpower: their cost scales with the spacing between points, not with the magnitude of the coordinates. Storing “+30 metres east” takes a couple of characters whether you are in Chicago or Christchurch. On the 40-point route that meant 3.7 characters per point for polyline5 versus ~21 for raw text. Precision is nearly free, too: polyline6 gives you 10× the resolution of polyline5 for only 13% more size, because the deltas dominate the encoding, not the extra digit. Two caveats keep this honest — it is lossy (coordinates are rounded to the declared precision) and it only works for points and paths, never polygons with holes.
How to read an encoded polyline
Each point is stored as a delta from the previous point, that delta is zig-zag encoded (so small negative numbers stay small), split into 5-bit chunks, and each chunk is mapped to a printable ASCII character. Close-together points produce tiny deltas which produce 2–4 characters per point; the very first point is absolute and therefore the most expensive one in the whole string. That is why a dense, smooth GPS track compresses so well and a sparse set of far-flung points does not.
Cell IDs buy size by answering a different question
A geohash, S2 token, or H3 index is the smallest possible way to say “where”, because it doesn’t store a coordinate at all — it stores which cell. Geohash is ~9 characters for roughly ±2.4 m; an S2 token is variable length up to 16 characters (trailing zeros truncate, which is why the sample point’s token was only 10); H3 is a fixed 15-character hex string. The trade is right there in Sample 2: four extra decimals changed the WKT, polyline, geohash and S2, but the H3 index was byte-for-byte identical — a cell ID answers “which cell contains this?”, not “exactly where is this?”. And a cell is one point: turning a polygon into cells means a cover that can explode into hundreds of IDs, so “n/a” in the polygon table above is the honest answer, not a small number.
GeoJSON is the biggest in every test — on purpose
GeoJSON came out largest in every sample, and that is the price of what it buys: it is self-describing, human-readable, and parseable by essentially every tool and language without a schema. That makes it the right default for interchange — handing geometry to a system you don’t control — and the wrong default for storage at scale or for URL state, where its repeated keys and brackets are pure overhead. Use it at your boundaries, not in your hot path.
The honest caveat: gzip narrows the gap
Every number on this page is uncompressed size, and that matters because compression changes the story for some use cases. GeoJSON’s repetitive structure gzips and brotlis extremely well, so on a compressed HTTP response the difference between formats shrinks a lot — if bytes-on-an-already-compressed-wire is your only concern, don’t over-optimise. But compression does not help the cases where these gaps bite: URL and hash-fragment state (never compressed), primary keys and spatial-index columns, in-memory arrays, and per-row cost across billions of rows. This comparison matters most for storage, URLs, and memory — say so, and choose accordingly.
Which format when
A quick lookup — match the job on the left to its winner.
| Scenario | Winner | Runner-up |
|---|---|---|
| Exact point, human-readable | WKT / lat,lng | GeoJSON |
| Approximate point (a cell is OK) | geohash / S2 token | H3 |
| Route or track in a URL / JSON | polyline5 (polyline6 if sub-metre) | TWKB hex |
| Polygon, text transport, ≤ 6 decimals | WKT | GeoJSON |
| Polygon / route, binary storage | TWKB | WKB |
| Database column, full precision | WKB (native binary — never hex-in-JSON) | TWKB |
| Interchange with unknown tooling | GeoJSON | WKT |
The example encodings, verbatim
These are the exact codec outputs behind the numbers above — paste any of them into the tool (or the universal inspector) to check them yourself.
Point, 2 decimals — POINT (-87.65 41.85)
WKT POINT(-87.65 41.85)
EWKT SRID=4326;POINT(-87.65 41.85)
GeoJSON {"type":"Point","coordinates":[-87.65,41.85]}
WKB hex 01010000009a99999999e955c0cdccccccccec4440
EWKB hex 0101000020e61000009a99999999e955c0cdccccccccec4440
TWKB hex a1008ff9ad08d0eefe03
polyline5 oyl~Fnc~uO
polyline6 _hiynA~kvdfD
geohash dp3wj6x1y
H3 892664cf473ffff
S2 token 880e2c50c4
lat,lng 41.85, -87.65 Point, 6 decimals — POINT (-87.650052 41.850437)
WKT POINT(-87.650052 41.850437)
GeoJSON {"type":"Point","coordinates":[-87.650052,41.850437]}
WKB hex 0101000000c32cb4739ae955c078279f1edbec4440
TWKB hex a10099f9ad08a6effe03
polyline5 g|l~Fxc~uO
polyline6 icjynAfovdfD
geohash dp3wj6xhm
H3 892664cf473ffff ← IDENTICAL to the 2-decimal point
S2 token 880e2c50cc
lat,lng 41.850437, -87.650052 Note the H3 index: four extra decimals changed WKT, polyline, geohash and S2, but not the H3 cell — the cell ID answers “which cell”, not “exactly where”. WKB hex is the same length but different bytes, because doubles are fixed-width.
Polygon, 6 vertices, 4 decimals
WKT POLYGON((-87.6501 41.8501,-87.6512 41.8443,-87.6455 41.8421,-87.6398 41.8465,-87.6423 41.8512,-87.6501 41.8501))
GeoJSON {"type":"Polygon","coordinates":[[[-87.6501,41.8501],[-87.6512,41.8443],[-87.6455,41.8421],[-87.6398,41.8465],[-87.6423,41.8512],[-87.6501,41.8501]]]}
TWKB hex a3000106a3f9ad08e2eefe03db018709f408b503f408f006f303ac07970cdb01
WKB hex 010300000001000000060000004bc8073d9be955c0302aa913d0ec4440efc9c342ade955c0a301bc0512ec44405a643bdf4fe955c016fbcbeec9eb4440c5feb27bf2e855c03108ac1c5aec4440228e75711be955c0772d211ff4ec44404bc8073d9be955c0302aa913d0ec4440 The 64-character TWKB next to the 218-character hex WKB is the whole argument in one line. polyline, geohash, H3 and S2 are n/a here — a polygon has no single cell and an encoded polyline can’t carry a ring — and the tool surfaces each codec’s own message when you paste this in.
Route, 40 points, 6 decimals — the polyline showcase
polyline5 (148 chars, the full 40-point drive, ~1 m precision, URL-safe):
o{l~F|a~uOWeAe@k@y@_@Q{@s@aAo@c@Oe@}@aAa@y@[_@}@m@UeAi@o@w@]Oy@w@cAk@e@Sc@}@_A]}@]_@_Ak@QeAm@q@u@]Ou@y@eAg@g@Ua@_A}@Y_Aa@a@}@g@OeAq@u@q@]Qq@{@eAc@k@ polyline6 (168 chars, ~0.1 m, only 13% longer):
_|iynAj|udfDoFyTuJaMqQyHeDuQcOsS_NgJqDwJgR_TmIcQoGsH}RuMsE{T{K}MyPqHaD{PcPcTyL}JcEaJuRoSgH}QsH}HqRwL_EyTcM{N{OkH_D_PcQoToKwK{EoI_SyRcGuRyIkIcR}KkDqTkNyOwNkHiDaOyQwTiJqL TWKB hex (89 raw bytes → 178 hex chars):
a20028ddf8ad0890effe0344162c241e3a3a10423224302410423c3a201e1a2e3e46142e281e3838104236262c2212403e3c1c1e1e2c3e4412322c1e363610443a282820163e3e3e1a2022283c441034301c323210443c2a24
For contrast the same route is 836 characters as WKT (it starts
LINESTRING(-87.64975 41.85032,-87.649401 41.85044,…), 942 as GeoJSON, and 1,298
as hex WKB. Same 40 points; a 14.6× spread from smallest to largest.
Methodology and format references
Every table value is the literal UTF-8 character count or raw byte count emitted by this site's tested codecs for the verbatim examples above. No estimated or vendor-reported sizes are used.
- OGC Simple Feature Access — Common Architecture — WKT and WKB geometry model
- PostGIS ST_AsTWKB — TWKB precision and compact-encoding behavior
- Google Encoded Polyline Algorithm Format — delta encoding, precision and ASCII representation
- RFC 7946 — The GeoJSON Format — GeoJSON structure and coordinate representation