Since QGIS 3.18, no need for third party to geocode from Nominatim or Google Maps (not demonstrated below as you need an API key) as there is a class dedicated for this purpose in PyQGIS
Below, a recipe for Nominatim
import json
from qgis.core import (
QgsNominatimGeocoder,
QgsGeocoderContext,
QgsCoordinateTransformContext,
QgsGeocoderInterface
)
n = QgsNominatimGeocoder()
context = QgsGeocoderContext(QgsCoordinateTransformContext())
address = '28 rue paul bellamy, 44000 Nantes'
out = n.geocodeString(address, context)
getter_methods_geocoder_result = [
'additionalAttributes',
'crs',
'geometry',
'isValid',
'description',
'error',
'group',
'identifier'
]
for f in out:
for method in getter_methods_geocoder_result:
print(method, getattr(f, method)())
For reverse geocoding, there is an interface method geocodeFeature
in parent class QgsGeocoderInterface
of both QgsNominatimGeocoder
and QgsGoogleMapsGeocoder
. You need to implement it and change flags to set that your geocoder supports both geocoding and reverse geocoding
A "naive" reverse geocoding implementation below
import json
from qgis.core import (
QgsNominatimGeocoder,
QgsGeocoderContext,
QgsCoordinateTransformContext,
QgsGeocoderInterface
)
class QgsNominatimGeocoderOverloaded(QgsNominatimGeocoder):
def __init__(self, endpointReverse="https://nominatim.openstreetmap.org/reverse"):
super().__init__()
self.endpointReverse = endpointReverse
def flags(self):
return QgsGeocoderInterface.Flag.GeocodesStrings & QgsGeocoderInterface.Flag.GeocodesFeatures
def geocodeFeature(self, feature, context, feedback=None):
pt = feature.geometry().asPoint()
lon, lat = pt.x(), pt.y()
url = f"{self.endpointReverse}?lat={lat}&lon={lon}&format=json"
request = QgsBlockingNetworkRequest()
request.get(QNetworkRequest(QUrl(url)))
reply = request.reply()
content = reply.content()
jsonContent = json.loads(content.data().decode())
return self.jsonToResult(jsonContent)
context1 = QgsGeocoderContext(QgsCoordinateTransformContext())
if len(iface.activeLayer().selectedFeatures()) > 0:
firstSelected = iface.activeLayer().selectedFeatures()[0]
n1 = QgsNominatimGeocoderOverloaded()
out1 = n1.geocodeFeature(firstSelected, context1)
getter_methods_geocoder_result = [
'additionalAttributes',
'crs',
'geometry',
'isValid',
'description',
'error',
'group',
'identifier'
]
for method in getter_methods_geocoder_result:
print(method, getattr(out1, method)())
To account for rate limit, cache (avoid two calls with same URLs in a short interval to avoid being "banned"), a more complete code should be coded using code similar to geocodeString
implementation e.g https://qgis.org/api/qgsnominatimgeocoder_8cpp_source.html#l00072
London Eye
! :S