diff --git a/python/core/auto_generated/geocoding/qgsgeocoderresult.sip.in b/python/core/auto_generated/geocoding/qgsgeocoderresult.sip.in index 6e120ad6e18..c2e584816dd 100644 --- a/python/core/auto_generated/geocoding/qgsgeocoderresult.sip.in +++ b/python/core/auto_generated/geocoding/qgsgeocoderresult.sip.in @@ -99,6 +99,32 @@ Sets the coordinate reference system for the calculated :py:func:`~QgsGeocoderRe .. seealso:: :py:func:`crs` .. seealso:: :py:func:`geometry` +%End + + QgsRectangle viewport() const; +%Docstring +Returns the suggested viewport for the result, which reflects a recommended +map extent for displaying the result. + +This is an optional property, and will return a null rectangle if a recommended viewport +is not available (or not appropriate). + +The viewport CRS will match the CRS of :py:func:`~QgsGeocoderResult.geometry`, and can be retrieved via the :py:func:`~QgsGeocoderResult.crs` method. + +.. seealso:: :py:func:`setViewport` +%End + + void setViewport( const QgsRectangle &viewport ); +%Docstring +Sets the suggested ``viewport`` for the result, which reflects a recommended +map extent for displaying the result. + +This is an optional property, and can be set to a null rectangle if a recommended viewport +is not available (or not appropriate). + +The viewport CRS must match the CRS of geometry()d. + +.. seealso:: :py:func:`viewport` %End QVariantMap additionalAttributes() const; diff --git a/src/core/geocoding/qgsabstractgeocoderlocatorfilter.cpp b/src/core/geocoding/qgsabstractgeocoderlocatorfilter.cpp index e6a43b0505d..eb709885165 100644 --- a/src/core/geocoding/qgsabstractgeocoderlocatorfilter.cpp +++ b/src/core/geocoding/qgsabstractgeocoderlocatorfilter.cpp @@ -76,6 +76,7 @@ QgsGeocoderResult QgsAbstractGeocoderLocatorFilter::locatorResultToGeocoderResul attrs.value( QStringLiteral( "geom" ) ).value< QgsGeometry >(), attrs.value( QStringLiteral( "crs" ) ).value< QgsCoordinateReferenceSystem >() ); geocodeResult.setAdditionalAttributes( attrs.value( QStringLiteral( "attributes" ) ).toMap() ); + geocodeResult.setViewport( attrs.value( QStringLiteral( "viewport" ) ).value< QgsRectangle >() ); return geocodeResult; } @@ -84,6 +85,7 @@ QgsLocatorResult QgsAbstractGeocoderLocatorFilter::geocoderResultToLocatorResult QVariantMap attrs; attrs.insert( QStringLiteral( "identifier" ), result.identifier() ); attrs.insert( QStringLiteral( "geom" ), result.geometry() ); + attrs.insert( QStringLiteral( "viewport" ), result.viewport() ); attrs.insert( QStringLiteral( "crs" ), result.crs() ); attrs.insert( QStringLiteral( "attributes" ), result.additionalAttributes() ); return QgsLocatorResult( this, result.identifier(), attrs ); diff --git a/src/core/geocoding/qgsgeocoderresult.h b/src/core/geocoding/qgsgeocoderresult.h index 6bfe240ba22..500fdee337a 100644 --- a/src/core/geocoding/qgsgeocoderresult.h +++ b/src/core/geocoding/qgsgeocoderresult.h @@ -107,6 +107,32 @@ class CORE_EXPORT QgsGeocoderResult */ void setCrs( const QgsCoordinateReferenceSystem &crs ) { mCrs = crs; } + /** + * Returns the suggested viewport for the result, which reflects a recommended + * map extent for displaying the result. + * + * This is an optional property, and will return a null rectangle if a recommended viewport + * is not available (or not appropriate). + * + * The viewport CRS will match the CRS of geometry(), and can be retrieved via the crs() method. + * + * \see setViewport() + */ + QgsRectangle viewport() const { return mViewport; } + + /** + * Sets the suggested \a viewport for the result, which reflects a recommended + * map extent for displaying the result. + * + * This is an optional property, and can be set to a null rectangle if a recommended viewport + * is not available (or not appropriate). + * + * The viewport CRS must match the CRS of geometry()d. + * + * \see viewport() + */ + void setViewport( const QgsRectangle &viewport ) { mViewport = viewport; } + /** * Contains additional attributes generated during the geocode, * which may be added to features being geocoded. @@ -133,6 +159,7 @@ class CORE_EXPORT QgsGeocoderResult QString mIdentifier; QgsGeometry mGeometry; QgsCoordinateReferenceSystem mCrs; + QgsRectangle mViewport; QVariantMap mAdditionalAttributes; }; diff --git a/src/core/geocoding/qgsgooglemapsgeocoder.cpp b/src/core/geocoding/qgsgooglemapsgeocoder.cpp index cb349a0c703..ee346b594de 100644 --- a/src/core/geocoding/qgsgooglemapsgeocoder.cpp +++ b/src/core/geocoding/qgsgooglemapsgeocoder.cpp @@ -243,6 +243,18 @@ QgsGeocoderResult QgsGoogleMapsGeocoder::jsonToResult( const QVariantMap &json ) } } + if ( geometry.contains( QStringLiteral( "viewport" ) ) ) + { + const QVariantMap viewport = geometry.value( QStringLiteral( "viewport" ) ).toMap(); + const QVariantMap northEast = viewport.value( QStringLiteral( "northeast" ) ).toMap(); + const QVariantMap southWest = viewport.value( QStringLiteral( "southwest" ) ).toMap(); + res.setViewport( QgsRectangle( southWest.value( QStringLiteral( "lng" ) ).toDouble(), + southWest.value( QStringLiteral( "lat" ) ).toDouble(), + northEast.value( QStringLiteral( "lng" ) ).toDouble(), + northEast.value( QStringLiteral( "lat" ) ).toDouble() + ) ); + } + res.setAdditionalAttributes( attributes ); return res; } diff --git a/src/gui/qgsgeocoderlocatorfilter.cpp b/src/gui/qgsgeocoderlocatorfilter.cpp index 898bb79ebc5..53deb9c0e84 100644 --- a/src/gui/qgsgeocoderlocatorfilter.cpp +++ b/src/gui/qgsgeocoderlocatorfilter.cpp @@ -34,10 +34,19 @@ void QgsGeocoderLocatorFilter::handleGeocodeResult( const QgsGeocoderResult &res { QgsCoordinateTransform ct( result.crs(), mCanvas->mapSettings().destinationCrs(), mCanvas->mapSettings().transformContext() ); QgsGeometry g = result.geometry(); + const QgsRectangle viewport = result.viewport(); try { - g.transform( ct ); - QgsRectangle bounds = g.boundingBox(); + QgsRectangle bounds; + if ( viewport.isNull() ) + { + g.transform( ct ); + bounds = g.boundingBox(); + } + else + { + bounds = ct.transformBoundingBox( viewport ); + } mCanvas->zoomToFeatureExtent( bounds ); mCanvas->flashGeometries( QList< QgsGeometry >() << g ); diff --git a/tests/src/python/test_qgsgeocoderlocatorfilter.py b/tests/src/python/test_qgsgeocoderlocatorfilter.py index 27e25fa98be..b8481b4acd9 100644 --- a/tests/src/python/test_qgsgeocoderlocatorfilter.py +++ b/tests/src/python/test_qgsgeocoderlocatorfilter.py @@ -22,7 +22,8 @@ from qgis.core import ( QgsCoordinateReferenceSystem, QgsLocatorContext, QgsFeedback, - QgsGeocoderContext + QgsGeocoderContext, + QgsRectangle ) from qgis.gui import ( QgsMapCanvas, @@ -55,6 +56,7 @@ class TestGeocoder(QgsGeocoderInterface): result2 = QgsGeocoderResult('res 2', QgsGeometry.fromPointXY(QgsPointXY(13, 14)), QgsCoordinateReferenceSystem('EPSG:3857')) result2.setAdditionalAttributes({'d': 456}) + result2.setViewport(QgsRectangle(1, 2, 3, 4)) return [result1, result2] return [] @@ -93,6 +95,7 @@ class TestQgsGeocoderLocatorFilter(unittest.TestCase): self.assertEqual(geocode_result.geometry().asWkt(), 'Point (1 2)') self.assertEqual(geocode_result.crs().authid(), 'EPSG:4326') self.assertEqual(geocode_result.additionalAttributes(), {'b': 123, 'c': 'xyz'}) + self.assertTrue(geocode_result.viewport().isNull()) # two possible results filter.fetchResults('b', context, feedback) @@ -105,12 +108,14 @@ class TestQgsGeocoderLocatorFilter(unittest.TestCase): self.assertEqual(geocode_result.geometry().asWkt(), 'Point (11 12)') self.assertEqual(geocode_result.crs().authid(), 'EPSG:4326') self.assertEqual(geocode_result.additionalAttributes(), {'b': 123, 'c': 'xyz'}) + self.assertTrue(geocode_result.viewport().isNull()) self.assertEqual(res2.displayString, 'res 2') geocode_result = filter.locatorResultToGeocoderResult(res2) self.assertEqual(geocode_result.identifier(), 'res 2') self.assertEqual(geocode_result.geometry().asWkt(), 'Point (13 14)') self.assertEqual(geocode_result.crs().authid(), 'EPSG:3857') self.assertEqual(geocode_result.additionalAttributes(), {'d': 456}) + self.assertEqual(geocode_result.viewport(), QgsRectangle(1, 2, 3, 4)) if __name__ == '__main__': diff --git a/tests/src/python/test_qgsgooglemapsgeocoder.py b/tests/src/python/test_qgsgooglemapsgeocoder.py index 5c029e82979..1d6fa72c70e 100644 --- a/tests/src/python/test_qgsgooglemapsgeocoder.py +++ b/tests/src/python/test_qgsgooglemapsgeocoder.py @@ -144,6 +144,16 @@ class TestQgsGeocoderLocatorFilter(unittest.TestCase): "lng": -83.55521200000001 }, "location_type": "APPROXIMATE", + "viewport": { + "northeast": { + "lat": 42.1, + "lng": -87.7 + }, + "southwest": { + "lat": 42.0, + "lng": -87.7 + } + } }, "place_id": "ChIJeU4e_C2HO4gRRcM6RZ_IPHw", "types": ["locality", "political"] @@ -159,6 +169,7 @@ class TestQgsGeocoderLocatorFilter(unittest.TestCase): 'locality': 'Mountain View', 'location_type': 'APPROXIMATE', 'place_id': 'ChIJeU4e_C2HO4gRRcM6RZ_IPHw', 'postal_code': '94043', 'route': 'Amphitheatre Pkwy', 'street_number': '1600'}) + self.assertEqual(res.viewport(), QgsRectangle(-87.7, 42, -87.7, 42.1)) def test_geocode(self): geocoder = QgsGoogleMapsGeocoder('my key')