Skip to content
Permalink
Browse files

Fix search tolerance when doing identification in 3D map view

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 2da705864bf539d96cfd34feef149bd3419d1e84
@@ -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

};
@@ -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();
@@ -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();
@@ -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()
@@ -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()
@@ -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
@@ -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();
@@ -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
@@ -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 );
@@ -238,6 +259,8 @@ class GUI_EXPORT QgsMapToolIdentify : public QgsMapTool
QgsRectangle mLastExtent;

int mCoordinatePrecision;

double mOverrideCanvasSearchRadius = -1;
};

Q_DECLARE_OPERATORS_FOR_FLAGS( QgsMapToolIdentify::LayerType )

0 comments on commit 2da7058

Please sign in to comment.
You can’t perform that action at this time.