25 #include <QNetworkRequest> 
   26 #include <QJsonDocument> 
   27 #include <QJsonObject> 
   29 QReadWriteLock 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       QgsDebugMsg( 
"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_" ) );
 
  187     QgsDebugMsg( QStringLiteral( 
"Get %1" ).arg( modifiedUrlString ) );
 
  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     QgsDebugMsg( QStringLiteral( 
"Get %1 (after laundering)" ).arg( modifiedUrlString ) );
 
  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;