Skip to content

Commit

Permalink
Fix search tolerance when doing identification in 3D map view
Browse files Browse the repository at this point in the history
Until now the identification from 3D map view used tolerance based
on the current view of the main 2D map canvas - that was giving often
unexpected results if the 2D map canvas had significantly different
zoom level from the 3D map view.
  • Loading branch information
wonder-sk committed Sep 19, 2018
1 parent e3f63d8 commit 2da7058
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 4 deletions.
25 changes: 25 additions & 0 deletions python/gui/auto_generated/qgsmaptoolidentify.sip.in
Expand Up @@ -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

};
Expand Down
14 changes: 14 additions & 0 deletions src/3d/qgs3dmapscene.cpp
Expand Up @@ -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();
Expand Down
6 changes: 6 additions & 0 deletions src/3d/qgs3dmapscene.h
Expand Up @@ -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();
Expand Down
9 changes: 8 additions & 1 deletion src/app/3d/qgs3dmaptoolidentify.cpp
Expand Up @@ -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()
Expand Down
4 changes: 3 additions & 1 deletion src/app/qgsmaptoolidentifyaction.cpp
Expand Up @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion src/app/qgsmaptoolidentifyaction.h
Expand Up @@ -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
Expand Down
12 changes: 11 additions & 1 deletion src/gui/qgsmaptoolidentify.cpp
Expand Up @@ -179,6 +179,16 @@ QList<QgsMapToolIdentify::IdentifyResult> QgsMapToolIdentify::identify( const Qg
return results;
}

void QgsMapToolIdentify::setCanvasPropertiesOverrides( double searchRadiusMapUnits )
{
mOverrideCanvasSearchRadius = searchRadiusMapUnits;
}

void QgsMapToolIdentify::restoreCanvasPropertiesOverrides()
{
mOverrideCanvasSearchRadius = -1;
}

void QgsMapToolIdentify::activate()
{
QgsMapTool::activate();
Expand Down Expand Up @@ -270,7 +280,7 @@ bool QgsMapToolIdentify::identifyVectorLayer( QList<QgsMapToolIdentify::Identify
QgsRectangle r;
if ( isSingleClick )
{
double sr = searchRadiusMU( mCanvas );
double sr = mOverrideCanvasSearchRadius < 0 ? searchRadiusMU( mCanvas ) : mOverrideCanvasSearchRadius;
r = toLayerCoordinates( layer, QgsRectangle( point.x() - sr, point.y() - sr, point.x() + sr, point.y() + sr ) );
}
else
Expand Down
23 changes: 23 additions & 0 deletions src/gui/qgsmaptoolidentify.h
Expand Up @@ -168,6 +168,27 @@ class GUI_EXPORT QgsMapToolIdentify : public QgsMapTool
//! Returns derived attributes map for a clicked point in map coordinates. May be 2D or 3D point.
QMap< QString, QString > 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<QgsMapToolIdentify::IdentifyResult> *results, QgsMapLayer *layer, const QgsGeometry &geometry, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType = AllLayers );
Expand Down Expand Up @@ -238,6 +259,8 @@ class GUI_EXPORT QgsMapToolIdentify : public QgsMapTool
QgsRectangle mLastExtent;

int mCoordinatePrecision;

double mOverrideCanvasSearchRadius = -1;
};

Q_DECLARE_OPERATORS_FOR_FLAGS( QgsMapToolIdentify::LayerType )
Expand Down

0 comments on commit 2da7058

Please sign in to comment.