25 #include <QNetworkRequest> 
   26 #include <QJsonDocument> 
   29 QMutex QgsNominatimGeocoder::sMutex;
 
   32 qint64 QgsNominatimGeocoder::sLastRequestTimestamp = 0;
 
   36   , mCountryCodes( countryCodes )
 
   37   , mEndpoint( QStringLiteral( 
"https://nominatim.qgis.org/search" ) )
 
   51   fields.
append( 
QgsField( QStringLiteral( 
"osm_type" ), QVariant::String ) );
 
   52   fields.
append( 
QgsField( QStringLiteral( 
"display_name" ), QVariant::String ) );
 
   53   fields.
append( 
QgsField( QStringLiteral( 
"place_id" ), QVariant::String ) );
 
   54   fields.
append( 
QgsField( QStringLiteral( 
"class" ), QVariant::String ) );
 
   55   fields.
append( 
QgsField( QStringLiteral( 
"type" ), QVariant::String ) );
 
   56   fields.
append( 
QgsField( QStringLiteral( 
"road" ), QVariant::String ) );
 
   57   fields.
append( 
QgsField( QStringLiteral( 
"village" ), QVariant::String ) );
 
   58   fields.
append( 
QgsField( QStringLiteral( 
"city_district" ), QVariant::String ) );
 
   59   fields.
append( 
QgsField( QStringLiteral( 
"town" ), QVariant::String ) );
 
   60   fields.
append( 
QgsField( QStringLiteral( 
"city" ), QVariant::String ) );
 
   61   fields.
append( 
QgsField( QStringLiteral( 
"state" ), QVariant::String ) );
 
   62   fields.
append( 
QgsField( QStringLiteral( 
"country" ), QVariant::String ) );
 
   63   fields.
