GeoParquet vs FlatGeobuf for Cold-Archive Retrieval

Choosing between GeoParquet and FlatGeobuf for a cold spatial archive is a decision about access pattern, not file size: GeoParquet’s columnar layout and per-row-group statistics win analytical scans that touch few columns across many features, while FlatGeobuf’s packed Hilbert R-tree and HTTP range streaming win feature-by-bounding-box reads served straight from object storage. Both are cloud-native, both read anywhere GDAL runs, and both beat legacy shapefiles for archival — but they optimize opposite retrieval shapes, and picking the wrong one turns a penny range request into a multi-gigabyte download. This decision guide is for the data engineers and cloud architects who must commit an archive to one format per access tier under the Format Conversion & Pipeline Automation framework, weighing predicate pushdown, index structure, compression, and tooling against how each dataset is actually retrieved.

Framing the Decision

Cold-archive retrieval is dominated by two workloads that pull the format choice in opposite directions. The first is the analytical scan: “sum the burned area across every wildfire polygon in the 2015–2023 archive,” which reads two or three columns from millions of features and never materializes geometry. The second is the point read: “fetch the parcels intersecting this map viewport,” which needs whole features inside a small bounding box and nothing else. A columnar format serves the first for the cost of reading a few column chunks; a spatially indexed feature format serves the second for the cost of an R-tree descent plus a few range requests. The mistake is committing an archive to one format before profiling which workload dominates — or worse, assuming one format can be optimal for both. The tiering logic here mirrors the broader hot/warm/cold tier design for geospatial data: access pattern, not age alone, drives the layout.

Head-to-Head Comparison

The two formats differ on every axis that matters for retrieval economics:

GeoParquet versus FlatGeobuf retrieval comparison matrix An eight-row matrix comparing GeoParquet and FlatGeobuf across physical layout, spatial index, best access pattern, compression, HTTP range read granularity, predicate pushdown, partial column read, and tooling. GeoParquet leads on analytical and columnar dimensions; FlatGeobuf leads on per-feature spatial reads and streaming. Dimension GeoParquet FlatGeobuf Physical layout columnar (Parquet) row-oriented per feature Spatial index row-group bbox stats packed Hilbert R-tree Best access pattern analytical column scans feature-by-bbox reads Compression strong per-column ZSTD lighter, packed geometry HTTP range read row-group granularity per-feature streaming Predicate pushdown strong (column + stats) spatial bbox only Partial column read yes (projection) no (whole feature) Tooling DuckDB · Arrow · GDAL GDAL · web mapping libs

The Same Query Against Both Formats

The clearest way to separate the formats is to trace one realistic archive query through each. The query — “give me every parcel in this district with a land-use code of R3, as of the 2024 generation” — has a spatial predicate, an attribute predicate, and a projection, which is the normal shape of an archival read.

One archival query through GeoParquet and through FlatGeobuf Parallel traces of the same query. GeoParquet applies directory pruning, row-group statistics, column statistics and column projection to transfer 34 megabytes. FlatGeobuf applies the spatial index only and transfers whole features, 210 megabytes, filtering attributes on the client. Query: parcels in district 41 where landuse = R3 · return geometry + parcel_id GeoParquet partition prune 1,400 → 1 dir bbox statistics 64 → 15 groups landuse stats 15 → 4 groups project 2 cols of 22 34 MB transferred FlatGeobuf pick file from manifest R-tree descent 4 requests fetch features whole records filter landuse client-side 210 MB transferred The gap is not the spatial index — both resolve the extent well. It is the two filters GeoParquet can apply before transfer: attribute statistics and column projection. Where the query has no attribute predicate and needs every field, the gap closes to about 1.4×.

That last sentence is the honest qualification. FlatGeobuf’s disadvantage here is specific to selective attribute queries over wide tables; for a map client that wants whole features in an extent — which is what it was designed for — it transfers less than a columnar reader that has to reassemble rows from twenty-two column chunks, and it does it without an engine.

Per-Dimension Analysis

Physical layout and partial reads

GeoParquet stores each attribute in its own contiguous column chunk, so a query that needs burn_area_ha and fire_year reads exactly those two chunks and skips the geometry entirely. That projection is impossible in FlatGeobuf, where every feature is a self-contained record: reading one attribute means paging in the whole feature, geometry included. For an analytical scan over a wide attribute table, this is the single largest difference in bytes transferred — often an order of magnitude — because cold-storage cost is dominated by what you move, not what you store. FlatGeobuf’s row orientation is the correct trade only when you genuinely want the whole feature.

Index structure and the point-read path

FlatGeobuf embeds a packed Hilbert R-tree in the file header, laid out so a bounding-box query resolves to a small set of byte ranges without a full scan. A client issues one range request for the index, walks it, then issues range requests for the matching feature bytes — three or four round trips to pull the parcels in a viewport, straight from S3 with no server. GeoParquet’s spatial “index” is coarser: per-row-group bounding-box statistics let a reader skip row groups that cannot intersect the query, but within a surviving row group it still scans. If your row groups are spatially clustered — the payoff of spatial partitioning techniques — this pruning is effective for regional filters, but it will never match FlatGeobuf’s per-feature precision for a tight bbox over a dense layer.

