24 #include <QNetworkRequest> 
   25 #include <QJsonDocument> 
   26 #include <QJsonObject> 
   28 QReadWriteLock QgsGoogleMapsGeocoder::sMutex;
 
   37   , mRegion( regionBias )
 
   38   , mEndpoint( QStringLiteral( "https:
 
   51   fields.
append( 
QgsField( QStringLiteral( 
"location_type" ), QVariant::String ) );
 
   52   fields.
append( 
QgsField( QStringLiteral( 
"formatted_address" ), QVariant::String ) );
 
   53   fields.
append( 
QgsField( QStringLiteral( 
"place_id" ), QVariant::String ) );
 
   56   fields.
append( 
QgsField( QStringLiteral( 
"street_number" ), QVariant::String ) );
 
   57   fields.
append( 
QgsField( QStringLiteral( 
"route" ), QVariant::String ) );
 
   58   fields.
append( 
QgsField( QStringLiteral( 
"locality" ), QVariant::String ) );
 
   59   fields.
append( 
QgsField( QStringLiteral( 
"administrative_area_level_2" ), QVariant::String ) );
 
   60   fields.
append( 
QgsField( QStringLiteral( 
"administrative_area_level_1" ), QVariant::String ) );
 
   61   fields.
append( 
QgsField( QStringLiteral( 
"country" ), QVariant::String ) );
 
   62   fields.
append( 
QgsField( QStringLiteral( 
"postal_code" ), QVariant::String ) );
 
   85       QgsDebugMsg( 
"Could not transform geocode bounds to WGS84" );
 
   92   auto it = sCachedResults()->constFind( url );
 
   93   if ( it != sCachedResults()->constEnd() )
 
   99   QNetworkRequest request( url );
 
  111   QJsonDocument doc = QJsonDocument::fromJson( newReq.
reply().
content(), &err );
 
  116   const QVariantMap res = doc.object().toVariantMap();
 
  117   const QString status = res.value( QStringLiteral( 
"status" ) ).toString();
 
  118   if ( status.isEmpty() || !res.contains( QStringLiteral( 
"results" ) ) )
 
  120     return QList<QgsGeocoderResult>();
 
  123   if ( res.contains( QLatin1String( 
"error_message" ) ) )
 
  128   if ( status == QLatin1String( 
"REQUEST_DENIED" ) || status == QLatin1String( 
"OVER_QUERY_LIMIT" ) )
 
  132   if ( status != QLatin1String( 
"OK" ) && status != QLatin1String( 
"ZERO_RESULTS" ) )
 
  140   const QVariantList results = res.value( QStringLiteral( 
"results" ) ).toList();
 
  141   if ( results.empty() )
 
  143     sCachedResults()->insert( url, QList<QgsGeocoderResult>() );
 
  144     return QList<QgsGeocoderResult>();
 
  147   QList< QgsGeocoderResult > matches;
 
  148   matches.reserve( results.size( ) );
 
  149   for ( 
const QVariant &result : results )
 
  153   sCachedResults()->insert( url, matches );
 
  160   QUrl res( mEndpoint );
 
  164     query.addQueryItem( QStringLiteral( 
"bounds" ), QStringLiteral( 
"%1,%2|%3,%4" ).arg( bounds.
yMinimum() )
 
  169   if ( !mRegion.isEmpty() )
 
  171     query.addQueryItem( QStringLiteral( 
"region" ), mRegion.toLower() );
 
  173   query.addQueryItem( QStringLiteral( 
"sensor" ), QStringLiteral( 
"false" ) );
 
  174   query.addQueryItem( QStringLiteral( 
"address" ), address );
 
  175   query.addQueryItem( QStringLiteral( 
"key" ), mApiKey );
 
  176   res.setQuery( query );
 
  179   if ( res.toString().contains( QLatin1String( 
"fake_qgis_http_endpoint" ) ) )
 
  182     QString modifiedUrlString = res.toString();
 
  184     modifiedUrlString = QUrl::fromPercentEncoding( modifiedUrlString.toUtf8() );
 
  185     modifiedUrlString.replace( QLatin1String( 
"fake_qgis_http_endpoint/" ), QLatin1String( 
"fake_qgis_http_endpoint_" ) );
 
  186     QgsDebugMsg( QStringLiteral( 
"Get %1" ).arg( modifiedUrlString ) );
 
  187     modifiedUrlString = modifiedUrlString.mid( QStringLiteral( 
"http://" ).size() );
 
  188     QString args = modifiedUrlString.mid( modifiedUrlString.indexOf( 
'?' ) );
 
  189     if ( modifiedUrlString.size() > 150 )
 
  191       args = QCryptographicHash::hash( args.toUtf8(), QCryptographicHash::Md5 ).toHex();
 
  195       args.replace( QLatin1String( 
"?" ), QLatin1String( 
"_" ) );
 
  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( 
"\n" ), QLatin1String( 
"_" ) );
 
  209     if ( modifiedUrlString[1] == 
'/' )
 
  211       modifiedUrlString = modifiedUrlString[0] + 
":/" + modifiedUrlString.mid( 2 );
 
  214     modifiedUrlString = modifiedUrlString.mid( 0, modifiedUrlString.indexOf( 
'?' ) ) + args;
 
  215     QgsDebugMsg( QStringLiteral( 
"Get %1 (after laundering)" ).arg( modifiedUrlString ) );
 
  216     res = QUrl::fromLocalFile( modifiedUrlString );
 
  224   const QVariantMap geometry = json.value( QStringLiteral( 
"geometry" ) ).toMap();
 
  225   const QVariantMap location = geometry.value( QStringLiteral( 
"location" ) ).toMap();
 
  226   const double latitude = location.value( QStringLiteral( 
"lat" ) ).toDouble();
 
  227   const double longitude = location.value( QStringLiteral( 
"lng" ) ).toDouble();
 
  231   QgsGeocoderResult res( json.value( QStringLiteral( 
"formatted_address" ) ).toString(),
 
  235   QVariantMap attributes;
 
  237   if ( json.contains( QStringLiteral( 
"formatted_address" ) ) )
 
  238     attributes.insert( QStringLiteral( 
"formatted_address" ), json.value( QStringLiteral( 
"formatted_address" ) ).toString() );
 
  239   if ( json.contains( QStringLiteral( 
"place_id" ) ) )
 
  240     attributes.insert( QStringLiteral( 
"place_id" ), json.value( QStringLiteral( 
"place_id" ) ).toString() );
 
  241   if ( geometry.contains( QStringLiteral( 
"location_type" ) ) )
 
  242     attributes.insert( QStringLiteral( 
"location_type" ), geometry.value( QStringLiteral( 
"location_type" ) ).toString() );
 
  244   const QVariantList components = json.value( QStringLiteral( 
"address_components" ) ).toList();
 
  245   for ( 
const QVariant &component : components )
 
  247     const QVariantMap componentMap = component.toMap();
 
  248     const QStringList types = componentMap.value( QStringLiteral( 
"types" ) ).toStringList();
 
  250     for ( 
const QString &t :
 
  252             QStringLiteral( 
"street_number" ),
 
  253             QStringLiteral( 
"route" ),
 
  254             QStringLiteral( 
"locality" ),
 
  255             QStringLiteral( 
"administrative_area_level_2" ),
 
  256             QStringLiteral( 
"administrative_area_level_1" ),
 
  257             QStringLiteral( 
"country" ),
 
  258             QStringLiteral( 
"postal_code" )
 
  261       if ( types.contains( t ) )
 
  263         attributes.insert( t, componentMap.value( QStringLiteral( 
"long_name" ) ).toString() );
 
  264         if ( t == QLatin1String( 
"administrative_area_level_1" ) )
 
  265           res.
setGroup( componentMap.value( QStringLiteral( 
"long_name" ) ).toString() );
 
  270   if ( geometry.contains( QStringLiteral( 
"viewport" ) ) )
 
  272     const QVariantMap viewport = geometry.value( QStringLiteral( 
"viewport" ) ).toMap();
 
  273     const QVariantMap northEast = viewport.value( QStringLiteral( 
"northeast" ) ).toMap();
 
  274     const QVariantMap southWest = viewport.value( QStringLiteral( 
"southwest" ) ).toMap();
 
  276                                    southWest.value( QStringLiteral( 
"lat" ) ).toDouble(),
 
  277                                    northEast.value( QStringLiteral( 
"lng" ) ).toDouble(),
 
  278                                    northEast.value( QStringLiteral( 
"lat" ) ).toDouble()
 
  288   mEndpoint = endpoint;
 
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() or post() request has been made.
@ NoError
No error was encountered.
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get() or post() request has been made.
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.
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...
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
A geocoder which uses the Google Map geocoding API to retrieve results.
QgsWkbTypes::Type wkbType() const override
Returns the WKB type of geometries returned by the geocoder.
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.
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()).
Type
The WKB type describes the number of dimensions a geometry has.
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QMap< QUrl, QList< QgsGeocoderResult > > CachedGeocodeResult
#define QgsSetRequestInitiatorClass(request, _class)