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 – banbar Jul 29 '17 at 17:33