GeoParquet — Columnar Format for Big-Data GIS: History, OGC Status, DuckDB and OSM Export

2026-04-087 min read
GeoParquetParquetcolumnarbig-dataOGCanalytics

GeoParquet is Apache Parquet with a geo extension: a columnar binary format where geometries are stored as WKB in a dedicated column and spatial metadata lives in the Parquet schema. The 1.0.0 specification shipped on September 27, 2023 in the opengeospatial/geoparquet repository; version 1.1 followed in September 2024, adding bbox covering for predicate push-down. Since December 2024 the OGC Standards Working Group has formally promoted GeoParquet to an OGC Community Standard — a public, non-paid status distinct from a full OGC standard. The main use case is analytics over cloud object storage via DuckDB Spatial, AWS Athena, Google BigQuery, Snowflake, and Apache Spark + Sedona. On petabyte-scale datasets like Overture Maps Foundation, columnar storage delivers 10–100x query speedups with predicate push-down compared to Shapefile or GeoJSON.

Map generation of Nizhny Novgorod city center

History: from Apache Parquet to OGC Community Standard

Apache Parquet emerged in March 2013 from a joint Twitter + Cloudera effort as a columnar format for the Hadoop ecosystem. The idea is straightforward: data within a column is uniform, so compression ratios climb (typically 5–15x vs CSV), and analytical queries like "sum of column X where Y > 100" scan only the needed columns rather than full rows. Snappy, Gzip, LZ4, and Zstd are the four standard codecs.

GeoParquet grew out of a Cloud-Native Geospatial Foundation (formerly Radiant Earth) initiative and the Planet Labs community. Chris Holmes, the cloud-native-geo evangelist, formally kicked off the project at State of the Map US 2020. By August 2023 the opengeospatial/geoparquet repository published the 1.0.0 specification; by September 2024, version 1.1 brought two key additions: optional GeoArrow encoding as a WKB alternative, and bbox covering — a dedicated bounding box column per feature that enables predicate push-down without WKB decoding.

By 2026 GeoParquet is the standard for publishing major open datasets: Overture Maps Foundation (TomTom + Microsoft + Meta + AWS), Microsoft Building Footprints (every populated area on Earth), Google Open Buildings, Source Cooperative from Radiant Earth — all of them ship GeoParquet first. DuckDB Spatial, which launched in April 2023 as "PostGEESE? Introducing The DuckDB Spatial Extension", became the reference engine for GeoParquet analytics in Jupyter notebooks.

What's inside a GeoParquet file

A GeoParquet file is a plain Parquet file with two extra conventions. First: one or more columns hold geometry in Well-Known Binary (WKB) — the standard OGC encoding read by every GIS engine. Second: the Parquet schema metadata under the geo key (a JSON string) describes which column is geometric, which CRSes apply (default is OGC:CRS84 — WGS 84 with lon-lat ordering), the geometry_type, the bbox, and the encoding conventions.

Physically Parquet is hierarchical. At the top — the file; below it — row groups (typically 100K–1M rows); inside row groups — column chunks (one column for one row group); inside column chunks — pages (the smallest read unit, normally 1 MB). Each row group stores min/max statistics per column — this is the foundation of predicate push-down: to find all buildings in Berlin, the engine looks at which row groups have a bbox overlap with Berlin and skips the rest without reading data.

GeoParquet 1.1 added bbox covering: dedicated bbox.xmin/ymin/xmax/ymax columns alongside the geometry column, with min/max statistics tracked at the row group level. This lets WHERE ST_Intersects(geometry, bbox) filter without decoding WKB — DuckDB reads only the bbox columns, drops 99% of row groups, then reads WKB only from the remaining 1%.

Mermaid diagram: flowchart TD

Map generation of Prague city center

Strengths and weaknesses

Strengths. Columnar storage — 5–15x smaller than CSV and 2–3x smaller than GeoJSON on disk. Predicate push-down via row-group statistics accelerates analytics 10–100x on large datasets. Compatibility with the big-data ecosystem: DuckDB, Spark, Athena, BigQuery, Snowflake, Dask, Polars, GeoPandas. Hive partitioning — a dataset can be split into thousands of small files by geohash or admin boundary, and DuckDB/Spark read only the matching ones. From v1.1 — bbox covering for efficient spatial filtering without WKB decoding.

