Skip to content

Commit

Permalink
Add optional viewport property for geocoder results
Browse files Browse the repository at this point in the history
Allows specifying an optional recommended viewport bounds for
displaying the geocode result (e.g. the canvas extent to zoom
to for showing the results)
  • Loading branch information
nyalldawson committed Nov 3, 2020
1 parent e70caeb commit cbaeb99
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 3 deletions.
26 changes: 26 additions & 0 deletions python/core/auto_generated/geocoding/qgsgeocoderresult.sip.in
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/core/geocoding/qgsabstractgeocoderlocatorfilter.cpp
Expand Up @@ -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;
}

Expand All @@ -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 );
Expand Down
27 changes: 27 additions & 0 deletions src/core/geocoding/qgsgeocoderresult.h
Expand Up @@ -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.
Expand All @@ -133,6 +159,7 @@ class CORE_EXPORT QgsGeocoderResult
QString mIdentifier;
QgsGeometry mGeometry;
QgsCoordinateReferenceSystem mCrs;
QgsRectangle mViewport;
QVariantMap mAdditionalAttributes;

};
Expand Down
12 changes: 12 additions & 0 deletions src/core/geocoding/qgsgooglemapsgeocoder.cpp
Expand Up @@ -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;
}
Expand Down
13 changes: 11 additions & 2 deletions src/gui/qgsgeocoderlocatorfilter.cpp
Expand Up @@ -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 );
Expand Down
7 changes: 6 additions & 1 deletion tests/src/python/test_qgsgeocoderlocatorfilter.py
Expand Up @@ -22,7 +22,8 @@
QgsCoordinateReferenceSystem,
QgsLocatorContext,
QgsFeedback,
QgsGeocoderContext
QgsGeocoderContext,
QgsRectangle
)
from qgis.gui import (
QgsMapCanvas,
Expand Down Expand Up @@ -55,6 +56,7 @@ def geocodeString(self, string, context, feedback):
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 []
Expand Down Expand Up @@ -93,6 +95,7 @@ def test_geocode(self):
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)
Expand All @@ -105,12 +108,14 @@ def test_geocode(self):
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__':
Expand Down
11 changes: 11 additions & 0 deletions tests/src/python/test_qgsgooglemapsgeocoder.py
Expand Up @@ -144,6 +144,16 @@ def test_json_to_result(self):
"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"]
Expand All @@ -159,6 +169,7 @@ def test_json_to_result(self):
'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')
Expand Down

0 comments on commit cbaeb99

Please sign in to comment.