3D Kaart NL tot leven gebracht met OSM Buildings

Eind november maakten we een interactieve kaart van het centrum van Valkenburg (Limburg) op basis van de gebouwen uit 3D Kaart NL met behulp van de JavaScript-bibliotheek OSM Buildings. In deze blog leggen we uit hoe we deze kaart hebben gemaakt en doen enkele aanbevelingen voor het leveren van 3D-gegevens.

Valkenburg in 3D

Van geodatabase naar PostGIS

Het Kadaster biedt 3D Kaart NL aan als een gecomprimeerde Esri file geodatabase (FGDB). Geen ArcGIS softwarelicentie? Geen nood! Ook het open source programma ogr kan FGDB-bestanden lezen. Na het uitpakken van het ZIP-betand, kan met het commando ogrinfo informatie worden opgevraagd over het bestand:

ogrinfo -so Valkenburg.gdb

-so (summary only) : Hiermee zorg je ervoor, dat alleen een korte samenvatting wordt weergegeven.

Dit commando geeft de volgende samenvatting weer over het bestand Valkenburg.gdb:

Had to open data source read-only.
INFO: Open of `Valkenburg.gdb'
using driver `OpenFileGDB' successful.
1: terreinVlak (Multi Polygon)
2: waterdeelVlak (Multi Polygon)
3: wegdeelVlak (Multi Polygon)
4: gebouwVlak (Multi Polygon)
5: gebouwVlak_stat (Multi Polygon)
6: terreinpunten (3D Point)
7: wegdeelVlak_3D_LOD0 (3D Multi Polygon)
8: terreinVlak_3D_LOD0 (3D Multi Polygon)
9: gebouw_3D_LOD0 (3D Multi Polygon)
10: waterdeelVlak_3D_LOD0 (3D Multi Polygon)
11: gebouw_3D_LOD1 (3D Multi Polygon)
12: brugWater (3D Multi Polygon)
13: brugWeg (3D Multi Polygon)
14: TerreinOnder (3D Multi Polygon)

Van de 14 thema’s in dit bestand lijkt het thema gebouw_3D_LOD0 het meest geëigend voor onze toepassing met OSM Buildings. We gebruiken opnieuw het commando ogrinfo om uit te vinden, welke informatie in dit thema beschikbaar is:

ogrinfo -so Valkenburg.gdb gebouw_3D_LOD0

Een van de eigenschappen van het thema gebouw_3D_LOD0 is HOOGTE. We gebruiken opnieuw het commando ogrinfo om uit te vinden welke waarden deze eigenschap heeft:

ogrinfo -sql 'SELECT DISTINCT HOOGTE FROM gebouw_3D_LOD0' Valkenburg.gdb

Dit commando geeft de volgende uitvoer:

Had to open data source read-only.
INFO: Open of `Valkenburg.gdb'
using driver `OpenFileGDB' successful.

Layer name: gebouw_3D_LOD0
Geometry: None
Feature Count: 0
Layer SRS WKT:
(unknown)
HOOGTE: String (0.0)

Weliswaar is de geometrie van de gebouwen gemodelleerd als 3D Multi-polygonen, maar de eigenschap HOOGTE bevat geen informatie! OSM Buildings heeft een GeoJSON-bestand nodig met de 2D-geometrie in WGS-84 coördinaten en met een eigenschap height, die de hoogte in meters bevat. Die hoogte zullen we dus zelf moeten afleiden uit de 3D-geometrie. Hiervoor gebruiken we de ruimtelijke functies van PostGIS. Met het ogr2ogr commando van ogr importeren we het thema gebouw_3D_LOD0 uit de FGDB in de database:

ogr2ogr -skipfailures -progress --config PG_USE_COPY YES -gt 65536 -s_srs "epsg:28992" -t_srs "epsg:28992" -lco GEOMETRY_NAME=geom -lco SPATIAL_INDEX=OFF -lco PRECISION=NO -nln valkenburg.buildings -f "PGDump" /vsistdout/ Valkenburg.gdb gebouw_3D_LOD1 | psql -q

