25#include <QNetworkRequest> 
   26#include <QJsonDocument> 
   29QReadWriteLock QgsGoogleMapsGeocoder::sMutex;
 
   38  , mRegion( regionBias )
 
   39  , mEndpoint( QStringLiteral( "https:
 
   52  fields.
append( 
QgsField( QStringLiteral( 
"location_type" ), QVariant::String ) );
 
   53  fields.
append( 
QgsField( QStringLiteral( 
"formatted_address" ), QVariant::String ) );
 
   54  fields.
append( 
QgsField( QStringLiteral( 
"place_id" ), QVariant::String ) );
 
   57  fields.
append( 
QgsField( QStringLiteral( 
"street_number" ), QVariant::String ) );
 
   58  fields.
append( 
QgsField( QStringLiteral( 
"route" ), QVariant::String ) );
 
   59  fields.
append( 
QgsField( QStringLiteral( 
"locality" ), QVariant::String ) );
 
   60  fields.
append( 
QgsField( QStringLiteral( 
"administrative_area_level_2" ), QVariant::String ) );
 
   61  fields.
append( 
QgsField( QStringLiteral( 
"administrative_area_level_1" ), QVariant::String ) );
 
   62  fields.
append( 
QgsField( QStringLiteral( 
"country" ), QVariant::String ) );
 
   63  fields.
append( 
QgsField( QStringLiteral( 
"postal_code" ), QVariant::String ) );
 
   86      QgsDebugError( 
"Could not transform geocode bounds to WGS84" );
 
   93  const auto it = sCachedResults()->constFind( url );
 
   94  if ( it != sCachedResults()->constEnd() )
 
  100  QNetworkRequest request( url );
 
  112  const QJsonDocument doc = QJsonDocument::fromJson( newReq.
reply().
content(), &err );
 
  117  const QVariantMap res = doc.object().toVariantMap();
 
  118  const QString status = res.value( QStringLiteral( 
"status" ) ).toString();
 
  119  if ( status.isEmpty() || !res.contains( QStringLiteral( 
"results" ) ) )
 
  121    return QList<QgsGeocoderResult>();
 
  124  if ( res.contains( QLatin1String( 
"error_message" ) ) )
 
  129  if ( status == QLatin1String( 
"REQUEST_DENIED" ) || status == QLatin1String( 
"OVER_QUERY_LIMIT" ) )
 
  133  if ( status != QLatin1String( 
"OK" ) && status != QLatin1String( 
"ZERO_RESULTS" ) )
 
  141  const QVariantList results = res.value( QStringLiteral( 
"results" ) ).toList();
 
  142  if ( results.empty() )
 
  144    sCachedResults()->insert( url, QList<QgsGeocoderResult>() );
 
  145    return QList<QgsGeocoderResult>();
 
  148  QList< QgsGeocoderResult > matches;
 
  149  matches.reserve( results.size( ) );
 
  150  for ( 
const QVariant &result : results )
 
  154  sCachedResults()->insert( url, matches );
 
  161  QUrl res( mEndpoint );
 
  165    query.addQueryItem( QStringLiteral( 
"bounds" ), QStringLiteral( 
"%1,%2|%3,%4" ).arg( bounds.
yMinimum() )
 
  170  if ( !mRegion.isEmpty() )
 
  172    query.addQueryItem( QStringLiteral( 
"region" ), mRegion.toLower() );
 
  174  query.addQueryItem( QStringLiteral( 
"sensor" ), QStringLiteral( 
"false" ) );
 
  175  query.addQueryItem( QStringLiteral( 
"address" ), address );
 
  176  query.addQueryItem( QStringLiteral( 
"key" ), mApiKey );
 
  177  res.setQuery( query );
 
  180  if ( res.toString().contains( QLatin1String( 
"fake_qgis_http_endpoint" ) ) )
 
  183    QString modifiedUrlString = res.toString();
 
  185    modifiedUrlString = QUrl::fromPercentEncoding( modifiedUrlString.toUtf8() );
 
  186    modifiedUrlString.replace( QLatin1String( 
"fake_qgis_http_endpoint/" ), QLatin1String( 
"fake_qgis_http_endpoint_" ) );
 
  188    modifiedUrlString = modifiedUrlString.mid( QStringLiteral( 
"http://" ).size() );
 
  189    QString args = modifiedUrlString.mid( modifiedUrlString.indexOf( 
'?' ) );
 
  190    if ( modifiedUrlString.size() > 150 )
 
  192      args = QCryptographicHash::hash( args.toUtf8(), QCryptographicHash::Md5 ).toHex();
 
  196      args.replace( QLatin1String( 
"?" ), QLatin1String( 
"_" ) );
 
  197      args.replace( QLatin1String( 
"&" ), QLatin1String( 
"_" ) );
 
  198      args.replace( QLatin1String( 
"<" ), QLatin1String( 
"_" ) );
 
  199      args.replace( QLatin1String( 
">" ), QLatin1String( 
"_" ) );
 
  200      args.replace( QLatin1String( 
"'" ), QLatin1String( 
"_" ) );
 
  201      args.replace( QLatin1String( 
"\"" ), QLatin1String( 
"_" ) );
 
  202      args.replace( QLatin1String( 
" " ), QLatin1String( 
"_" ) );
 
  203      args.replace( QLatin1String( 
":" ), QLatin1String( 
"_" ) );
 
  204      args.replace( QLatin1String( 
"/" ), QLatin1String( 
"_" ) );
 
  205      args.replace( QLatin1String( 
"\n" ), QLatin1String( 
"_" ) );
 
  210    if ( modifiedUrlString[1] == 
'/' )
 
  212      modifiedUrlString = modifiedUrlString[0] + 
":/" + modifiedUrlString.mid( 2 );
 
  215    modifiedUrlString = modifiedUrlString.mid( 0, modifiedUrlString.indexOf( 
'?' ) ) + args;
 
  216    QgsDebugMsgLevel( QStringLiteral( 
"Get %1 (after laundering)" ).arg( modifiedUrlString ), 2 );
 
  217    res = QUrl::fromLocalFile( modifiedUrlString );
 
  225  const QVariantMap geometry = json.value( QStringLiteral( 
"geometry" ) ).toMap();
 
  226  const QVariantMap location = geometry.value( QStringLiteral( 
"location" ) ).toMap();
 
  227  const double latitude = location.value( QStringLiteral( 
"lat" ) ).toDouble();
 
  228  const double longitude = location.value( QStringLiteral( 
"lng" ) ).toDouble();
 
  232  QgsGeocoderResult res( json.value( QStringLiteral( 
"formatted_address" ) ).toString(),
 
  236  QVariantMap attributes;
 
  238  if ( json.contains( QStringLiteral( 
"formatted_address" ) ) )
 
  239    attributes.insert( QStringLiteral( 
"formatted_address" ), json.value( QStringLiteral( 
"formatted_address" ) ).toString() );
 
  240  if ( json.contains( QStringLiteral( 
"place_id" ) ) )
 
  241    attributes.insert( QStringLiteral( 
"place_id" ), json.value( QStringLiteral( 
"place_id" ) ).toString() );
 
  242  if ( geometry.contains( QStringLiteral( 
"location_type" ) ) )
 
  243    attributes.insert( QStringLiteral( 
"location_type" ), geometry.value( QStringLiteral( 
"location_type" ) ).toString() );
 
  245  const QVariantList components = json.value( QStringLiteral( 
"address_components" ) ).toList();
 
  246  for ( 
const QVariant &component : components )
 
  248    const QVariantMap componentMap = component.toMap();
 
  249    const QStringList types = componentMap.value( QStringLiteral( 
"types" ) ).toStringList();
 
  251    for ( 
const QString &t :
 
  253            QStringLiteral( 
"street_number" ),
 
  254            QStringLiteral( 
"route" ),
 
  255            QStringLiteral( 
"locality" ),
 
  256            QStringLiteral( 
"administrative_area_level_2" ),
 
  257            QStringLiteral( 
"administrative_area_level_1" ),
 
  258            QStringLiteral( 
"country" ),
 
  259            QStringLiteral( 
"postal_code" )
 
  262      if ( types.contains( t ) )
 
  264        attributes.insert( t, componentMap.value( QStringLiteral( 
"long_name" ) ).toString() );
 
  265        if ( t == QLatin1String( 
"administrative_area_level_1" ) )
 
  266          res.
setGroup( componentMap.value( QStringLiteral( 
"long_name" ) ).toString() );
 
  271  if ( geometry.contains( QStringLiteral( 
"viewport" ) ) )
 
  273    const QVariantMap viewport = geometry.value( QStringLiteral( 
"viewport" ) ).toMap();
 
  274    const QVariantMap northEast = viewport.value( QStringLiteral( 
"northeast" ) ).toMap();
 
  275    const QVariantMap southWest = viewport.value( QStringLiteral( 
"southwest" ) ).toMap();
 
  277                                   southWest.value( QStringLiteral( 
"lat" ) ).toDouble(),
 
  278                                   northEast.value( QStringLiteral( 
"lng" ) ).toDouble(),
 
  279                                   northEast.value( QStringLiteral( 
"lat" ) ).toDouble()
 
  289  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...
 
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "get" operation on the specified request.
 
QString errorMessage() const
Returns the error message string, after a get(), post(), head() or put() request has been made.
 
@ 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...
 
This class 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, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
 
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) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
 
static QgsGeometry fromPointXY(const QgsPointXY &point) SIP_HOLDGIL
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.
 
A geocoder which uses the Google Map geocoding API to retrieve results.
 
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.
 
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 class to represent a 2D point.
 
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
 
void unlock()
Unlocks the lock.
 
void changeMode(Mode mode)
Change the mode of the lock to mode.
 
A rectangle specified with double values.
 
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
 
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
 
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
 
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
 
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
 
QMap< QUrl, QList< QgsGeocoderResult > > CachedGeocodeResult
 
#define QgsDebugMsgLevel(str, level)
 
#define QgsDebugError(str)
 
#define QgsSetRequestInitiatorClass(request, _class)