| Title: | Access Open Geodata from the Dutch 'PDOK' Platform |
|---|---|
| Description: | Tools to discover, download, and spatially filter open geographic data from 'PDOK' (Publieke Dienstverlening Op de Kaart), the national geodata platform of the Netherlands. Datasets and their layers are searched and loaded as vector simple feature ('sf') objects through 'OGC' API Features endpoints, with automatic pagination and explicit coordinate reference system handling. Loaded layers can be filtered by any polygon area, and addresses or place names can be geocoded through the 'PDOK' location server. The focus is on vector feature data; raster, tile, and coverage services are out of scope. See <https://www.pdok.nl/> for more information about the platform and its services. |
| Authors: | Coen Eisma [aut, cre, cph] (ORCID: <https://orcid.org/0009-0007-9001-2572>) |
| Maintainer: | Coen Eisma <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.0 |
| Built: | 2026-07-22 12:26:29 UTC |
| Source: | https://github.com/cran/pdokr |
Returns the URL of the official PDOK 'BRT Achtergrondkaart' (or aerial
imagery) for use as the background of a map. Nothing is downloaded — the
function only constructs the URL, so it works offline and instantly. Hand the
result to any mapping package: tmap and leaflet take the raster tile URL,
maplibre/mapgl take the vector style URL.
pdok_basemap(style = "standaard", format = c("raster", "vector"))pdok_basemap(style = "standaard", format = c("raster", "vector"))
style |
The basemap style. For |
format |
|
A single string: a raster tile-URL template, or a vector style URL.
The map data is © Kadaster / PDOK. Show this attribution on any map that uses the basemap.
# Raster tile URL (tmap / leaflet) pdok_basemap() pdok_basemap("grijs") # Vector style URL (maplibre / mapgl) pdok_basemap("standaard", format = "vector")# Raster tile URL (tmap / leaflet) pdok_basemap() pdok_basemap("grijs") # Vector style URL (maplibre / mapgl) pdok_basemap("standaard", format = "vector")
Keeps the features of data that relate to filter_geometry under a spatial
predicate. It does what sf::st_filter() does, and additionally reconciles
coordinate reference systems for you: filter_geometry is transformed to the
CRS of data before filtering.
pdok_filter_by(data, filter_geometry, predicate = "intersects")pdok_filter_by(data, filter_geometry, predicate = "intersects")
data |
An sf object to filter (for example a layer loaded
with |
filter_geometry |
An |
predicate |
The spatial relationship to test, one of |
filter_geometry can be any polygon: a municipality from
pdok_read() on the CBS administrative boundaries, a nature reserve, a
water-authority area, a hand-drawn polygon, or another PDOK layer.
The plain-sf equivalent is data[filter_geometry, , op = sf::st_intersects]
(after matching CRS); use that if you prefer to drop down to sf.
An sf object: the subset of data whose features satisfy
predicate with respect to filter_geometry. A zero-row sf is returned
(silently) when no feature matches.
The predicate selects how a feature must relate to filter_geometry to be
kept. Each value calls the matching sf predicate function sf::st_<predicate>()
(so "within" uses sf::st_within(), "intersects" uses sf::st_intersects(),
and so on). The common choices:
"intersects" (default) — the feature touches filter_geometry in any way
(overlaps, is inside, or shares a boundary). The most permissive; the usual
choice for "everything in this area".
"within" — the feature lies entirely inside filter_geometry. Use this
to exclude features that only stick partly into the area.
"contains" — the feature entirely encloses filter_geometry (the
inverse of "within").
"disjoint" — the feature does not touch filter_geometry at all
(everything outside the area).
"overlaps", "touches", "crosses", "covers" and "covered_by" are the
remaining, more specialized DE-9IM relationships. For the precise definition
of each, see the sf help page sf::geos_binary_pred (the topic that
documents st_intersects(), st_within(), and the rest).
sf::geos_binary_pred for the definition of each spatial predicate;
sf::st_filter(), the sf filter this wraps; and pdok_read(), whose
filter_by argument applies this filter while loading.
# All national parks that intersect the province of Utrecht utrecht <- pdok_read( "cbs/gebiedsindelingen", "provincie_gegeneraliseerd", datetime = 2024 ) utrecht <- utrecht[utrecht$statnaam == "Utrecht", ] parks <- pdok_read("rvo/nationale-parken-geharmoniseerd", "protectedsite") pdok_filter_by(parks, utrecht)# All national parks that intersect the province of Utrecht utrecht <- pdok_read( "cbs/gebiedsindelingen", "provincie_gegeneraliseerd", datetime = 2024 ) utrecht <- utrecht[utrecht$statnaam == "Utrecht", ] parks <- pdok_read("rvo/nationale-parken-geharmoniseerd", "protectedsite") pdok_filter_by(parks, utrecht)
Looks up addresses, place names, postcodes, municipalities, provinces and
more through the 'PDOK' Locatieserver, returning the results as a simple
feature collection. Point geometry is returned for addresses and places, and
boundary polygons for administrative areas such as municipalities — so a
result drops straight into the filter_by argument of pdok_read().
pdok_geocode(query, type = NULL, crs = NULL, limit = 1)pdok_geocode(query, type = NULL, crs = NULL, limit = 1)
query |
A character vector of one or more non-empty search strings, e.g.
|
type |
Optional result type to restrict to, one of |
crs |
Optional output CRS as an EPSG code (e.g. |
limit |
Maximum number of results to return per query (default 1). |
query may be a vector, geocoding many locations in one call (for example a
column of addresses). Each result row carries a query column with the input
it came from, so the output maps back to the input even when a query returns
several candidates or none.
An sf object with one row per match and a query column
identifying the input each row came from. All non-geometry fields the
service returns are kept as columns (with query, weergavenaam, type,
score, and the administrative names first); the geometry is a point for
addresses and places and a polygon for administrative areas. Queries that
match nothing are dropped (with a warning), so the result can have fewer
rows than query has elements; a zero-row sf is returned when nothing
matches at all.
pdok_reverse_geocode() for the reverse lookup (coordinates to the
nearest address), and pdok_read(), whose filter_by argument accepts the
result.
# An address: a point pdok_geocode("Park Arenberg 88, De Bilt") # A municipality: a boundary polygon pdok_geocode("De Bilt", type = "gemeente") # Several addresses in one call; the `query` column maps rows to inputs pdok_geocode(c("Domplein 1, Utrecht", "Coolsingel 40, Rotterdam"))# An address: a point pdok_geocode("Park Arenberg 88, De Bilt") # A municipality: a boundary polygon pdok_geocode("De Bilt", type = "gemeente") # Several addresses in one call; the `query` column maps rows to inputs pdok_geocode(c("Domplein 1, Utrecht", "Coolsingel 40, Rotterdam"))
Returns the full table of contents of datasets offered through the 'PDOK' OGC API Features platform, fetched live from https://api.pdok.nl/index.json.
pdok_list_datasets()pdok_list_datasets()
A tibble with one row per dataset and the columns
id (the identifier passed to pdok_list_layers() and pdok_read()),
name, description, keywords (a list-column of character vectors),
services, owner, and ogc_url.
pdok_search_datasets() to filter this list.
pdok_list_datasets()pdok_list_datasets()
Lists the layers (OGC API Features collections) offered by a dataset.
pdok_list_layers(dataset)pdok_list_layers(dataset)
dataset |
A dataset id from |
A tibble with one row per layer and the columns
dataset (the dataset id, echoing the input so each row works directly
with pdok_read()), layer (the layer identifier), title,
description, start_date and end_date (the temporal extent the layer
covers, as Dates; end_date is NA when the layer is ongoing), crs (a
list-column of available EPSG codes), storage_crs (the EPSG code the data
is stored in), and bbox (a list-column of named numeric extents
c(xmin, ymin, xmax, ymax) in CRS84).
pdok_search_layers() to filter this list,
pdok_list_datasets() for the datasets.
pdok_list_layers("cbs/gebiedsindelingen")pdok_list_layers("cbs/gebiedsindelingen")
Loads a layer from PDOK as a simple feature collection over the OGC API Features service, handling pagination automatically.
pdok_read( dataset, layer, bbox = NULL, filter_by = NULL, predicate = "intersects", datetime = NULL, crs = NULL, max_features = NULL )pdok_read( dataset, layer, bbox = NULL, filter_by = NULL, predicate = "intersects", datetime = NULL, crs = NULL, max_features = NULL )
dataset |
A dataset id from |
layer |
A layer id from |
bbox |
Optional server-side bounding-box pre-filter: a numeric vector
|
filter_by |
Optional |
predicate |
Spatial predicate for |
datetime |
Optional temporal filter: a single year (e.g. |
crs |
Optional output CRS as an EPSG code (e.g. |
max_features |
Optional cap on the number of features returned. |
By default the data is returned in the coordinate reference system the
service provides (lon/lat, CRS84, for the OGC path). Set crs to receive the
data in another CRS; the transformation is done client-side with
sf::st_transform().
An sf object with one row per feature, the layer's
attribute columns, and a geometry column. A zero-row sf is returned (with
a warning) when nothing matches.
pdok_list_layers() to find layer ids.
# A whole layer: the Dutch national parks parks <- pdok_read("rvo/nationale-parken-geharmoniseerd", "protectedsite") # Municipalities for 2024, in RD New (EPSG:28992) pdok_read( "cbs/gebiedsindelingen", "gemeente_gegeneraliseerd", datetime = 2024, crs = 28992, max_features = 5 ) # One-call area filter: national parks within the province of Utrecht provinces <- pdok_read( "cbs/gebiedsindelingen", "provincie_gegeneraliseerd", datetime = 2024 ) utrecht <- provinces[provinces$statnaam == "Utrecht", ] parks_utrecht <- pdok_read( "rvo/nationale-parken-geharmoniseerd", "protectedsite", filter_by = utrecht )# A whole layer: the Dutch national parks parks <- pdok_read("rvo/nationale-parken-geharmoniseerd", "protectedsite") # Municipalities for 2024, in RD New (EPSG:28992) pdok_read( "cbs/gebiedsindelingen", "gemeente_gegeneraliseerd", datetime = 2024, crs = 28992, max_features = 5 ) # One-call area filter: national parks within the province of Utrecht provinces <- pdok_read( "cbs/gebiedsindelingen", "provincie_gegeneraliseerd", datetime = 2024 ) utrecht <- provinces[provinces$statnaam == "Utrecht", ] parks_utrecht <- pdok_read( "rvo/nationale-parken-geharmoniseerd", "protectedsite", filter_by = utrecht )
Finds the address, road or place nearest to each point, through the 'PDOK'
Locatieserver reverse service — the inverse of pdok_geocode(). Give it sf
points (in any CRS); it returns the nearest match(es) as an sf, including an
afstand column with the distance in meters.
pdok_reverse_geocode(points, type = NULL, crs = NULL, limit = 1)pdok_reverse_geocode(points, type = NULL, crs = NULL, limit = 1)
points |
An sf or |
type |
Optional result type to restrict to (see |
crs |
Optional output CRS as an EPSG code (e.g. |
limit |
Maximum number of results to return per point (default 1, the single nearest match). |
An sf object with one row per match and a point_id
column giving the row index of the input point each match came from. The
afstand column holds the distance in meters; all other non-geometry
fields the service returns are kept too. Points that match nothing are
dropped (with a warning); a zero-row sf is returned when nothing matches
at all.
pdok_geocode() for the forward lookup (address to coordinates).
# A point in the center of Utrecht: the nearest address pt <- sf::st_sfc(sf::st_point(c(5.121, 52.090)), crs = 4326) pdok_reverse_geocode(pt)# A point in the center of Utrecht: the nearest address pt <- sf::st_sfc(sf::st_point(c(5.121, 52.090)), crs = 4326) pdok_reverse_geocode(pt)
Filters the dataset registry from pdok_list_datasets() by a
case-insensitive partial match. The query is matched against each dataset's
identifier, name, description, and keywords.
pdok_search_datasets(query)pdok_search_datasets(query)
query |
A single non-empty string to search for, e.g. |
A tibble with the same columns as
pdok_list_datasets(), containing only the matching rows (zero rows when
nothing matches).
pdok_list_datasets() for the full list.
pdok_search_datasets("gemeente")pdok_search_datasets("gemeente")
Filters the layers from pdok_list_layers() by a case-insensitive partial
match against each layer's identifier, title, and description.
pdok_search_layers(dataset, query)pdok_search_layers(dataset, query)
dataset |
A dataset id from |
query |
A single non-empty string to search for, e.g. |
A tibble with the same columns as
pdok_list_layers(), containing only the matching rows (zero rows when
nothing matches).
pdok_list_layers() for the full list.
pdok_search_layers("cbs/gebiedsindelingen", "gemeente")pdok_search_layers("cbs/gebiedsindelingen", "gemeente")