Skip to content

Open data — CBS & PDOK

The Netherlands publishes a wealth of geospatial open data. Two platforms cover most use cases:

Platform What it provides
PDOK Authoritative Dutch geo-datasets via WFS/WMS — administrative boundaries (CBS gebiedsindelingen), buildings (BAG), topographic maps (BRT/BGT), and more
CBS StatLine Statistics Netherlands open data — population, housing, economy — accessible via a REST API

All examples below require only geopandas and requests, both available in any standard Python environment with mapyta installed.


Provinciekaart

The PDOK CBS gebiedsindelingen service provides administrative boundary layers directly as GeoJSON. Fetching all 12 provinces takes a single call.

Make this Notebook Trusted to load map: File -> Trust Notebook

import requests
import geopandas as gpd
from mapyta import Map

url = "https://service.pdok.nl/cbs/gebiedsindelingen/2024/wfs/v1_0"
r = requests.get(url, params={
    "service": "WFS",
    "version": "2.0.0",
    "request": "GetFeature",
    "typeNames": "gebiedsindelingen:provincie_gegeneraliseerd",
    "outputFormat": "application/json",
}, timeout=30)

gdf = gpd.GeoDataFrame.from_features(r.json()["features"], crs="EPSG:4326")

m = Map(title="Nederlandse provincies")
m = Map.from_geodataframe(
    gdf,
    hover_columns=["statnaam"],
    label_column="statnaam",
)
m.to_html("provincies.html")

The layer returns the following properties per feature:

Column Description
statcode CBS statistic code (e.g. PV20)
statnaam Province name
jrstatcode Year + statcode
rubriek Feature category

Bevolkingsdichtheid per gemeente

CBS StatLine provides kerncijfers (key figures) per municipality. By joining these with PDOK municipality boundaries you can build a choropleth in a few lines.

Make this Notebook Trusted to load map: File -> Trust Notebook

import json
import requests
import pandas as pd
import geopandas as gpd
from mapyta import Map

# 1. Municipality boundaries from PDOK
url = "https://service.pdok.nl/cbs/gebiedsindelingen/2024/wfs/v1_0"
r = requests.get(url, params={
    "service": "WFS",
    "version": "2.0.0",
    "request": "GetFeature",
    "typeNames": "gebiedsindelingen:gemeente_gegeneraliseerd",
    "outputFormat": "application/json",
}, timeout=30)
gdf = gpd.GeoDataFrame.from_features(r.json()["features"], crs="EPSG:4326")

# 2. Population density per municipality from CBS StatLine (table 70072ned)
r2 = requests.get(
    "https://opendata.cbs.nl/ODataApi/odata/70072ned/TypedDataSet",
    params={
        "$filter": "startswith(RegioS,'GM') and Perioden eq '2024JJ00'",
        "$select": "RegioS,Bevolkingsdichtheid_57,TotaleBevolking_1",
    },
    timeout=30,
)
stats = pd.DataFrame(r2.json()["value"])
stats["statcode"] = stats["RegioS"].str.strip()  # remove CBS padding spaces

# 3. Join and map
gdf = gdf.merge(
    stats[["statcode", "Bevolkingsdichtheid_57", "TotaleBevolking_1"]],
    on="statcode",
    how="left",
)
gdf = gdf.rename(columns={
    "Bevolkingsdichtheid_57": "inw_per_km2",
    "TotaleBevolking_1": "inwoners",
})

m = Map.from_geodataframe(
    gdf,
    color_column="inw_per_km2",
    hover_columns=["statnaam", "inw_per_km2", "inwoners"],
    title="Bevolkingsdichtheid per gemeente (2024)",
)
m.to_html("bevolkingsdichtheid.html")

CBS table IDs

CBS table 70072ned covers Kerncijfers wijken en buurten (updated annually). The column Bevolkingsdichtheid_57 is inhabitants per km². Browse all available tables at opendata.cbs.nl.

Other useful CBS columns

The same table contains hundreds of variables. A few useful ones:

Column Description
TotaleBevolking_1 Total population
Bevolkingsdichtheid_57 Inhabitants per km²
GemiddeldeWOZWaardeVanWoningen_98 Average WOZ property value (€)
TotaalBanen_116 Total jobs
Werkloosheid_159 Unemployment benefit recipients
AfstandTotTreinstation_238 Average distance to train station (km)

Gebouwleeftijd in Amsterdam (BAG)

The PDOK BAG (Basisregistratie Adressen en Gebouwen) WFS provides building footprints and metadata for every registered building in the Netherlands. Because the dataset is large, always filter by a bounding box.

Make this Notebook Trusted to load map: File -> Trust Notebook

import requests
import geopandas as gpd
from mapyta import Map

# Grachtengordel Amsterdam — RD New bounding box (EPSG:28992)
# Use srsName=EPSG:4326 so the returned geometries are already in WGS84
url = "https://service.pdok.nl/lv/bag/wfs/v2_0"
r = requests.get(url, params={
    "service": "WFS",
    "version": "2.0.0",
    "request": "GetFeature",
    "typeName": "bag:pand",
    "outputFormat": "application/json",
    "bbox": "119500,487000,121500,488500,EPSG:28992",
    "srsName": "EPSG:4326",
    "count": "500",
}, timeout=30)

gdf = gpd.GeoDataFrame.from_features(r.json()["features"], crs="EPSG:4326")

m = Map.from_geodataframe(
    gdf,
    color_column="bouwjaar",
    hover_columns=["bouwjaar", "gebruiksdoel", "status", "aantal_verblijfsobjecten"],
    title="Gebouwleeftijd — Amsterdam Grachtengordel",
)
m.to_html("bag_bouwjaar.html")

RD New bounding boxes

The BAG WFS expects bounding boxes in RD New (EPSG:28992). Use epsg.io/transform to convert WGS84 coordinates. If you omit srsName, the returned geometries will also be in RD New — mapyta detects this automatically and reprojects them.

BAG feature properties

Column Description
identificatie Unique BAG building ID
bouwjaar Construction year
status E.g. Pand in gebruik, Pand gesloopt
gebruiksdoel Primary use: woonfunctie, kantoorfunctie, etc.
oppervlakte_min / oppervlakte_max Floor area range (m²)
aantal_verblijfsobjecten Number of address units in the building

Other BAG layers

The BAG WFS exposes more than just buildings:

Layer Description
bag:pand Building footprints
bag:verblijfsobject Individual address objects (flats, offices, …)
bag:woonplaats Place name boundaries
bag:standplaats Permanent standplaatsen (caravan sites, etc.)
bag:ligplaats Mooring locations for houseboats

Other useful PDOK datasets

PDOK hosts dozens of datasets beyond CBS and BAG. A selection relevant for mapping:

Dataset WFS base URL Use case
BGT (topography) https://service.pdok.nl/brt/achtergrondkaart/wmts/v2_0 Detailed topographic features
BRO (subsurface) https://publiek.broservices.nl/sr/cpt/v2/wfs Cone penetration test locations
NWB wegen https://service.pdok.nl/rws/nwb-wegen/wfs/v1_0 Road network
Bestemmingsplannen https://service.pdok.nl/rvo/bestemmingsplannen/wfs/v1_0 Zoning plans
CBS wijken & buurten Same gebiedsindelingen URL, typeNames=gebiedsindelingen:buurt_gegeneraliseerd Neighbourhood boundaries