Add optional viewport property for geocoder results

Allows specifying an optional recommended viewport bounds for
displaying the geocode result (e.g. the canvas extent to zoom
to for showing the results)
This commit is contained in:
Nyall Dawson 2020-11-03 08:50:48 +10:00
parent e70caeb3e1
commit cbaeb99a72
7 changed files with 95 additions and 3 deletions

View File

@ -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;

View File

@ -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 );

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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 );

View File

@ -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__':

View File

@ -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')