This tool has originally been forked from Geodan/pg2b3dm but has been modified in order to accommodate the needs of the 3DBAG pipeline. The modifications include multithreading, the use of a custom quadtree (stored in a database) and gzip compression. It is being used for the generation of 3D tiles for the 3DBAG.
Prerequisite: .NET 6.0 SDK should be installed installed
Assuming that the quadtree is available in .tsv format (usually in /data/3DBAG/export_<release_version>/ on gilfoyle) first create the table in the baseregisters DB (drop it if it already exists):
-- DROP TABLE tiles.quadtree;
CREATE TABLE tiles.quadtree(
id varchar(30) NOT NULL,
level int NOT NULL,
nr_items int NOT NULL,
leaf bool NOT NULL,
geom TEXT NOT NULL,
CONSTRAINT id PRIMARY KEY (id));You can import the file by connecting to the baseregisters database from Gilfoyle and running:
\COPY tiles.quadtree
FROM '/data/3DBAG/export_<release_version>/quadtree.tsv'
DELIMITER E'\t'
CSV HEADER;And finally modify the Geometry column:
ALTER TABLE tiles.quadtree
ALTER COLUMN geom TYPE GEOMETRY(POLYGON, 28992)
USING ST_GeomFromText(geom, 28992);On Gilfoyle, gather the paths of the triangulated gpkg files in a single file:
find -L /data/3DBAG/export_<release_version>/tiles/ -path "/data/3DBAG/export_<release_version>/tiles/*tri.gpkg" > all_gpkg.txtIn the baseregisters database first drop the old tiles.gpkg_files table and then import the .gpkg files:
export PG_USE_COPY=TRUE
while read f; do
base_name=$(basename ${f})
echo ${base_name}
names=($(echo ${base_name} | sed s/-/\\n/g))
id=${names[0]}/${names[1]}/${names[2]}
lod=${names[3]: -2}
ogr2ogr -update -append -f "PostgreSQL" PG:"host=localhost user=<USERNAME> dbname=baseregisters password=<PASSWORD>" $f -nlt MULTIPOLYGON25D -nln tiles.gpkg_files -sql """SELECT '$base_name' AS filename, ${names[0]} AS level, '$id' AS tile_id, ${lod} AS lod, * FROM geom""" -gt 65536 -lco SPATIAL_INDEX=NO
done < all_gpkg.txtAfter importing you need to 1) create the attributes column (make sure you include any new attributes), 2) correct the Z dimension of geometries by subtracting the ground height, 3) create the indexes and 4) delete features with dimensions with edge values.
ALTER TABLE tiles.gpkg_files ADD COLUMN attributes text;
UPDATE tiles.gpkg_files SET attributes = ROW_TO_JSON(
(SELECT d
FROM (
SELECT
"identificatie",
"status",
"oorspronkelijkbouwjaar",
"b3_h_maaiveld",
"b3_volume_lod12",
"b3_volume_lod13",
"b3_volume_lod22",
"b3_dak_type",
"b3_pw_datum",
"b3_pw_bron",
"b3_pw_onvoldoende",
"b3_pw_selectie_reden",
"b3_kas_warenhuis",
"b3_val3dity_lod12",
"b3_val3dity_lod13",
"b3_val3dity_lod22",
"b3_rmse_lod12",
"b3_rmse_lod13",
"b3_rmse_lod22",
"b3_mutatie_ahn3_ahn4",
"b3_mutatie_ahn4_ahn5",
"b3_nodata_fractie_ahn3",
"b3_nodata_fractie_ahn4",
"b3_nodata_fractie_ahn5",
"b3_nodata_radius_ahn3",
"b3_nodata_radius_ahn4",
"b3_nodata_radius_ahn5",
"b3_puntdichtheid_ahn3",
"b3_puntdichtheid_ahn4",
"b3_puntdichtheid_ahn5",
"b3_opp_buitenmuur",
"b3_opp_dak_plat",
"b3_opp_dak_schuin",
"b3_opp_grond",
"b3_opp_scheidingsmuur",
"b3_bouwlagen",
"b3_kwaliteitsindicator",
"b3_extrusie",
"b3_is_glas_dak",
"b3_n_vlakken",
"b3_h_nok",
"b3_n_nok",
"b3_t_run"
) d))::text;
UPDATE tiles.gpkg_files SET geom = ST_Translate(geom, 0, 0, "b3_h_maaiveld" * -1.0);
CREATE INDEX IF NOT EXISTS gpkg_files_tile_id_idx
ON tiles.gpkg_files
USING btree(tile_id);
CREATE INDEX IF NOT EXISTS gpkg_files_geom_idx
ON tiles.gpkg_files
USING gist(geom);
DELETE FROM tiles.gpkg_files a
WHERE (st_xmax(a.geom) - st_xmin(a.geom)) > 1500
OR (st_ymax(a.geom) - st_ymin(a.geom)) > 1500
OR (st_zmax(a.geom) - st_zmin(a.geom)) > 1500;This step is performed until we have another mechanism in place which detects and rejects greenhouses before the reconstruction. Greenhouses or other problematic cases which are characterised by glass roofs can be identified with the excessive number of building parts within a single building.
First we identify the buildings with > 10000 building parts (can also be 5000). Then we store them in a separate table tiles.gpkg_files_only_greenhouses and I drop the same buildings from the tiles.gpkg_files table.
CREATE TABLE tiles.gpkg_files_only_greenhouses AS
SELECT identificatie
FROM tiles.gpkg_files
GROUP BY identificatie
HAVING COUNT(*) > 10000;After cloning the pg2b3dm repo on godzilla, you need to activate a tunnel to gilfoyle:
ssh -f -N -M -S /tmp/gilfoyle_postgres -L 5435:localhost:5432 gilfoyleIf you do not have .NET SDK 6.0 installed you can use the following to open a shell that has it:
export NIXPKGS_ALLOW_INSECURE=1
nix-shell -p dotnet-sdk_6Then you can build from within the root of the repo with:
cd pg2b3dm/src/pg2b3dm
dotnet buildCreate the directory /data/3DBAG/<release_version>/3dtiles/ and the lod subdirectories.
Then run this command to create the tiles (make sure you have a .pgpass file with the credentials for the gilfoyle DB):
dotnet run -- -U <USER_NAME> -p 5435 --dbname baseregisters -t 'tiles.gpkg_files' -c 'geom' -i 'ogc_fid' --qttable tiles.quadtree --tileidcolumn tile_id --lodcolumn lod --attributescolumn attributes --skiptilesntriangles 3500000 --passfile ~/.pgpass --maxthreads 30 --compression gzip --disableprogressbar -o /data/3DBAG/<release_version>/3dtiles/ --skiptilesAll parameters are optional, except the -t --table option.
If --username and/or --dbname are not specified the current username is used as default.
-U, --username (Default: username) Database user
-h, --host (Default: localhost) Database host
-d, --dbname (Default: username) Database name
-c, --column (Default: geom) Geometry column name
-i, --idcolumn (Default: id): Identifier column
-t, --table (Required) Database table name, include database schema if needed
-o, --output (Default: ./output/tiles) Output directory, will be created if not exists
-p, --port (Default: 5432) Database port
-r, --roofcolorcolumn (Default: '') color column name
-a, --attributescolumn (Default: '') attributes column name
-e, --extenttile (Default: 1000) Maximum extent per tile
-g, --geometricerrors (Default: 500, 0) Geometric errors
-l, --lodcolumn (default: '') lod column name
--refine (Default: REPLACE) Refinement method (ADD/REPLACE)
--skiptiles (Default: false) Skip creation of existing tiles
--maxthreads (Default: -1) The maximum number of threads to use
--qttable Required. Pre-defined quadtree full table
--leavestable Required. Pre-defined quadtree leaves table
--compression (Default: ) Tiles compression type (gzip)
--passfile (Default: ) Psql passfile path (.pgpass)
--tileidcolumn (Default: tile_id) Tile ID column
--lod (Default: 22) LoD to be extracted
--skiptilesntriangles (Default: 0) Skip tiles with more than n triangles
--disableprogressbar (Default: false) Disable the progress bar
--help Display this help screen.
--version Display version information.