Het leek me interessant en nuttig om wat meer te leren over het bevragen van een Web Feature Service (WFS) in de browser in het algemeen, en de WFS van de Basisregistratie Adressen en Gebouwen (BAG) in het bijzonder. Daarom ben ik maar eens in de materie gedoken 😉 Wat ik heb opgestoken, lees je in dit blog. De volgende bronnen waren daarbij erg behulpzaam:
- Using a Web Feature Service van Chris Holmes en Mike Pumphrey
- Intro: WFS CQL filters om geodata te bevragen en Haal meer data (en GeoJSON) uit een PDOK WFS van Thijs Brentjens
- PDOK / NGR documentatie beheerd door Simeon Nedkov
- OpenGIS Web Feature Service 2.0 Interface Standard van het Open Geospatial Consortium (OGC)
WFS is een interface voor het opvragen van geografische vector data en bijbehorende administratieve data over het internet (zie Wikipedia). Je kunt een WFS bevragen met een HTTP GET of HTTP POST request. Een HTTP GET request (kortweg: GET) is een verzoek aan een WFS om gegevens te retourneren versleuteld in een URL. Een GET voor de WFS van de BAG ziet er zo uit:
http://geodata.nationaalgeoregister.nl/bag/wfs?param1=waarde1¶m2=waarde2&...
De drie meest gebruikte GET requests voor het bevragen van een WFS zijn:
GetCapabilities
DescribeFeatureType
GetFeature
Tenzij je een ander formaat specificeert, geeft de WFS antwoord in de vorm van een XML-document.
In onderstaande tekst wordt het gebruik van deze drie requests geïllustreerd aan de hand van de WFS voor de BAG.
Met een GetCapabilities
request krijg je snel inzicht in wat de WFS voor mogelijkheden biedt:
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&request=GetCapabilities
Welke lagen bevat de service? In welk coördinatenstelsel worden de features standaard geretourneerd? Wat voor uitvoerformaten ondersteunt de service? Vragen die beantwoord worden in het GetCapabilities document dat de service retourneert.
Als je detailinformatie over een laag (‘feature type’) in de WFS wilt, bijvoorbeeld de namen en gegevenstypen van de attributen, gebruik dan DescribeFeatureType
. Door een waarde op te geven voor de parameter typeName
geef je aan van welke laag je meer informatie wilt opvragen.
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=DescribeFeatureType&typeName=bag:verblijfsobject
Voor de duidelijkheid is in bovenstaand request version=2.0.0
opgenomen, maar strikt genomen is dit niet nodig. Als je de parameter version
weg laat, geeft de service standaard de hoogste ondersteunde versie terug. In het geval van de BAG is dat 2.0.0.
Met een getFeature
request kun je de geografische objecten (‘features’) en hun bijbehorende administratieve gegevens opvragen. Met de parameter count
kun je het maximum aantal features aangeven dat in het antwoord geretourneerd wordt. Voor de BAG-service geldt dat count
standaard de waarde 15.000 heeft.
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=GetFeature&typeName=bag:verblijfsobject&count=25
Met de parameter sortBy
kun je de features in het antwoord van de service sorteren op attribuutwaarden. Met de toevoeging +A
of +D
kun je aangeven of de waarden in respectievelijk oplopende of aflopende volgorde gesorteerd moeten worden. Let op: bij een WFS die met ArcGIS Server gepubliceerd wordt, werkt de parameter sortBy
alléén als de gegevens zijn opgeslagen in een ArcSDE geodatabase.
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=GetFeature&typeName=bag:verblijfsobject&count=25&sortBy=bag:identificatie+D
Als je slechts geïnteresseerd bent in één of enkele attributen, kun je het aantal attributen in het antwoord beperken met behulp van de parameter propertyName
.
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=GetFeature&typeName=bag:verblijfsobject&count=25&propertyName=bag:postcode,bag:woonplaats
De gegevens van een specifieke feature kun je opvragen met de featureId
parameter. De waarde die je in het geval van de BAG-service moet opgeven is het gml:id
en níet de bag:identificatie!
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=GetFeature&typeName=bag:verblijfsobject&count=25&featureId=verblijfsobject.118285
Door met de parameter bbox
een ‘bounding box’ op te geven, vraag je alleen de features op die binnen het opgegeven gebied liggen. Als de gegevens in EPSG:28992 (Amersfoort / RD New) geretourneerd worden, zoals bij de BAG standaard het geval is, geldt bbox=xmin,ymin,xmax,ymax
. In het voorbeeld worden alle verblijfsobjecten in een deel van de gemeente De Marne opgevraagd.
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=GetFeature&typeName=bag:verblijfsobject&bbox=213089,593892,217076,597981
Het is ook mogelijk om een selectie te maken met behulp van de filter
parameter. Het filter om alleen de verblijfsobjecten uit de BAG opgevraagd met postcode 9712 JN is: filter=<Filter><PropertyIsEqualTo><PropertyName>bag:postcode</PropertyName><Literal>9712JN</Literal></PropertyIsEqualTo></Filter>
Als je een filter opneemt in een URL, moet je niet vergeten bijzonder tekens zoals spaties, haakjes en aanhalingstekens te vervangen met behulp van URL encoding. Het maakt het request er jammer genoeg niet overzichtelijker op.
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=GetFeature&typeName=bag:verblijfsobject&filter=%3CFilter%3E%3CPropertyIsEqualTo%3E%3CPropertyName%3Ebag:postcode%3C/PropertyName%3E%3CLiteral%3E9712JN%3C/Literal%3E%3C/PropertyIsEqualTo%3E%3C/Filter%3E
Een ander voorbeeld van het filteren van features op basis van attribuutwaarden is onderstaand verzoek om alleen de verblijfsobjecten in Groningen met huisnummer 4 te retourneren. Het filter is in dit geval: Filter=<Filter><And><PropertyIsEqualTo><PropertyName>bag:woonplaats</PropertyName><Literal>Groningen</Literal></PropertyIsEqualTo><PropertyIsEqualTo><PropertyName>bag:huisnummer</PropertyName><Literal>4</Literal></PropertyIsEqualTo></And></Filter>
Met URL encoding wordt de URL dan:
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=GetFeature&typeName=bag:verblijfsobject&filter=%3CFilter%3E%3CAnd%3E%3CPropertyIsEqualTo%3E%3CPropertyName%3Ebag:woonplaats%3C/PropertyName%3E%3CLiteral%3EGroningen%3C/Literal%3E%3C/PropertyIsEqualTo%3E%3CPropertyIsEqualTo%3E%3CPropertyName%3Ebag:huisnummer%3C/PropertyName%3E%3CLiteral%3E4%3C/Literal%3E%3C/PropertyIsEqualTo%3E%3C/And%3E%3C/Filter%3E
Jammer genoeg kun je de parameters bbox
en filter
niet combineren. Als je het probeert, krijg je een foutmelding: Filter and bbox both specified but are mutually exclusive.
Het is niet eenvoudig om een filter te specificeren conform de officiële specificatie van het OGC. GeoServer maakt het iets makkelijker met CQL filters. CQL staat voor Common Query Language. Het is beter leesbaar dan ‘gewone’ filters, al moet je bij CQL filters ook URL encoding toepassen. CQL filters zijn echter geen onderdeel van de formele WFS-specificatie! De volgende voorbeelden met CQL filters werken, omdat de BAG-service ‘draait’ op GeoServer. Bij een service die wordt ontsloten met ArcGIS Server of Deegree kun je geen gebruik maken van CQL filters.
Het request in het vorige voorbeeld zou je ook kunnen doen met een CQL filter: cql_filter=(huisnummer=4 and woonplaats='Groningen')
Als je een CQL filter opneemt in een URL, moet je niet vergeten bijzonder tekens zoals spaties, haakjes en aanhalingstekens te vervangen met behulp van URL encoding. Het verzoek om alle verblijfsobjecten in Groningen met huisnummer 4 wordt dan:
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=GetFeature&typeName=bag:verblijfsobject&cql_filter=%28bag:woonplaats=%27Groningen%27%20and%20bag:huisnummer=4%29
Met een CQL filter kunt je ook op geometrie filteren. Met dit CQL filter worden alle verblijfsobjecten in een straal van 100 meter rondom het provinciehuis in Groningen opgevraagd: cql_filter=dwithin(bag:geometrie, point(234055 582111), 100, meters)
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=GetFeature&typeName=bag:verblijfsobject&cql_filter=dwithin%28bag:geometrie,point%28234055%20582111%29,100,meters%29
Het is mogelijk om tegelijkertijd te filteren op geometrie – bijvoorbeeld door een bounding box op te geven – en op attribuutwaarden. Het CQL filter voor het opvragen van alle verblijfsobjecten met een logiesfunctie in een door coördinaten begrensd deel van de gemeente De Marne is: cql_filter=((bbox(bag:geometrie, 213089, 593892, 217076, 597981) and (bag:gebruiksdoel='logiesfunctie'))
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=GetFeature&typeName=bag:verblijfsobject&cql_filter=%28bbox%28bag:geometrie,213089,593892,217076,597981%29and%28bag:gebruiksdoel=%27logiesfunctie%27%29%29
De BAG-service retourneert maximaal 15.000 features per request. Maar soms wil je meer features opvragen, dan kun je gebruik maken van ‘response paging’. Vraag eerst het totaal aantal features op dat voldoet aan je zoekopdracht door gebruik te maken van resultType=hits
. Vervolgens kun je door slim gebruik te maken van de parameters startindex
, count
en sortBy
in ‘etappes’ alle features opvragen.
In onderstaand voorbeeld wordt het aantal verblijfsobjecten in de woonplaats Groningen opgevraagd. Het aantal wordt geretourneerd in het XML-attribuut numberMatched
.
http://geodata.nationaalgeoregister.nl/bag/wfs?service=WFS&version=2.0.0&request=GetFeature&typename=bag:verblijfsobject&cql_filter=%28bag:woonplaats=%27Groningen%27%29&resulttype=hits
De eerste 5.000 verblijfsobjecten in Groningen krijg je retour met het volgende request:
http://geodata.nationaalgeoregister.nl/bag/wfs?service=WFS&version=2.0.0&request=GetFeature&typename=bag:verblijfsobject&cql_filter=%28bag:woonplaats=%27Groningen%27%29&count=5000&startindex=0&sortBy=bag:identificatie+A
De volgende 5.000 verblijfsobjecten krijg je door startIndex=5000
op te geven.
http://geodata.nationaalgeoregister.nl/bag/wfs?service=WFS&version=2.0.0&request=GetFeature&typename=bag:verblijfsobject&cql_filter=%28bag:woonplaats=%27Groningen%27%29&count=5000&startindex=5000&sortBy=bag:identificatie+A
Standaard geeft een WFS het antwoord terug in XML-formaat, maar je kunt ook een andere formaat opgeven met behulp van de outputFormat
parameter. In dit voorbeeld worden bijvoorbeeld alle verblijfsobjecten met postcode 9712 JN opgevraagd in CSV-formaat.
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=GetFeature&typeName=bag:verblijfsobject&cql_filter=%28bag:postcode=%279712JN%27%29&outputFormat=csv
KML kan ook:
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=GetFeature&typeName=bag:verblijfsobject&cql_filter=%28bag:postcode=%279712JN%27%29&srsName=EPSG:4326&outputFormat=kml
Als je de gegevens wilt opslaan in GeoJSON formaat, vergeet dan niet EPSG:4326 (WGS84) als coördinatenstelsel op te geven met behulp van de srsName
parameter. De meeste ontwikkelaars en data analisten verwachten namelijk dat coördinaten in een GeoJSON-bestand in WGS84 zijn.
http://geodata.nationaalgeoregister.nl/bag/wfs?service=wfs&version=2.0.0&request=GetFeature&typeName=bag:verblijfsobject&cql_filter=%28bag:postcode=%279712JN%27%29&outputFormat=json&srsName=epsg:4326
Let op: de outputFormat
parameter is weliswaar onderdeel van de formele WFS-specificatie van het OGC, maar welke uitvoerformaten beschikbaar zijn, hangt af van de map server die gebruikt wordt. Zo ondersteunt Deegree op dit moment bijvoorbeeld nog geen KML en GeoJSON. De uitvoerformaten waaruit je kunt kiezen voor een bepaalde WFS, kun je opvragen met een GetCapabilities
request.
Tot zover. Mocht je nog verbeteringen of aanvullingen hebben, dan hoor ik het graag!