26#include <QCryptographicHash>
27#include <QJsonDocument>
29#include <QNetworkRequest>
34using namespace Qt::StringLiterals;
36QReadWriteLock QgsGoogleMapsGeocoder::sMutex;
45 , mRegion( regionBias )
46 , mEndpoint( u
"https://maps.googleapis.com/maps/api/geocode/json"_s )
59 fields.
append(
QgsField( u
"location_type"_s, QMetaType::Type::QString ) );
60 fields.
append(
QgsField( u
"formatted_address"_s, QMetaType::Type::QString ) );
61 fields.
append(
QgsField( u
"place_id"_s, QMetaType::Type::QString ) );
64 fields.
append(
QgsField( u
"street_number"_s, QMetaType::Type::QString ) );
66 fields.
append(
QgsField( u
"locality"_s, QMetaType::Type::QString ) );
67 fields.
append(
QgsField( u
"administrative_area_level_2"_s, QMetaType::Type::QString ) );
68 fields.
append(
QgsField( u
"administrative_area_level_1"_s, QMetaType::Type::QString ) );
69 fields.
append(
QgsField( u
"country"_s, QMetaType::Type::QString ) );
70 fields.
append(
QgsField( u
"postal_code"_s, QMetaType::Type::QString ) );
93 QgsDebugError(
"Could not transform geocode bounds to WGS84" );
100 const auto it = sCachedResultsGM()->constFind( url );
101 if ( it != sCachedResultsGM()->constEnd() )
107 QNetworkRequest request( url );
119 const QJsonDocument doc = QJsonDocument::fromJson( newReq.
reply().
content(), &err );
124 const QVariantMap res = doc.object().toVariantMap();
125 const QString status = res.value( u
"status"_s ).toString();
126 if ( status.isEmpty() || !res.contains( u
"results"_s ) )
128 return QList<QgsGeocoderResult>();
131 if ( res.contains(
"error_message"_L1 ) )
136 if ( status ==
"REQUEST_DENIED"_L1 || status ==
"OVER_QUERY_LIMIT"_L1 )
140 if ( status !=
"OK"_L1 && status !=
"ZERO_RESULTS"_L1 )
148 const QVariantList results = res.value( u
"results"_s ).toList();
149 if ( results.empty() )
151 sCachedResultsGM()->insert( url, QList<QgsGeocoderResult>() );
152 return QList<QgsGeocoderResult>();
155 QList< QgsGeocoderResult > matches;
156 matches.reserve( results.size( ) );
157 for (
const QVariant &result : results )
161 sCachedResultsGM()->insert( url, matches );
168 QUrl res( mEndpoint );
172 query.addQueryItem( u
"bounds"_s, u
"%1,%2|%3,%4"_s.arg( bounds.
yMinimum() )
177 if ( !mRegion.isEmpty() )
179 query.addQueryItem( u
"region"_s, mRegion.toLower() );
181 query.addQueryItem( u
"sensor"_s, u
"false"_s );
182 query.addQueryItem( u
"address"_s, address );
183 query.addQueryItem( u
"key"_s, mApiKey );
184 res.setQuery( query );
187 if ( res.toString().contains(
"fake_qgis_http_endpoint"_L1 ) )
190 QString modifiedUrlString = res.toString();
192 modifiedUrlString = QUrl::fromPercentEncoding( modifiedUrlString.toUtf8() );
193 modifiedUrlString.replace(
"fake_qgis_http_endpoint/"_L1,
"fake_qgis_http_endpoint_"_L1 );
195 modifiedUrlString = modifiedUrlString.mid( u
"http://"_s.size() );
196 QString args = modifiedUrlString.mid( modifiedUrlString.indexOf(
'?' ) );
197 if ( modifiedUrlString.size() > 150 )
199 args = QCryptographicHash::hash( args.toUtf8(), QCryptographicHash::Md5 ).toHex();
203 args.replace(
"?"_L1,
"_"_L1 );
204 args.replace(
"&"_L1,
"_"_L1 );
205 args.replace(
"<"_L1,
"_"_L1 );
206 args.replace(
">"_L1,
"_"_L1 );
207 args.replace(
"'"_L1,
"_"_L1 );
208 args.replace(
"\""_L1,
"_"_L1 );
209 args.replace(
" "_L1,
"_"_L1 );
210 args.replace(
":"_L1,
"_"_L1 );
211 args.replace(
"/"_L1,
"_"_L1 );
212 args.replace(
"\n"_L1,
"_"_L1 );
217 if ( modifiedUrlString[1] ==
'/' )
219 modifiedUrlString = modifiedUrlString[0] +
":/" + modifiedUrlString.mid( 2 );
222 modifiedUrlString = modifiedUrlString.mid( 0, modifiedUrlString.indexOf(
'?' ) ) + args;
223 QgsDebugMsgLevel( u
"Get %1 (after laundering)"_s.arg( modifiedUrlString ), 2 );
224 res = QUrl::fromLocalFile( modifiedUrlString );
232 const QVariantMap geometry = json.value( u
"geometry"_s ).toMap();
233 const QVariantMap location = geometry.value( u
"location"_s ).toMap();
234 const double latitude = location.value( u
"lat"_s ).toDouble();
235 const double longitude = location.value( u
"lng"_s ).toDouble();
243 QVariantMap attributes;
245 if ( json.contains( u
"formatted_address"_s ) )
246 attributes.insert( u
"formatted_address"_s, json.value( u
"formatted_address"_s ).toString() );
247 if ( json.contains( u
"place_id"_s ) )
248 attributes.insert( u
"place_id"_s, json.value( u
"place_id"_s ).toString() );
249 if ( geometry.contains( u
"location_type"_s ) )
250 attributes.insert( u
"location_type"_s, geometry.value( u
"location_type"_s ).toString() );
252 const QVariantList components = json.value( u
"address_components"_s ).toList();
253 for (
const QVariant &component : components )
255 const QVariantMap componentMap = component.toMap();
256 const QStringList types = componentMap.value( u
"types"_s ).toStringList();
258 for (
const QString &t :
263 u
"administrative_area_level_2"_s,
264 u
"administrative_area_level_1"_s,
269 if ( types.contains( t ) )
271 attributes.insert( t, componentMap.value( u
"long_name"_s ).toString() );
272 if ( t ==
"administrative_area_level_1"_L1 )
273 res.
setGroup( componentMap.value( u
"long_name"_s ).toString() );
278 if ( geometry.contains( u
"viewport"_s ) )
280 const QVariantMap viewport = geometry.value( u
"viewport"_s ).toMap();
281 const QVariantMap northEast = viewport.value( u
"northeast"_s ).toMap();
282 const QVariantMap southWest = viewport.value( u
"southwest"_s ).toMap();
284 southWest.value( u
"lat"_s ).toDouble(),
285 northEast.value( u
"lng"_s ).toDouble(),
286 northEast.value( u
"lat"_s ).toDouble()
296 mEndpoint = endpoint;
WkbType
The WKB type describes the number of dimensions a geometry has.
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
QString errorMessage() const
Returns the error message string, after a get(), post(), head() or put() request has been made.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr, RequestFlags requestFlags=QgsBlockingNetworkRequest::RequestFlags())
Performs a "get" operation on the specified request.
@ NoError
No error was encountered.
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get(), post(), head() or put() request has been mad...
Represents a coordinate reference system (CRS).
Custom exception class for Coordinate Reference System related exceptions.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Encapsulate a field in an attribute table or data source.
Container of fields for a vector layer.
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Encapsulates the context of a geocoding operation.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which should be used whenever the geocoder constructs a coo...
QgsCoordinateReferenceSystem areaOfInterestCrs() const
Returns the coordinate reference system for the area of interest, which can be used to indicate the d...
QgsGeometry areaOfInterest() const
Returns the optional area of interest, which can be used to indicate the desired geographic area wher...
@ GeocodesStrings
Can geocode string input values.
Represents a matching result from a geocoder search.
void setAdditionalAttributes(const QVariantMap &attributes)
Setss additional attributes generated during the geocode, which may be added to features being geocod...
void setGroup(const QString &group)
Sets the optional group value for the result.
void setViewport(const QgsRectangle &viewport)
Sets the suggested viewport for the result, which reflects a recommended map extent for displaying th...
static QgsGeocoderResult errorResult(const QString &errorMessage)
Creates an invalid error result, with the specified errorMessage string.
A geometry is the spatial representation of a feature.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
QList< QgsGeocoderResult > geocodeString(const QString &string, const QgsGeocoderContext &context, QgsFeedback *feedback=nullptr) const override
Geocodes a string.
void setEndpoint(const QString &endpoint)
Sets a specific API endpoint to use for requests.
QgsGeocoderResult jsonToResult(const QVariantMap &json) const
Converts a JSON result returned from the Google Maps service to a geocoder result object.
QgsFields appendedFields() const override
Returns a set of newly created fields which will be appended to existing features during the geocode ...
QString apiKey() const
Returns the API key which will be used when accessing the Google Maps API.
Qgis::WkbType wkbType() const override
Returns the WKB type of geometries returned by the geocoder.
QString region() const
Returns the optional region bias which will be used to prioritize results in a certain region.
QUrl requestUrl(const QString &address, const QgsRectangle &bounds=QgsRectangle()) const
Returns the URL generated for geocoding the specified address.
QgsGoogleMapsGeocoder(const QString &apiKey, const QString ®ionBias=QString())
Constructor for QgsGoogleMapsGeocoder.
void setRegion(const QString ®ion)
Sets the optional region bias which will be used to prioritize results in a certain region.
void setApiKey(const QString &key)
Sets the API key to use when accessing the Google Maps API.
Flags flags() const override
Returns the geocoder's capability flags.
QByteArray content() const
Returns the reply content.
A convenience class that simplifies locking and unlocking QReadWriteLocks.
void unlock()
Unlocks the lock.
void changeMode(Mode mode)
Change the mode of the lock to mode.
A rectangle specified with double values.
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QMap< QUrl, QList< QgsGeocoderResult > > CachedGeocodeResult
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
#define QgsSetRequestInitiatorClass(request, _class)