diff --git a/python/gui/auto_generated/qgsmaptoolidentify.sip.in b/python/gui/auto_generated/qgsmaptoolidentify.sip.in index bf6f4d3536a..f79254c65cc 100644 --- a/python/gui/auto_generated/qgsmaptoolidentify.sip.in +++ b/python/gui/auto_generated/qgsmaptoolidentify.sip.in @@ -161,6 +161,31 @@ Call the right method depending on layer type QMap< QString, QString > derivedAttributesForPoint( const QgsPoint &point ); %Docstring Returns derived attributes map for a clicked point in map coordinates. May be 2D or 3D point. +%End + + void setCanvasPropertiesOverrides( double searchRadiusMapUnits ); +%Docstring +Overrides some map canvas properties inside the map tool for the upcoming identify requests. + +This is useful when the identification is triggered by some other piece of GUI like a 3D map view +and some properties like search radius need to be adjusted so that identification returns correct +results. Currently only search radius may be overridden. + +When the custom identification has finished, restoreCanvasPropertiesOverrides() should +be called to erase any overrides. + +.. seealso:: :py:func:`restoreCanvasProperties` + +.. versionadded:: 3.4 +%End + + void restoreCanvasPropertiesOverrides(); +%Docstring +Clears canvas properties overrides previously set with setCanvasPropertiesOverrides() + +.. seealso:: :py:func:`setCanvasPropertiesOverrides` + +.. versionadded:: 3.4 %End }; diff --git a/src/3d/qgs3dmapscene.cpp b/src/3d/qgs3dmapscene.cpp index 3ba902fb008..5a856d49785 100644 --- a/src/3d/qgs3dmapscene.cpp +++ b/src/3d/qgs3dmapscene.cpp @@ -210,6 +210,20 @@ void Qgs3DMapScene::unregisterPickHandler( Qgs3DMapScenePickHandler *pickHandler } } +float Qgs3DMapScene::worldSpaceError( float epsilon, float distance ) +{ + Qt3DRender::QCamera *camera = mCameraController->camera(); + float fov = camera->fieldOfView(); + QRect rect = mCameraController->viewport(); + float screenSizePx = std::max( rect.width(), rect.height() ); // TODO: is this correct? + + // in qgschunkedentity_p.cpp there is inverse calculation (world space error to screen space error) + // with explanation of the math. + float frustumWidthAtDistance = 2 * distance * tan( fov / 2 ); + float err = frustumWidthAtDistance * epsilon / screenSizePx; + return err; +} + QgsChunkedEntity::SceneState _sceneState( QgsCameraController *cameraController ) { Qt3DRender::QCamera *camera = cameraController->camera(); diff --git a/src/3d/qgs3dmapscene.h b/src/3d/qgs3dmapscene.h index 0de14d13bf2..c8a288d20fc 100644 --- a/src/3d/qgs3dmapscene.h +++ b/src/3d/qgs3dmapscene.h @@ -83,6 +83,12 @@ class _3D_EXPORT Qgs3DMapScene : public Qt3DCore::QEntity //! Unregisters previously registered pick handler. Pick handler is not deleted. Also removes object picker components from 3D entities. void unregisterPickHandler( Qgs3DMapScenePickHandler *pickHandler ); + /** + * Given screen error (in pixels) and distance from camera (in 3D world coordinates), this function + * estimates the error in world space. Takes into account camera's field of view and the screen (3D view) size. + */ + float worldSpaceError( float epsilon, float distance ); + signals: //! Emitted when the current terrain entity is replaced by a new one void terrainEntityChanged(); diff --git a/src/app/3d/qgs3dmaptoolidentify.cpp b/src/app/3d/qgs3dmaptoolidentify.cpp index 3be7e61f828..8f5e856923e 100644 --- a/src/app/3d/qgs3dmaptoolidentify.cpp +++ b/src/app/3d/qgs3dmaptoolidentify.cpp @@ -96,9 +96,16 @@ void Qgs3DMapToolIdentify::onTerrainPicked( Qt3DRender::QPickEvent *event ) QgsGeometry geom = QgsGeometry::fromPointXY( QgsPointXY( mapCoords.x(), mapCoords.y() ) ); + // estimate search radius + Qgs3DMapScene *scene = mCanvas->scene(); + double searchRadiusMM = QgsMapTool::searchRadiusMM(); + double pixelsPerMM = mCanvas->logicalDpiX() / 25.4; + double searchRadiusPx = searchRadiusMM * pixelsPerMM; + double searchRadiusMapUnits = scene->worldSpaceError( searchRadiusPx, event->distance() ); + QgsMapToolIdentifyAction *identifyTool2D = QgisApp::instance()->identifyMapTool(); - identifyTool2D->identifyAndShowResults( geom ); + identifyTool2D->identifyAndShowResults( geom, searchRadiusMapUnits ); } void Qgs3DMapToolIdentify::onTerrainEntityChanged() diff --git a/src/app/qgsmaptoolidentifyaction.cpp b/src/app/qgsmaptoolidentifyaction.cpp index 354f561fb92..481e4a696ab 100644 --- a/src/app/qgsmaptoolidentifyaction.cpp +++ b/src/app/qgsmaptoolidentifyaction.cpp @@ -212,9 +212,11 @@ void QgsMapToolIdentifyAction::deactivate() QgsMapToolIdentify::deactivate(); } -void QgsMapToolIdentifyAction::identifyAndShowResults( const QgsGeometry &geom ) +void QgsMapToolIdentifyAction::identifyAndShowResults( const QgsGeometry &geom, double searchRadiusMapUnits ) { + setCanvasPropertiesOverrides( searchRadiusMapUnits ); mSelectionHandler->setSelectedGeometry( geom ); + restoreCanvasPropertiesOverrides(); } void QgsMapToolIdentifyAction::clearResults() diff --git a/src/app/qgsmaptoolidentifyaction.h b/src/app/qgsmaptoolidentifyaction.h index bf1ffb6b545..c69b166a88f 100644 --- a/src/app/qgsmaptoolidentifyaction.h +++ b/src/app/qgsmaptoolidentifyaction.h @@ -62,7 +62,7 @@ class APP_EXPORT QgsMapToolIdentifyAction : public QgsMapToolIdentify void deactivate() override; //! Triggers map identification of at the given location and outputs results in GUI - void identifyAndShowResults( const QgsGeometry &geom ); + void identifyAndShowResults( const QgsGeometry &geom, double searchRadiusMapUnits ); //! Clears any previous results from the GUI void clearResults(); //! Looks up feature by its ID and outputs the result in GUI diff --git a/src/gui/qgsmaptoolidentify.cpp b/src/gui/qgsmaptoolidentify.cpp index 85abc34b7cd..5e36fb3c86f 100644 --- a/src/gui/qgsmaptoolidentify.cpp +++ b/src/gui/qgsmaptoolidentify.cpp @@ -179,6 +179,16 @@ QList QgsMapToolIdentify::identify( const Qg return results; } +void QgsMapToolIdentify::setCanvasPropertiesOverrides( double searchRadiusMapUnits ) +{ + mOverrideCanvasSearchRadius = searchRadiusMapUnits; +} + +void QgsMapToolIdentify::restoreCanvasPropertiesOverrides() +{ + mOverrideCanvasSearchRadius = -1; +} + void QgsMapToolIdentify::activate() { QgsMapTool::activate(); @@ -270,7 +280,7 @@ bool QgsMapToolIdentify::identifyVectorLayer( QList derivedAttributesForPoint( const QgsPoint &point ); + /** + * Overrides some map canvas properties inside the map tool for the upcoming identify requests. + * + * This is useful when the identification is triggered by some other piece of GUI like a 3D map view + * and some properties like search radius need to be adjusted so that identification returns correct + * results. Currently only search radius may be overridden. + * + * When the custom identification has finished, restoreCanvasPropertiesOverrides() should + * be called to erase any overrides. + * \see restoreCanvasProperties() + * \since QGIS 3.4 + */ + void setCanvasPropertiesOverrides( double searchRadiusMapUnits ); + + /** + * Clears canvas properties overrides previously set with setCanvasPropertiesOverrides() + * \see setCanvasPropertiesOverrides() + * \since QGIS 3.4 + */ + void restoreCanvasPropertiesOverrides(); + private: bool identifyLayer( QList *results, QgsMapLayer *layer, const QgsGeometry &geometry, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType = AllLayers ); @@ -238,6 +259,8 @@ class GUI_EXPORT QgsMapToolIdentify : public QgsMapTool QgsRectangle mLastExtent; int mCoordinatePrecision; + + double mOverrideCanvasSearchRadius = -1; }; Q_DECLARE_OPERATORS_FOR_FLAGS( QgsMapToolIdentify::LayerType )