Download first
CityGML and CityJSON are text. A query normally begins by pulling gigabytes of a single region to a local machine.
Format specification · v0.1.0-draft
no database
Before you can ask a question, you have to download, parse, or load the city model into a database.
CityGML and CityJSON are text. A query normally begins by pulling gigabytes of a single region to a local machine.
Even locally, every query parses the whole document into memory before computing anything. Slow and memory-hungry.
3DCityDB works, but you host a server and load the data — and hit cost and scale limits at national size.
Read only the data a question needs, directly from static files — using familiar tools, without running a database server.
Read the columns you need. Skip places you do not. Fetch only the matching parts of the file.
Attributes and levels of detail live in separate columns. A query about building height can read that column without decoding every building's geometry or parsing a whole text file.
Nearby objects are stored together. Bounding-box statistics let a reader skip groups outside the query area — an index-like benefit stored in the file itself, without a database service.
HTTP range requests fetch the required byte ranges from object storage. You can query a published package without downloading the entire dataset first.
The result retains the CityGML semantics: object types, relationships, levels of detail, and semantic surfaces travel with the geometry.
One conceptual model, several encodings. Each writes the same semantics — a wall is a WallSurface — for a different job. CityParquet is the columnar member and replaces none of the others. Compare it with each of them.
GML / XML
JSON text
Relational
Row binaryFiltering, aggregating, or measuring over a large collection in object storage. The query reads the bytes it needs, not the file.
Handing data to another tool or person, or editing it. CityParquet round-trips back, so deliver in one and exchange in the other.
File sizes on disk for Montréal, 31,415 CityObjects, the largest dataset in the benchmark corpus, using Hilbert-ordered CityParquet.
A model carries one geometry column per LoD. A query ongeometry_lod2_2 never reads the LoD 1.2 bytes.
The bbox column feeds per-row-group statistics; Hilbert ordering clusters near rows so more groups can be skipped.
CityParquet combines compact storage with fast selective reads. FlatCityBuf leads the measured spatial and full-read scenarios; CityParquet does not win every workload.
A package is Parquet on object storage, so DuckDB opens it as it stands — read_parquet over HTTPS, no import step, no server, no conversion. Two community extensions carry the parts Parquet has no vocabulary for: cityjson reads the text formats, three_d turns stored WKB back into solids you can measure. The example below uses a stored volume attribute, so it needs no geometry extension.
Reference implementation and CLI: convert, export, compare, bench.
cityjson/cityparquetCommunity extension reading CityJSON and CityJSONSeq into the wide layout.
cityjson/duckdb-cityjsonA package is Parquet, so mature readers open it with no extension at all.
“Give me the total building volume in this area of Delft, out of roughly 11 million buildings across the Netherlands.”
This example selects buildings whose bounding boxes overlap a 1 km × 1 km area, then adds their recorded LoD 2.2 volumes. The coordinates define the area in the dataset’s Dutch national grid. DuckDB can skip unrelated row groups and read just the columns needed.
SELECT ROUND(SUM(b3_volume_lod22)) AS total_volume_m3
FROM read_parquet(
'https://cityparquet.open3d.city/data/3dbag/building.parquet')
WHERE object_type = 'Building'
AND bbox.xmin < 85000 AND bbox.xmax > 84000
AND bbox.ymin < 447000 AND bbox.ymax > 446000;DuckDB is a trademark of the DuckDB Foundation. CityParquet is an independent project and is not affiliated with or endorsed by it.
Ask a question. Let your agent query the city model.
How many buildings in the Netherlands are higher than 100 metres?
Thinking
SELECT COUNT(*) AS buildings_over_100m
FROM read_parquet(
'https://cityparquet.open3d.city/data/3dbag/building.parquet')
WHERE object_type = 'Building'
AND b3_h_dak_max - b3_h_maaiveld > 100;674 buildings are higher than 100 metres in this dataset.
Using roof height above ground. Only building type and height attributes are needed — no 3D geometry to download.
Try the query →Query result checked on 23 September 2026.
What datasets are available to you?
Thinking
3DBAG in the Netherlands, PLATEAU in Japan, and LoD3 buildings in French economic activity zones.
Explore a whole country with 3DBAG, Tokyo and Yokohama with PLATEAU, or economic activity zones in Auvergne-Rhône-Alpes.
Explore the datasets →Examples from the published dataset documentation; availability may change.
How should geometries be encoded in CityParquet?
Thinking
Use little-endian ISO WKB, with a separate geometry column for each level of detail.
For example, geometry_lod2_2 stores the WKB and geometry_properties_lod2_2 carries semantics and shell structure. Coordinates are world-coordinate doubles in the file CRS; a Solid uses PolyhedralSurfaceZ.
Read the geometry specification →Based on the draft CityParquet geometry specification.
Connect your agent with the CityParquet MCP server.See the setup →
Install DuckDB, load two extensions, point a query at a package — or skip all three and query 3DBAG in the browser playground. The draft specification is in the docs.