QGIS acties en een OpenStreetMap API
In april ben ik naar de QGIS Gebruikersconferentie 2023 in ‘s-Hertogenbosch geweest. Op die conferentie werd er een zeer interessante workshop gegeven over het gebruik van Acties in QGIS door Ujaval Gandhi van Spatial Thoughts. Op dat moment besliste ik om een andere workshop en lezing bij te wonen die ook heel interessant waren, maar gelukkig deelde Ujaval zijn workshop notities online, dus na de conferentie heb ik al zijn voorbeelden geprobeerd en ik was zeer onder de indruk van de mogelijkheden van Acties in QGIS. Je moet ze zelf zeker ook eens proberen.
Een paar weken geleden las ik een blog post door Brendan Ashworth van Bunting Labs over hun API om van OpenStreetMap te downloaden. Wat me het meeste opviel, was het feit dat deze API GeoJSON gegevens als resultaat van een opvraging oplevert. Dus vroeg ik mij af of het mogelijk was om deze API te gebruiken in QGIS Acties. In deze blog post, laat ik je zien hoe ik dat juist gedaan heb.
Doel
Wat ik wilde bereiken, is dat wanneer ik op een punt van een puntenkaartlaag klik en Acties is geactiveerd, dat dan alle gebouwen uit OpenStreetMap in een straal van 100 meter daar rond op de kaart te zien zijn in een “Temporary Scratch Layer”, inclusief alle data uit tags in de attributentabel.
Waar moet je de QGIS actie code gebruiken?
Eerst voegde ik een puntenkaartlaag toe aan mijn QGIS project. Alle puntenkaartlagen zouden moeten werken voor dit voorbeeld. Als je hetzelfde zou willen doen voor een lijnen- of een polygonenkaartlaag, zal je de code hieronder moeten aanpassen. Als je de puntenkaartlaag toegevoegd hebt, moet je de “Properties” van die kaartlaag openen en aan de linkerkant van het Properties-venster de optie “Actions” selecteren.

Daarna klik je op het knopje met de groene plus onder de (waarschijnlijk lege) lijst met Acties.

Er verschijnt dan een nieuw venster waar je de instellingen voor je actie kan instellen. Zorg er zeker voor dat je het type op “Python” zet en dat je minstens het veld “Description” invult met een duidelijke omschrijving. Als je wil kan je ook “Short Name” en “Icon” toevoegen. De “Action Scopes” Canvas en Feature moeten zeker aangevinkt zijn.

Daarna kan je de code toevoegen aan het “Action Text” tekstveld.
De code voor de QGIS Actie
De volgende code is degene die ik gemaakt heb om het bovenstaande doel te bereiken. In de commentaar tussen de code beschrijf ik telkens wat elke stap doet.
# Importeer de benodigde packages
import requests
import json
from qgis.core import Qgis, QgsVectorLayer, QgsJsonUtils, QgsProject
# Verkrijg de coördinaten van het punt waarop je klikte in de kaart. De delen tussen [% %] zijn QGIS Expressions. Deze werken op dezelfde manier zoals je ze in de Field Calculator zou gebruiken. Python interpreteert ze als de berekende waarde uit de expressie.
pointxy = "[%replace(to_string(x(transform($geometry, @layer_crs ,'EPSG:4326'))),',','.')%],[%replace( to_string(y(transform($geometry, @layer_crs ,'EPSG:4326'))),',','.')%]"
# De parameters voor de bevraging aan de API. Vervang MY_API_KEY door je eigen key, die je kan verkrijgen op https://buntinglabs.com/solutions/openstreetmap-extracts. We vragen alle gebouwen op binnen een straal van 100 meter rond het huidige punt.
params = {
"tags": "building=*",
"api_key": "MY_API_KEY",
"center": pointxy,
"radius": "100"
}
# Bevraging aan de extract-in-radius endpoint van de API
response = requests.get("https://osm.buntinglabs.com/v1/osm/extract-in-radius", params=params)
# Converteer het verkregen antwoord naar een JSON tekstwaarde
geoj = json.dumps(response.json())
# Vind de attribuutvelden in de GeoJSON
fields = QgsJsonUtils.stringToFields(geoj)
# Verkrijg alle features uit de GeoJSON en gebruik de hierboven gevonden attribuutvelden
feats = QgsJsonUtils.stringToFeatureList(geoj,fields, None)
# Als de API bevraging 1 of meer resultaten oplevert, voer dan de onderstaande code uit, anders druk je een boodschap af in de console log
if len(feats) > 0:
# Vind het geometry type van het resultaat van de de bevraging. Voor onze gebouwen zal dat altijd Multipolygon zijn, maar als je iets anders opvraagt, kan dat ook iets anders zijn.
geom_type = feats[0].geometry().type()
if geom_type == QgsWkbTypes.PointGeometry:
geotype = "MultiPoint"
elif geom_type == QgsWkbTypes.LineGeometry:
geotype = "MultiLineString"
elif geom_type == QgsWkbTypes.PolygonGeometry:
geotype = "MultiPolygon"
else:
print("geometry type not defined in the result")
# Creëer een lege Temporary Scratch Layer met de naam "Buildings" van het hiervoor gevonden geometry type
layer = QgsVectorLayer(geotype, "Buildings", "memory")
# Voeg de benodigde attribuutvelden toe aan de Temporary Scratch Layer
layer.dataProvider().addAttributes(fields)
# Voeg de lege Temporary Scratch Layer toe aan de kaart
registry = QgsProject.instance()
registry.addMapLayer(layer)
# Voeg elke feature uit het resultaat van de API bevraging toe aan de Temporary Scratch Layer
for feature in feats:
layer.dataProvider().addFeatures([feature])
else:
print("no features found in the geoJSON")
# Herlaad de visualisatie van de Temporary Scratch Layer
layer.updateExtents()
layer.reload()
Het resultaat
Het resultaat van deze actie is een Temporary Scratch Layer met polygonen van de gebouwen in een straal van 100 meter rondom het aangeklikte punt.