Weaknesses. Not designed for editing — analytical store, write-once. No built-in R-tree (only bbox covering, which is weaker than R-tree for complex spatial filtering). Not browser-readable without libraries — needs parquet-wasm or DuckDB-Wasm. A young standard (1.0 in 2023, 1.1 in 2024) — older QGIS, ArcGIS, FME may not support it. For small files (< 10 MB) the Parquet overhead doesn't pay off — pick FlatGeobuf or GeoJSON instead.

GeoParquet vs alternatives

FlatGeobuf wins on streaming and HTTP range requests; GeoParquet wins on analytics via DuckDB and Spark. GeoPackage for transactional editing and QGIS projects; GeoParquet for read-only datasets. Shapefile loses on every dimension. GeoJSON for web frontends, not big data. GeoTIFF for rasters, not vectors.

Typical use cases

Data scientist in Jupyter. Opens a 50-million-POI GeoParquet via import duckdb; con = duckdb.connect(); con.sql("SELECT category, COUNT(*) FROM 'pois.parquet' WHERE ST_Intersects(geometry, ST_GeomFromText('POLYGON(...)')) GROUP BY category"). The query on 50M rows runs in 2 seconds — DuckDB reads only the category column and filters via bbox covering.

Cloud architect on AWS. Puts an OSM Germany extract (GeoParquet, 6 GB) into S3, registers it in AWS Glue Data Catalog, runs Athena with SELECT * FROM germany WHERE ST_Within(...). Query cost — cents (Athena bills bytes scanned; push-down skips 95% of data).

GIS analyst in QGIS 3.30+. Opens a GeoParquet of Moscow buildings directly via File → Add Layer → Add Vector Layer. QGIS uses the built-in GDAL Parquet driver and shows only the visible part of the map via bbox covering — no full file read.

Cloud-native data publisher. Radiant Earth / Overture Maps / Microsoft Building Footprints publish datasets as partitioned GeoParquet in an open S3 bucket. Clients connect via DuckDB straight from the browser (DuckDB-Wasm) or from Jupyter — no full-file download required.

Pipeline in OSM2CDR

Our geoparquet_exporter.py uses QGIS QgsVectorFileWriter with the GDAL Parquet driver, baking a GeoParquet 1.0-compatible file. Input: OSM layers (buildings, roads, POIs, water, green areas) from PostGIS via WKB fast-path. Output: a single .parquet file with embedded geo metadata and a WKB geometry column.

Default compression is Snappy (fast decoding, moderate ratio). For archival dumps you can pick Zstd via query parameter — another 20–30% size reduction at the cost of slower decoding. Row group size is 100K rows (GDAL default), optimal for bbox covering filtering.

CRS is exported as an EPSG code in the geo JSON metadata. For Russian projects the list offers 122 Gauss-Kruger zones keyed to MSK numbers plus 43 popular global systems — an approximation, not the true MSK of a subject; the parameters of 222 real zones across 75 MSK numbers, each with a named source and confidence class, are computed by the coordinate converter.

Map generation of Vladivostok city center

FAQ

Is it an OGC standard? Since December 2024 — an OGC Community Standard. That's not the same as a full OGC standard (like GeoPackage 1.3 or GeoTIFF 1.1) — Community Standard follows a lighter approval process but still gets officially published by OGC with a registration number.

Can GeoParquet be edited incrementally? No — Parquet is write-once. To add rows, rewrite the file or use partitioning plus deletion/addition of individual partition files.

What's the difference between WKB and GeoArrow in GeoParquet 1.1? WKB is the standard OGC encoding, universally read. GeoArrow is an experimental nested encoding where coordinates live in a struct column {x: double, y: double}. GeoArrow is faster for compute-heavy operations (DuckDB Spatial with GeoArrow runs 3–5x faster than with WKB), but not every engine supports it yet. WKB is the default for compatibility.

How do I partition a large dataset? Via Hive partitioning: output/category=building/region=de/file_001.parquet. DuckDB and Spark automatically prune partition files that don't match the WHERE predicate.

What's the optimal number of rows per row group? Standard is 100K–1M. Fewer means more metadata overhead; more means weaker predicate push-down. GDAL's default of 100K works for most geo datasets.

Sources

← All articles