append( 
QgsField( QStringLiteral( 
"postcode" ), QVariant::String ) );
 
   86       QgsDebugMsg( 
"Could not transform geocode bounds to WGS84" );
 
   92   QMutexLocker locker( &sMutex );
 
   93   auto it = sCachedResults()->constFind( url );
 
   94   if ( it != sCachedResults()->constEnd() )
 
   99   while ( QDateTime::currentMSecsSinceEpoch() - sLastRequestTimestamp < 1000 / mRequestsPerSecond )
 
  101     QThread::msleep( 50 );
 
  103       return QList<QgsGeocoderResult>();
 
  106   QNetworkRequest request( url );
 
  112   sLastRequestTimestamp = QDateTime::currentMSecsSinceEpoch();
 
  121   QJsonDocument doc = QJsonDocument::fromJson( newReq.
reply().
content(), &err );
 
  127   const QVariantList results = doc.array().toVariantList();
 
  128   if ( results.isEmpty() )
 
  130     sCachedResults()->insert( url, QList<QgsGeocoderResult>() );
 
  131     return QList<QgsGeocoderResult>();
 
  134   QList< QgsGeocoderResult > matches;
 
  135   matches.reserve( results.size() );
 
  136   for ( 
const QVariant &result : results )
 
  141   sCachedResults()->insert( url, matches );
 
  148   QUrl res( mEndpoint );
 
  150   query.addQueryItem( QStringLiteral( 
"format" ), QStringLiteral( 
"json" ) );
 
  151   query.addQueryItem( QStringLiteral( 
"addressdetails" ), QStringLiteral( 
"1" ) );
 
  154     query.addQueryItem( QStringLiteral( 
"viewbox" ), QStringLiteral( 
"%1,%2,%3,%4" ).arg( bounds.
xMinimum() )
 
  159   if ( !mCountryCodes.isEmpty() )
 
  161     query.addQueryItem( QStringLiteral( 
"countrycodes" ), mCountryCodes.toLower() );
 
  163   query.addQueryItem( QStringLiteral( 
"q" ), address );
 
  164   res.setQuery( query );
 
  171   const double latitude = json.value( QStringLiteral( 
"lat" ) ).toDouble();
 
  172   const double longitude = json.value( QStringLiteral( 
"lon" ) ).toDouble();
 
  176   QgsGeocoderResult res( json.value( QStringLiteral( 
"display_name" ) ).toString(),
 
  180   QVariantMap attributes;
 
  182   if ( json.contains( QStringLiteral( 
"display_name" ) ) )
 
  183     attributes.insert( QStringLiteral( 
"display_name" ), json.value( QStringLiteral( 
"display_name" ) ).toString() );
 
  184   if ( json.contains( QStringLiteral( 
"place_id" ) ) )
 
  185     attributes.insert( QStringLiteral( 
"place_id" ), json.value( QStringLiteral( 
"place_id" ) ).toString() );
 
  186   if ( json.contains( QStringLiteral( 
"osm_type" ) ) )
 
  187     attributes.insert( QStringLiteral( 
"osm_type" ), json.value( QStringLiteral( 
"osm_type" ) ).toString() );
 
  188   if ( json.contains( QStringLiteral( 
"class" ) ) )
 
  189     attributes.insert( QStringLiteral( 
"class" ), json.value( QStringLiteral( 
"class" ) ).toString() );
 
  190   if ( json.contains( QStringLiteral( 
"type" ) ) )
 
  191     attributes.insert( QStringLiteral( 
"type" ), json.value( QStringLiteral( 
"type" ) ).toString() );
 
  193   if ( json.contains( QStringLiteral( 
"address" ) ) )
 
  195     const QVariantMap address_components = json.value( QStringLiteral( 
"address" ) ).toMap();
 
  196     if ( address_components.contains( QStringLiteral( 
"road" ) ) )
 
  197       attributes.insert( QStringLiteral( 
"road" ), address_components.value( QStringLiteral( 
"road" ) ).toString() );
 
  198     if ( address_components.contains( QStringLiteral( 
"village" ) ) )
 
  199       attributes.insert( QStringLiteral( 
"village" ), address_components.value( QStringLiteral( 
"village" ) ).toString() );
 
  200     if ( address_components.contains( QStringLiteral( 
"city_district" ) ) )
 
  201       attributes.insert( QStringLiteral( 
"city_district" ), address_components.value( QStringLiteral( 
"city_district" ) ).toString() );
 
  202     if ( address_components.contains( QStringLiteral( 
"town" ) ) )
 
  203       attributes.insert( QStringLiteral( 
"town" ), address_components.value( QStringLiteral( 
"town" ) ).toString() );
 
  204     if ( address_components.contains( QStringLiteral( 
"city" ) ) )
 
  205       attributes.insert( QStringLiteral( 
"city" ), address_components.value( QStringLiteral( 
"city" ) ).toString() );
 
  206     if ( address_components.contains( QStringLiteral( 
"state" ) ) )
 
  208       attributes.insert( QStringLiteral( 
"state" ), address_components.value( QStringLiteral( 
"state" ) ).toString() );
 
  209       res.
setGroup( address_components.value( QStringLiteral( 
"state" ) ).toString() );
 
  211     if ( address_components.contains( QStringLiteral( 
"country" ) ) )
 
  212       attributes.insert( QStringLiteral( 
"country" ), address_components.value( QStringLiteral( 
"country" ) ).toString() );
 
  213     if ( address_components.contains( QStringLiteral( 
"postcode" ) ) )
 
  214       attributes.insert( QStringLiteral( 
"postcode" ), address_components.value( QStringLiteral( 
"postcode" ) ).toString() );
 
  217   if ( json.contains( QStringLiteral( 
"boundingbox" ) ) )
 
  219     QVariantList boundingBox = json.value( QStringLiteral( 
"boundingbox" ) ).toList();
 
  220     if ( boundingBox.size() == 4 )
 
  222                                      boundingBox.at( 0 ).toDouble(),
 
  223                                      boundingBox.at( 3 ).toDouble(),
 
  224                                      boundingBox.at( 1 ).toDouble() ) );
 
  243   return mCountryCodes;
 
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.
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
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.
QByteArray content() const
Returns the reply content.
QList< QgsGeocoderResult > geocodeString(const QString &string, const QgsGeocoderContext &context, QgsFeedback *feedback=nullptr) const override
Geocodes a string.
QgsGeocoderResult jsonToResult(const QVariantMap &json) const
Converts a JSON result returned from the Nominatim service to a geocoder result object.
QgsNominatimGeocoder(const QString &countryCodes=QString(), const QString &endpoint=QString())
Constructor for QgsNominatimGeocoder.
QString countryCodes() const
Returns the optional region bias which will be used to prioritize results in a certain region.
QString endpoint() const
Returns the API endpoint used for requests.
QgsFields appendedFields() const override
Returns a set of newly created fields which will be appended to existing features during the geocode ...
void setEndpoint(const QString &endpoint)
Sets a specific API endpoint to use for requests.
Flags flags() const override
Returns the geocoder's capability flags.
void setCountryCodes(const QString &countryCodes)
Sets the optional region bias which will be used to prioritize results in a certain region.
QgsWkbTypes::Type wkbType() const override
Returns the WKB type of geometries returned by the geocoder.
QUrl requestUrl(const QString &address, const QgsRectangle &bounds=QgsRectangle()) const
Returns the URL generated for geocoding the specified address.
A class to represent a 2D point.
A rectangle specified with double values.
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right 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)
QMap< QUrl, QList< QgsGeocoderResult > > CachedGeocodeResult