A collection of datasets from various Dutch institutions to demonstrate a Spatial Data Infrastructure built on Portolan.
# 3D BAG — TU Delft / Netherlands
## What This Dataset Is
All ~10.8 million buildings in the Netherlands as 3D models from the
[3D BAG](https://3dbag.nl/), produced by the
[3D Geoinformation Research Group](https://3d.bk.tudelft.nl/) at TU Delft
and [3DGI](https://3dgi.xyz/).
The 3D BAG enriches the official [BAG building registry](https://www.pdok.nl/introductie/-/article/basisregistratie-adressen-en-gebouwen-ba-1)
(maintained by [Kadaster](https://www.kadaster.nl/)) with building heights,
roof types, volumes, and surface areas derived from the
[AHN](https://www.ahn.nl/) national LiDAR point cloud.
The source BAG footprints (without heights) are available in this catalog as
[Buildings (Panden)](../../kadaster/panden/) from Kadaster.
**Provider:** 3D Geoinformation Research Group, TU Delft
**License:** CC BY 4.0 (attribution required)
**Source:** https://3dbag.nl/
**Documentation:** https://docs.3dbag.nl/en/
## Files
Three files, two schemas:
### LOD 1.3 Building Parts (14.1M rows)
Available as GeoParquet (`3dbag-lod13.parquet`, 747 MB, EPSG:7415) and
PMTiles (`3dbag-lod13.pmtiles`, 1.2 GB, EPSG:4326). Same data, different
formats. Buildings are split into parts where roof heights differ.
### Whole Buildings (10.8M rows)
`3dbag-pand.parquet` (1.3 GB, EPSG:7415). One row per building with 62
attributes including volumes, surface areas, floor counts, and quality metrics.
Join to LOD 1.3 parts on `identificatie`.
## How to Access
### DuckDB
```sql
INSTALL spatial; LOAD spatial;
-- LOD 1.3 building parts with heights
SELECT identificatie, height, base_height, dak_type, bouwjaar, gebruiksdoel
FROM read_parquet('https://data.source.coop/cholmes/portolan-nl/tudelft/3dbag/3dbag-lod13.parquet')
LIMIT 5;
-- Whole buildings with full 3D attributes
SELECT identificatie, b3_volume_lod13, b3_opp_grond, b3_bouwlagen, b3_dak_type
FROM read_parquet('https://data.source.coop/cholmes/portolan-nl/tudelft/3dbag/3dbag-pand.parquet')
LIMIT 5;
```
Files are 747 MB and 1.3 GB. DuckDB streams via HTTP range requests.
### Python
```python
import geopandas as gpd
# LOD 1.3 building parts
gdf = gpd.read_parquet(
'https://data.source.coop/cholmes/portolan-nl/tudelft/3dbag/3dbag-lod13.parquet',
columns=['identificatie', 'height', 'base_height', 'dak_type', 'bouwjaar', 'geom']
)
```
## Schema — LOD 1.3 Building Parts
| Field | Type | Meaning |
|-------|------|---------|
| `identificatie` | string | BAG building ID (16 chars). First 4 digits = municipality code. Links to Kadaster BAG. |
| `b3_pand_deel_id` | int64 | Building-part ID within a building. |
| `b3_dd_id` | int64 | Sub-part ID within a building part. |
| `height` | double | Building part height in meters (roof 70th percentile minus ground). Use as fill-extrusion-height. |
| `base_height` | double | Base elevation relative to ground in meters. Use as fill-extrusion-base for stepped parts. |
| `dak_type` | string | Roof type: `horizontal`, `slanted`, `multiple horizontal`, or `unknown`. |
| `bouwjaar` | int64 | Construction year from BAG. 9999 = unknown. |
| `status` | string | Building lifecycle status (e.g. `Pand in gebruik`). |
| `gebruiksdoel` | string | Usage function (e.g. `woonfunctie`, `kantoorfunctie`). Comma-separated for mixed use. NULL = no dwelling units. |
| `oppervlakte_min` | int64 | Minimum floor area (m²) of dwelling units. |
| `oppervlakte_max` | int64 | Maximum floor area (m²) of dwelling units. |
| `aantal_verblijfsobjecten` | int64 | Number of dwelling units. |
## Important Columns
For visualization:
- **`height`** — building part height in meters (for 3D extrusion)
- **`base_height`** — base elevation for stepped building parts
- **`dak_type`** — roof type classification
For analysis (join from 3dbag-pand.parquet on `identificatie`):
- **`b3_volume_lod13`** — building volume in m³
- **`b3_opp_grond`** — ground floor area in m²
- **`b3_bouwlagen`** — estimated floor count
- **`b3_opp_buitenmuur`** — exterior wall area in m²
- **`b3_opp_dak_plat` / `b3_opp_dak_schuin`** — flat/sloped roof areas
BAG attributes (shared with Kadaster BAG):
- **`identificatie`** — BAG building ID (the linking key)
- **`bouwjaar`** — construction year
- **`gebruiksdoel`** — building usage function
- **`status`** — building lifecycle status
## Geometry Notes
- LOD 1.3 GeoParquet CRS is **EPSG:7415** (Amersfoort / RD New + NAP height) —
coordinates in meters. This is a compound 2D+vertical CRS.
- Geometries are **Polygon Z** (3D polygons with Z coordinates).
- PMTiles CRS is **EPSG:4326** (WGS84) for web map display.
- Bounding box (WGS84): [3.36, 50.75, 7.23, 53.50]
- LOD 1.3 has ~14.1M building parts for ~10.8M buildings — one building may
have multiple parts where roof heights step up or down.
## 3D Extrusion Rendering
The `height` and `base_height` fields are pre-computed for direct use with
MapLibre GL / Mapbox GL fill-extrusion layers:
```javascript
map.addLayer({
id: 'buildings-3d',
type: 'fill-extrusion',
source: '3dbag',
'source-layer': 'buildings',
paint: {
'fill-extrusion-height': ['get', 'height'],
'fill-extrusion-base': ['get', 'base_height'],
'fill-extrusion-color': '#D4D4D4',
'fill-extrusion-opacity': 0.85
}
});
```
The `base_height` creates stepped rooflines — a church nave and bell tower
render at different heights even though they share one `identificatie`.
## Useful Query Patterns
### Tallest buildings in the Netherlands
```sql
SELECT identificatie, height, bouwjaar, gebruiksdoel, dak_type
FROM read_parquet('https://data.source.coop/cholmes/portolan-nl/tudelft/3dbag/3dbag-lod13.parquet')
WHERE height > 50
ORDER BY height DESC
LIMIT 20
```
### Buildings by roof type
```sql
SELECT dak_type, COUNT(*) as cnt
FROM read_parquet('https://data.source.coop/cholmes/portolan-nl/tudelft/3dbag/3dbag-lod13.parquet')
GROUP BY dak_type
ORDER BY cnt DESC
```
### Height distribution
```sql
SELECT
CASE
WHEN height < 5 THEN '0-5m'
WHEN height < 10 THEN '5-10m'
WHEN height < 20 THEN '10-20m'
WHEN height < 50 THEN '20-50m'
ELSE '50m+'
END AS height_range,
COUNT(*) AS building_parts
FROM read_parquet('https://data.source.coop/cholmes/portolan-nl/tudelft/3dbag/3dbag-lod13.parquet')
GROUP BY 1
ORDER BY 1
```
### Join LOD 1.3 parts with whole-building attributes
```sql
SELECT
p.identificatie,
l.height,
p.b3_volume_lod13,
p.b3_opp_grond,
p.b3_bouwlagen,
p.b3_dak_type
FROM read_parquet('https://data.source.coop/cholmes/portolan-nl/tudelft/3dbag/3dbag-lod13.parquet') l
JOIN read_parquet('https://data.source.coop/cholmes/portolan-nl/tudelft/3dbag/3dbag-pand.parquet') p
ON l.identificatie = p.identificatie
WHERE p.b3_volume_lod13 > 50000
ORDER BY p.b3_volume_lod13 DESC
LIMIT 20
```
### Largest buildings by volume
```sql
SELECT identificatie, oorspronkelijkbouwjaar, b3_volume_lod13,
b3_opp_grond, b3_bouwlagen, b3_dak_type, status
FROM read_parquet('https://data.source.coop/cholmes/portolan-nl/tudelft/3dbag/3dbag-pand.parquet')
WHERE b3_volume_lod13 IS NOT NULL
ORDER BY b3_volume_lod13 DESC
LIMIT 20
```
### Solar potential — buildings with large flat roofs
```sql
SELECT identificatie, b3_opp_dak_plat, b3_opp_dak_schuin,
b3_dak_type, oorspronkelijkbouwjaar
FROM read_parquet('https://data.source.coop/cholmes/portolan-nl/tudelft/3dbag/3dbag-pand.parquet')
WHERE b3_dak_type = 'horizontal'
AND b3_opp_dak_plat > 500
ORDER BY b3_opp_dak_plat DESC
LIMIT 20
```
## Caveats
- **LOD 1.3 building parts ≠ buildings**: One building may have multiple parts
(rows). Don't count rows to get building count. Group by `identificatie`.
- **CRS is EPSG:7415**: The GeoParquet uses Dutch national coordinates
(meters), not degrees. Transform for web maps:
`ST_Transform(geom, 'EPSG:7415', 'EPSG:4326')`
- **Height = relative**: The `height` field in LOD 1.3 is relative to ground
(ready for extrusion). Absolute heights (NAP datum) are in 3dbag-pand.parquet
(`b3_h_maaiveld`, `b3_h_nok`).
- **NULL heights**: Some buildings lack AHN coverage; their height values may
be NULL or unreliable. Check `b3_kwaliteitsindicator` in the pand file.
- **Mixed-use encoding**: `gebruiksdoel` is comma-separated (same as BAG).
Use `LIKE '%woonfunctie%'` for all residential.
- **PMTiles zoom**: At low zoom levels, features are dropped for performance.
Full coverage at zoom 14-15.
## Related Data in This Catalog
- [Buildings (Panden)](../../kadaster/panden/) — Source BAG building footprints
from Kadaster (2D only, no heights). Smaller and simpler if heights are not
needed.