Compression and cold-storage footprint

Columnar storage compresses better because a column is a run of like-typed, often low-cardinality values — exactly what dictionary and ZSTD encoders exploit. A GeoParquet archive of categorical vector data routinely lands well under half the size of the equivalent FlatGeobuf, and the ratio widens as you tune the encoder; the level-versus-latency trade-off is laid out in ZSTD level configuration for spatial files. FlatGeobuf compresses its geometry buffer efficiently but cannot reach columnar ratios on attributes, because each feature interleaves fields of different types. For a deep-archive tier billed purely on stored bytes, GeoParquet’s footprint advantage is decisive; for a warm tier billed on retrieval, footprint matters less than range-read efficiency.

Streaming and serverless retrieval

FlatGeobuf was designed to stream: a reader can begin emitting features before the whole file arrives, and the format is a first-class citizen of browser mapping stacks that fetch directly from object storage over HTTP range requests. That makes it the natural archive format when the retrieval client is a map, not a query engine, and there is no compute layer between the bucket and the user. GeoParquet retrieval assumes a reader that understands row groups and statistics — DuckDB, Arrow, GDAL, or a Spark job — so it shines when a serverless SQL engine sits in front of the archive, and is awkward when the consumer is a thin web client. The optimization details for the streaming path are covered under FlatGeobuf optimization techniques, and the columnar migration path under GeoParquet migration workflows.

Tooling and durability

Both formats are open and GDAL-native, so neither is a lock-in risk. GeoParquet inherits the entire Parquet and Arrow ecosystem — DuckDB, pandas, Spark, cloud query services — which is a large advantage for analytical archives and for interoperability with non-spatial data lakes. FlatGeobuf’s ecosystem is narrower and mapping-centric, but its specification is simpler, which some archivists weigh as a durability argument for a format that must remain readable for decades. Both are backed by public specifications; verify writer conformance against the GeoParquet specification and the FlatGeobuf specification before committing an archive.

Recommendation by Scenario

Analytical archive queried by SQL — choose GeoParquet. When the dominant workload is aggregations, filters, and joins across many features touching few columns — climate model outputs, census-scale demographics, sensor time series — the columnar layout and predicate pushdown make GeoParquet the clear winner, and its compression minimizes the deep-archive bill.

Web-map archive served straight from object storage — choose FlatGeobuf. When retrieval means “give me the features in this viewport” and the client is a browser hitting a bucket with no query engine in between, FlatGeobuf’s packed R-tree and range streaming deliver sub-second bbox reads that GeoParquet cannot match on a dense layer.

Mixed archive with both patterns — dual-encode by tier. Keep the authoritative copy as partitioned GeoParquet in the cold, cost-optimized tier for analytics and long-term preservation, and derive a FlatGeobuf copy into a warm, retrieval-optimized tier for the map-serving path. The GeoParquet partition boundary maps cleanly onto the FlatGeobuf tiles, so the derivation is deterministic, and each tier’s storage class is chosen against its real access economics rather than a single compromise.

Uncertain or evolving access pattern — default to GeoParquet. Its broader tooling and superior compression make it the safer default when you cannot yet profile the workload; a FlatGeobuf derivative can always be generated later from the columnar master, whereas reconstructing columnar statistics from FlatGeobuf is a full rewrite.

Before committing either choice at scale, model the retrieval side — request counts, egress, and restore fees — with spatial archive cost modeling, because for cold data the retrieval bill, not the storage bill, usually decides the winner.

Cost of Keeping Both

Most mature archives stop treating this as an either-or and keep a columnar archive of record plus a delivery copy. That is a real cost, and it is smaller than teams expect once the storage classes are chosen deliberately.

Monthly cost of one copy, two copies, and convert-on-demand Three options for a 40 terabyte vector archive: a single archival copy at 160 dollars a month, a second delivery copy adding 12 dollars, and converting on demand which saves storage but adds 340 dollars a month in restore and compute. 40 TB vector archive · 6% of layers publicly served · 1,800 map sessions/day GeoParquet only $160/mo storage · no public delivery path + FlatGeobuf delivery copy $172/mo total — the delivery copy adds $12 convert on demand $164 storage + $340 restore and compute = $504/mo, and every session waits for a conversion The delivery copy is cheap because only the served fraction needs one, and it lives in an instant-retrieval class rather than a hot one. Convert-on-demand only wins when public access is rare enough that the restores never happen.

The decisive variable is what fraction of the archive is actually served. Where that fraction is small — and for most institutional archives it is under a tenth — duplicating it costs a rounding error against the archive’s storage line and removes both the restore latency and the conversion compute from every read path. Where nearly everything is served, the delivery copy stops being a copy and becomes the primary, which is a different design with different retention implications.

Validating the Choice on Real Data

Before standardizing a format, benchmark both against a representative sample and measure bytes transferred, not just wall-clock time. Encode the same layer each way, then run each format’s characteristic query directly against object storage:

# GeoParquet: analytical scan touching two columns, projection pushdown
duckdb -c "
  INSTALL httpfs; LOAD httpfs; INSTALL spatial; LOAD spatial;
  SELECT fire_year, sum(burn_area_ha) AS total_ha
  FROM read_parquet('s3://spatial-archive/fire/geoparquet/**/*.parquet')
  GROUP BY fire_year ORDER BY fire_year;"

# FlatGeobuf: bounding-box feature read, R-tree range requests only
ogr2ogr -f GPKG /vsimem/viewport.gpkg \
  /vsis3/spatial-archive/fire/fire_perimeters.fgb \
  -spat -122.6 37.7 -122.3 37.9 -progress

Expected shape of the result — the GeoParquet scan returns an aggregate having read only two column chunks, confirming projection pushdown worked:

┌───────────┬───────────┐
│ fire_year │ total_ha  │
├───────────┼───────────┤
│   2015    │  184203.5 │
│   2016    │  201884.1 │
└───────────┴───────────┘

Compare the bytes each retrieval moved — enable request logging on the bucket — and let the transfer volume, weighted by your real query mix, settle the decision.

Troubleshooting Format Selection

Symptom Cause Fix
GeoParquet bbox reads scan far more than expected Row groups are not spatially clustered, so statistics prune poorly Sort by a spatial key and re-write with smaller row groups before archiving
FlatGeobuf analytical scan transfers the whole file Row-oriented layout has no column projection Move analytics to a GeoParquet copy; keep FlatGeobuf only for bbox reads
GeoParquet retrieval needs a running engine users lack Consumers are thin web clients, not query engines Derive a FlatGeobuf tier for direct-from-bucket map serving
Deep-archive bill higher than modeled FlatGeobuf attribute compression trails columnar Store the preservation master as GeoParquet; treat FlatGeobuf as a derived access copy

Operational Execution Checklist

Frequently Asked Questions

Which format should be the archive of record?

GeoParquet, in nearly every case where the archive supports analysis as well as delivery. It compresses better, supports column projection and attribute pruning, carries its reference system and covering metadata in a structured block, and is read by every engine an analyst is likely to use. FlatGeobuf’s advantages are all on the delivery path, and a delivery copy can be regenerated from the archive of record whereas the reverse loses attributes and structure.

Does either format handle raster or point-cloud data?

Neither. Both are vector feature formats. Raster archives belong in Cloud-Optimised GeoTIFF, which supports the same ranged-read access pattern through internal tiling and overviews, and point clouds in COPC or LAZ. An archive covering all three keeps the access pattern consistent — range-readable, self-describing, no server — and varies the format by data type rather than forcing one format to cover everything.

How do the two compare on long-term readability?

Both are open specifications with multiple independent implementations, which is the property that matters for a multi-decade archive. Parquet has the broader base by a wide margin, being a general data-processing format with a large ecosystem beyond the spatial world, and GeoParquet is a thin, well-documented convention on top of it. FlatGeobuf is a simpler format, which cuts the other way: it is easier to write a reader for from the specification alone.

Can one file serve both purposes?

Only by accepting the weaker option on one axis. A GeoParquet file can be read over ranged requests by a client that understands Parquet footers, and there are browser implementations that do — but they are heavier than a FlatGeobuf reader and the projection benefit disappears if the client wants whole features anyway. If a single format must serve both, choose by whichever workload has the tighter constraint: storage cost and analysis favour GeoParquet, first-paint latency in a browser favours FlatGeobuf.

How does the choice interact with the storage class?

Strongly, and in opposite directions. FlatGeobuf’s advantage is ranged reads, which require a class that serves ranges without a restore — so a delivery copy has to sit in standard or instant-retrieval storage, where per-gigabyte costs are highest. GeoParquet’s advantages survive a restore intact, so the archive of record can live in the deepest, cheapest class the retention policy allows and still prune effectively once restored. That asymmetry is usually what decides the split: the served fraction goes to instant retrieval as FlatGeobuf, the rest goes deep as GeoParquet.

Which format is easier to validate at scale?

GeoParquet, because its footer carries a structured, machine-readable description of the file that can be fetched without reading the data. Validating a FlatGeobuf file means reading its header and, for anything beyond header fields, walking the index or the features. For an archive running continuous integrity checks over millions of objects, that difference in validation cost is significant.

What does a migration between the two formats cost?

Very little in either direction for the data itself, because both are lossless with respect to geometry and attributes — a conversion is a read, a re-encode and a write, with no decisions to make beyond ordering and compression. The cost is in what surrounds it: regenerating indexes, republishing the delivery manifest, invalidating caches, and updating whatever catalogue entries point at the old files. Budget the conversion as a fraction of a day per terabyte of compute and the surrounding work as the larger half.

Is there a third option worth considering?

For vector archives specifically, PMTiles is worth knowing about as a delivery format: it packs pre-rendered or pre-clipped tiles into a single range-readable archive, which suits map display better than either format here but discards the features themselves. It answers a different question — how to serve a basemap — rather than how to preserve and query a feature collection, so it complements a GeoParquet archive of record rather than competing with it.

Part of the Spatial Data Archival knowledge base.