Gegevensbewerking in PostGIS

In de eerste plaats herprojecteren we de geometrie van de gebouwen van RD-coördinaten naar lengte- en breedtegraden in WGS-84 (ST_Transform). Vervolgens wordt de 3D-geometrie teruggebracht naar 2D (ST_Force_2D) om de GeoJSON kleiner te maken: OSM Buildings negeert de Z-coördinaat toch. Ten slotte zetten we de coördinaten in de juiste volgorde (ST_ForceRHR). Dit zorgt ervoor, dat OSM Buildings de muren kan visualiseren. Dankjewel voor de tip, @OSMBuildings!

Suggestie van OSM Buildings om de volgorde van de coördinaten aan te passen.

Met de juiste functies is dit natuurlijk een koud kunstje voor PostGIS:

SELECT AddGeometryColumn (
'valkenburg',
'buildings',
'geometry',
4326,
'MULTIPOLYGON',
2
);
UPDATE
valkenburg.buildings
SET
geometry = ST_Transform(
ST_ForceRHR(
ST_Force_2D(geom)
),4326
);

Naast de 2D-geometrie willen we ook de hoogte van de gebouwen afleiden uit 3D-geometrie in het originele bestand. Met het commando ogrinfo hadden we namelijk geconstateerd, dat de eigenschap HOOGTE leeg is. Bovendien is deze eigenschap in het originele bestand als alfanumeriek veld (string) gedefinieerd. Dit kunnen we met PostGIS oplossen. In de eerste plaats veranderen we de definitie van de eigenschap HOOGTE naar een numeriek veld tot op de meter om ruimte te besparen in het uiteindelijke GeoJSON-bestand. Vervolgens berekenen we het daadwerkelijke hoogteverschil, niet de maximale hoogte: de gebouwen zijn namelijk ook al op een hoogte geplaatst!

ALTER TABLE
valkenburg.buildings
ALTER COLUMN
hoogte TYPE integer USING CAST(hoogte AS integer);
UPDATE
valkenburg.buildings
SET
hoogte = ST_ZMAX(geom) - ST_ZMIN(geom);

Ten slotte gebruiken we weer ogr om de gegevens uit een tabel in PostGIS te converteren naar een GeoJSON-bestand. Als extra informatie nemen we het gebouwtype mee. Dit kunnen we gebruiken om gebouwen te classificeren:

ogr2ogr -f "GeoJSON" buildings.js PG:"host=${PGHOST} port=${PGPORT} dbname=${PGDATABASE} user=${PGUSER}" -sql "SELECT geometry, type_gebouw AS building_type, hoogte AS height FROM valkenburg.buildings" -lco COORDINATE_PRECISION=6

-lco (layer creation option) COORDINATE_PRECISION=6: Hiermee zorg je ervoor, dat je coördinaten slechts 6 decimalen hebben. Dit is nauwkeurig genoeg voor een webkaart!

Nu hebben we een GeoJSON-bestand, dat we kunnen gebruiken voor OSM Buildings.

OSM Buildings

Met de JavaScript-bibliotheek OSM Buildings kan je 3D gebouwen visualiseren in combinatie met OpenLayers 2.x en Leaflet. Inderdaad, de aanduiding OSM in OSM Buildings komt van OpenStreetMap. OSM Buildings gebruikt namelijk standaard de gebouwen uit de OpenStreetMap-database voor haar visualisaties, maar biedt je ook de optie om op basis van een eigen GeoJSON-bestand gebouwen te visualiseren. Deze laatste optie gebruiken we hier.

Op basis van het gebouwtype (de eigenschap building_type) geven we muren en de daken een andere kleur:

var osmb = new OSMBuildings(map)
.each(function(item) {
var prop = item.properties;
if (Legend.hasOwnProperty(prop.building_type)) {
prop.wallColor = Legend[ prop.building_type ]['wall'];
prop.roofColor = Legend[ prop.building_type ]['roof'];
} else {
prop.wallColor = 'rgba(235,207,190,1.0)';
prop.roofColor = 'rgba(235,207,190,1.0)';
}
})
.set(BldgJSON);

De kleuren voor de verschillende gebouwtypes hebben we in een legenda-object gedefinieerd:

var Legend = {
gemeentehuis: { wall: 'rgba(200,155,54,0.8)', roof: 'rgba(236,173,42,0.7)'},
huizenblok: { wall: 'rgba(232,184,174,0.8)', roof: 'rgba(232,184,174,0.7)'},
kasteel: {wall: 'rgba(153,51,0,0.8)', roof: 'rgba(153,51,0,0.7)'},
overig: {wall: 'rgba(235,207,190,1.0)', roof: 'rgba(235,207,190,1.0)'},
politiebureau: { wall: 'rgba(200,155,54,0.8)', roof: 'rgba(236,173,42,0.7)'},
postkantoor: {wall: 'rgba(200,155,54,0.8)', roof: 'rgba(236,173,42,0.7)'},
ruïne: { wall: 'rgba(153,51,0,0.8)', roof: 'rgba(153,51,0,0.7)'},
sporthal: { wall: 'rgba(137,159,165,0.8)', roof: 'rgba(137,159,165,0.7)'},
toren: { wall: 'rgba(153,51,0,0.8)', roof: 'rgba(153,51,0,0.7)'},
zwembad: { wall: 'rgba(137,159,165,0.8)', roof: 'rgba(137,159,165,0.7)'}
}

De benodigde bestanden zijn beschikbaar als een gist op GitHub. Zo krijgen we uiteindelijk de interactieve kaart van Valkenburg met gebouwen in 3D.

Aanbevelingen

Op basis van onze ervaringen hebben we drie aanbevelingen voor het leveren van 3D Kaart NL:

  1. Gebruik het veld HOOGTE: weliswaar is de geometrie beschikbaar in 3D, maar voor de meeste toepassingen is een hoogte al voldoende. We hebben nu het maximale hoogteverschil op basis van de 3D-geometrie gebruikt, maar je kan je ook voorstellen, dat je hiervoor bijvoorbeeld de meest voorkomende (modale) hoogte gebruikt. Definieer ten slotte de eigenschap HOOGTE als een numeriek veld!
  2. Maak de volgorde van de coördinaten rechtsdraaiend. Dit is een conventie om vectoren in 3 dimensies vast te leggen. De buitenste ring van een polygoon draait dan met de klok mee en de binnenringen draaien dan tegen de klok in. Gebruikten we de geometrienotatie 1:1 uit het bronbestand, dan tekende OSM Buildings de muren niet correct. Dit is wellicht ook voor andere grafische bibliotheken het geval.
  3. Verwijder fysieke kenmerken uit de eigenschap GEBOUW_TYPE. In het bronbestand is deze eigenschap een mengeling van gebruiksfuncties (gemeentehuis, politiebureau, sporthal) en algemenere omschrijvingen (kasteel, ruïne). De waarde religieus gebouw, toren voor de eigenschap GEBOUW_TYPE duidt weer op een fysiek kenmerk. Dit fysieke kenmerk is in een 3D-bestand natuurlijk al impliciet vastgelegd.

In deze applicatie hebben we ons gericht op de gebouwen in 3D Kaart NL. Het vrijgeven van 3D Kaart NL was eerlijk gezegd vooral een aanleiding om eens met OSM Buildings aan de slag te gaan zónder eerst veel gegevensbewerking te moeten doen. Desalniettemin hebben we de smaak te pakken. Het is duidelijk: we staan aan het begin van een 3D-doorbraak!

contact

Meer weten? Bel of mail ons gerust!

Webmapper is gevestigd in
De Klompenfabriek:

Weerdsingel Westzijde 33a

3513 BC Utrecht

info@webmapper.net

+31 (0) 30 227 01 16