Skip to content

feat: serve the Netherlands use type from BAG units - #217

Merged
jravani merged 2 commits into
mainfrom
feat/nl-building-use
Oct 22, 2026
Merged

jravani merged 2 commits into
mainfrom
feat/nl-building-use

Conversation

@jravani

@jravani jravani commented Oct 22, 2026

Copy link
Copy Markdown
Member

Part of #210: the Netherlands, after Germany in #211.

use_type and residential were Germany only: they map a source function code, and Dutch 3D data carries none. BAG records the use (gebruiksdoel) and floor area (oppervlakte) of every unit (verblijfsobject) of a building, which gives the building's use, including mixed use.

Change

  • Attribute datasets: data/attributes/<country>/<dataset>/ holds an attribution.json (validated and credited like a 3D dataset's) and building_use.csv:

    Column Content
    object_id The building's object id as the 3D data carries it
    use_code One provider use code; several codes are several rows
    floor_area m2 the code covers, >= 0
  • Import (-create-db, -import-data, on-request runs): each dataset's rows in city2tabula.building_use are replaced via CopyFrom in one transaction. The table is created if missing, so a database built earlier gains it on its next import. A malformed file stops the import with file and line; every file is parsed before any row is written. dataset_id must be unique across 3D and attribute datasets.

  • Serving: where a building's function code gives no use, the buildings queries read its rows as one JSON column. The use type covering the most floor area wins, with equal areas going to the first by name. residential is true when any row is a dwelling. The dataset is added to attributions. A database without the table returns null, as before.

  • Mapping internal/onrequest/usetype/netherlands.csv:

    • the 11 BAG gebruiksdoelen;
    • plus geen verblijfsobject, which the extract gives a building without units (a shed, garage or barn): ancillary;
    • bijeenkomstfunctie, celfunctie and sportfunctie: public;
    • logiesfunctie: hospitality.
  • Deployment: environment/http/docker-compose.yml mounts data/attributes, and .gitignore excludes it.

  • Docs: data/README.md, the attribution page, OpenAPI (use_type, attributions), CHANGELOG.md.

The response shape is unchanged; Dutch buildings now carry values.

Tests

  • Unit:

    • TestUseTypeFromUnits: largest floor area, codes of one use type adding up, ties, unmapped codes, building without units.
    • TestParseBuildingUse, TestDiscoverAttributeDatasets: header, joined codes, bad area, missing file or folder.
  • Integration: TestServe_UseTypeFromBuildingUnits on the real Dutch seed covers the import, the served use type, residential, credits and restaging. With the fallback removed it fails (use_type null, residential <nil>, want retail and true).

  • go vet ./... and go test ./... pass. The integration suites for importer, process, onrequest, db and api pass.

  • End to end on the Loenen and Heeten BAG extract (20,039 rows) and 3DBAG tiles, through create-db, extract and the API:

    use_type Buildings Median footprint
    ancillary 4,482 (40.9%) 35 m2
    residential 4,013 (36.7%) 95 m2
    hospitality 1,759 (16.1%) 72 m2
    other uses 562 (5.1%)
    null 131 (1.2%)

    The 131 without a use type have left BAG since the 3DBAG snapshot. 177 buildings hold dwellings under another main use. All 10,947 buildings come back in one bbox request in 0.75 s.

use_type and residential were Germany only: they map a source function
code, and Dutch 3D data carries none. BAG records the use and floor
area of every unit (verblijfsobject) of a building, which gives the
building's use, including mixed use.

Building attributes the 3D data lacks are staged under
data/attributes/<country>/<dataset>/ as an attribution.json, validated
and credited like a 3D dataset's, plus building_use.csv with one row
per building and use code: object_id, use_code, floor_area. The import
(-create-db, -import-data, on-request runs) replaces each dataset's
rows in city2tabula.building_use, a table it creates if missing, so a
database built earlier gains it on its next import. A malformed file
stops the import with file and line.

Where a building's function code gives no use, the buildings queries
read its rows: the use type covering the most floor area wins, equal
areas going to the first by name; residential is true when any row is
a dwelling; the dataset joins the response's attributions. The Dutch
code list maps the 11 BAG gebruiksdoelen, plus "geen verblijfsobject",
which the extract gives a building without units (a shed, garage or
barn), to ancillary.

On Loenen and Heeten (10,947 buildings) the API serves a use type for
98.8%: ancillary 40.9%, residential 36.7%, hospitality 16.1%. The 131
without one have left BAG since the 3DBAG snapshot. The server needs
data/attributes mounted next to data/lod2.
@codecov

codecov Bot commented Oct 22, 2026

Copy link
Copy Markdown

@jravani
jravani merged commit 7e397f2 into main Oct 22, 2026
7 of 8 checks passed
@jravani
jravani deleted the feat/nl-building-use branch October 22, 2026 23:08
@jravani jravani mentioned this pull request Oct 23, 2026
@jravani jravani added this to the v0.12.0 milestone Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant