3DCityDB v5 is an open-source database designed for storing and managing semantic 3D city models based on the CityGML 3.0 standard. It runs on PostgreSQL with PostGIS and organizes city data into about 17 core tables that handle geometry, metadata, appearances, and relationships.
This guide outlines how to install 3DCityDB v5, import CityGML data, export it as 3D Tiles, and visualize the results.
See also https://tum-gis.github.io/citydb-3dtiler/ for support creating 3D Tiles using pg2b3dm in 3DCityDB.
3DCityDB v5 can be quickly deployed using Docker:
docker run -d -p 5440:5432 -it -e POSTGRES_PASSWORD=postgres -e PROJ_NETWORK=ON -e SRID=7415 3dcitydb/3dcitydb-pg
Explanation:
-p 5440:5432 — maps host port to container portPOSTGRES_PASSWORD — sets the database passwordPROJ_NETWORK=ON — enables coordinate transformation downloadsSRID=7415 — specifies the coordinate reference system (composite projection Amersfoort / RD New with NAP reference)After launching, the database schema is automatically created with tables for features (aka. city objects), geometry data, attributes, and appearances.
Download a sample CityGML file, such as the Den Haag Archipelbuurt 3D model (45MB) from https://ckan.dataplatform.nl/dataset/3d-stadsmodel-den-haag-2021-citygml/resource/be8d3a16-50f3-415f-a8bd-55d24c9d8cdc.
Install command line tool ‘citydb’ - see https://github.com/3dcitydb/citydb-tool
Example command for importing a CityGML file:
citydb import citygml -H localhost -d postgres -u postgres -p postgres --db-port 5440 den_haag_3d_archipelbuurt.gml
In a PostgreSQL client (like PGAdmin/ DBeaver/psql) open database connection to localhost, port 5440, user postgres.
psql -h localhost -p 5440 -U postgres -d postgres
In the PostgreSQL client create a spatial index:
CREATE INDEX ON citydb.geometry_data USING gist(st_centroid(st_envelope(geometry)))
Notes:
ST_PolyhedralSurface or ST_MultiPolygon in the geometry_data table.Once the data is imported, convert the data into 3D Tiles using the pg2b3dm tool:
pg2b3dm --connection "Host=localhost;Port=5440;Username=postgres;Database=postgres;CommandTimeOut=0" -t citydb.geometry_data -c geometry --attributecolumns geometry_properties
Result:
tileset.json file describing the dataset’s structure and bounding volumesThe resulting 3D Tiles can be viewed in any Cesium-compatible viewer -make sure to load the buildings tileset and a terrain.
When loaded, Cesium displays buildings and terrain data.
Optional styling can be added to control building colors, materials, or feature visibility.

To enhance the visual appearance of the 3D Tiles, we can apply shaders when generating the tiles. For example, use different colors per CityGML class (like RoofSurface, WallSurface, OuterfloorSurface).
See materials_for_features.csv for an example mapping of CityGML feature classes to material colors.
Import the materials mapping into the database:
CREATE TABLE citydb.materials_for_features (
namespace_of_classname TEXT,
classname TEXT,
pbr_metallic_roughness_base_color TEXT,
pbr_metallic_roughness_metallic_roughness TEXT
);
Copy the csv to the database:
\copy materials_for_features FROM 'materials_for_features.csv' WITH (FORMAT csv, HEADER true);
Create a view containing the geometry data along with the corresponding material colors:
CREATE OR REPLACE VIEW citydb.geoms4tiles
AS
WITH material_data_cte AS (
SELECT
mtf.namespace_of_classname,
mtf.classname,
JSON_OBJECT(
'PbrMetallicRoughness' : JSON_OBJECT(
'BaseColors' :
NULLIF(
ARRAY[mtf.pbr_metallic_roughness_base_color],
'{NULL}'::text[]
),
'MetallicRoughness' :
NULLIF(
ARRAY[mtf.pbr_metallic_roughness_metallic_roughness],
'{NULL}'::text[]
)
ABSENT ON NULL
RETURNING json
)
ABSENT ON NULL
RETURNING json
) AS material_data
FROM materials_for_features mtf
)
SELECT
ftr.objectid,
obcl.classname,
pbr.material_data,
geometry AS geom
FROM geometry_data gmdt
LEFT JOIN feature ftr ON ftr.id = gmdt.feature_id
LEFT JOIN objectclass obcl ON obcl.id = ftr.objectclass_id
LEFT JOIN namespace ns ON ns.id = obcl.namespace_id
LEFT JOIN material_data_cte pbr ON pbr.namespace_of_classname = ns.alias AND pbr.classname = obcl.classname
where ftr.objectid NOT LIKE 'bag%';
Sample resulting JSON in column geoms4tiles.material_data:
{"PbrMetallicRoughness" : {"BaseColors" : ["#FFFF00"]}}
Notes:
The view filters out building parts from the Dutch BAG dataset (ftr.objectid NOT LIKE ‘bag%’) to avoid visual clutter.
In some cases, the input CityGML file contains ‘intersection curves’. These intersection curves are stored as linestrings in the geometry_data table and visualized as a 3D pipes using pg2b3dm. A recommended filter to add to the view could be “st_geometrytype(geometry) != ‘Linestring’”.
Now, generate the 3D Tiles using the new view - adding the shaderscolumn parameter:
pg2b3dm --connection "Host=localhost;Port=5440;Username=postgres;Database=postgres;CommandTimeOut=0" -t citydb.geoms4tiles -c geom --shaderscolumn material_data -a objectid,classname
Result: The exported 3D Tiles will now have different colors based on the CityGML feature classes, enhancing the visualization quality.

For 3DCityDB v5 input (citydb.geometry_data or a view carrying geometry_data.id), pg2b3dm now automatically detects
texture data and applies textures per tile. Note: Executing texture compression is out of scope for pg2b3dm.
Relevant texture tables and columns:
citydb.surface_data_mapping.texture_mapping (texture coordinates)citydb.surface_data.tex_image_idcitydb.tex_image.image_data (image bytes)Example query:
SELECT
gmdt.id,
gmdt.geometry,
gmdt.geometry_properties,
sdm.texture_mapping,
ti.image_uri,
ti.image_data
FROM citydb.geometry_data gmdt
JOIN citydb.surface_data_mapping sdm
ON gmdt.id = sdm.geometry_data_id
JOIN citydb.surface_data sd
ON sd.id = sdm.surface_data_id
JOIN citydb.tex_image ti
ON ti.id = sd.tex_image_id
WHERE sdm.texture_mapping IS NOT NULL
AND ti.image_data IS NOT NULL;
Priority rule during export:
Sample World Port Center Rotterdam:
3DCityDB v5 streamlines the process of: