mirror of
https://github.com/qgis/QGIS.git
synced 2025-06-20 00:03:07 -04:00
[FEATURE] 3D identify tool working on 3D entities
Until now the tool only considered terrain. This commit adds support for 3D renderers created from vector layers, so it is possible to correctly identify polygons and linestrings in 3D.
This commit is contained in:
parent
f99b2db674
commit
2085dfa6ae
@ -158,6 +158,11 @@ Call the right method depending on layer type
|
||||
bool identifyRasterLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsRasterLayer *layer, QgsPointXY point, const QgsRectangle &viewExtent, double mapUnitsPerPixel );
|
||||
bool identifyVectorLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorLayer *layer, const QgsPointXY &point );
|
||||
|
||||
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
|
||||
|
||||
};
|
||||
|
||||
QFlags<QgsMapToolIdentify::Type> operator|(QgsMapToolIdentify::Type f1, QFlags<QgsMapToolIdentify::Type> f2);
|
||||
|
@ -56,6 +56,7 @@ SET(QGIS_3D_MOC_HDRS
|
||||
qgscameracontroller.h
|
||||
qgslayoutitem3dmap.h
|
||||
qgsoffscreen3dengine.h
|
||||
qgstessellatedpolygongeometry.h
|
||||
qgswindow3dengine.h
|
||||
|
||||
chunks/qgschunkedentity_p.h
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "qgsaabb.h"
|
||||
#include "qgsabstract3dengine.h"
|
||||
#include "qgs3dmapscenepickhandler.h"
|
||||
#include "qgs3dmapsettings.h"
|
||||
#include "qgs3dutils.h"
|
||||
#include "qgsabstract3drenderer.h"
|
||||
@ -38,6 +39,7 @@
|
||||
#include "qgschunknode_p.h"
|
||||
#include "qgsterrainentity_p.h"
|
||||
#include "qgsterraingenerator.h"
|
||||
#include "qgstessellatedpolygongeometry.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsvectorlayer3drenderer.h"
|
||||
|
||||
@ -177,6 +179,37 @@ int Qgs3DMapScene::terrainPendingJobsCount() const
|
||||
return mTerrain ? mTerrain->pendingJobsCount() : 0;
|
||||
}
|
||||
|
||||
void Qgs3DMapScene::registerPickHandler( Qgs3DMapScenePickHandler *pickHandler )
|
||||
{
|
||||
if ( mPickHandlers.isEmpty() )
|
||||
{
|
||||
// we need to add object pickers
|
||||
for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
|
||||
{
|
||||
Qt3DRender::QObjectPicker *picker = new Qt3DRender::QObjectPicker( entity );
|
||||
entity->addComponent( picker );
|
||||
connect( picker, &Qt3DRender::QObjectPicker::pressed, this, &Qgs3DMapScene::onLayerEntityPickEvent );
|
||||
}
|
||||
}
|
||||
|
||||
mPickHandlers.append( pickHandler );
|
||||
}
|
||||
|
||||
void Qgs3DMapScene::unregisterPickHandler( Qgs3DMapScenePickHandler *pickHandler )
|
||||
{
|
||||
mPickHandlers.removeOne( pickHandler );
|
||||
|
||||
if ( mPickHandlers.isEmpty() )
|
||||
{
|
||||
// we need to remove pickers
|
||||
for ( Qt3DCore::QEntity *entity : mLayerEntities.values() )
|
||||
{
|
||||
Qt3DRender::QObjectPicker *picker = entity->findChild<Qt3DRender::QObjectPicker *>();
|
||||
picker->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QgsChunkedEntity::SceneState _sceneState( QgsCameraController *cameraController )
|
||||
{
|
||||
Qt3DRender::QCamera *camera = cameraController->camera();
|
||||
@ -365,6 +398,54 @@ void Qgs3DMapScene::onBackgroundColorChanged()
|
||||
mEngine->setClearColor( mMap.backgroundColor() );
|
||||
}
|
||||
|
||||
void Qgs3DMapScene::onLayerEntityPickEvent( Qt3DRender::QPickEvent *event )
|
||||
{
|
||||
if ( event->button() != Qt3DRender::QPickEvent::LeftButton )
|
||||
return;
|
||||
|
||||
Qt3DRender::QPickTriangleEvent *triangleEvent = qobject_cast<Qt3DRender::QPickTriangleEvent *>( event );
|
||||
if ( !triangleEvent )
|
||||
return;
|
||||
|
||||
Qt3DRender::QObjectPicker *picker = qobject_cast<Qt3DRender::QObjectPicker *>( sender() );
|
||||
if ( !picker )
|
||||
return;
|
||||
|
||||
Qt3DCore::QEntity *entity = qobject_cast<Qt3DCore::QEntity *>( picker->parent() );
|
||||
if ( !entity )
|
||||
return;
|
||||
|
||||
QgsMapLayer *layer = mLayerEntities.key( entity );
|
||||
if ( !layer )
|
||||
return;
|
||||
|
||||
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
|
||||
if ( !vlayer )
|
||||
return;
|
||||
|
||||
for ( Qgs3DMapScenePickHandler *pickHandler : qgis::as_const( mPickHandlers ) )
|
||||
{
|
||||
// go figure out feature ID from the triangle index
|
||||
QgsFeatureId fid = -1;
|
||||
for ( Qt3DRender::QGeometryRenderer *geomRenderer : entity->findChildren<Qt3DRender::QGeometryRenderer *>() )
|
||||
{
|
||||
// unfortunately we can't access which sub-entity triggered the pick event
|
||||
// so as a temporary workaround let's just ignore the entity with selection
|
||||
// and hope the event was the main entity (QTBUG-58206)
|
||||
if ( geomRenderer->objectName() != "main" )
|
||||
continue;
|
||||
|
||||
if ( QgsTessellatedPolygonGeometry *g = qobject_cast<QgsTessellatedPolygonGeometry *>( geomRenderer->geometry() ) )
|
||||
{
|
||||
fid = g->triangleIndexToFeatureId( triangleEvent->triangleIndex() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
pickHandler->handlePickOnVectorLayer( vlayer, fid, event->worldIntersection() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Qgs3DMapScene::onLayerRenderer3DChanged()
|
||||
{
|
||||
QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
|
||||
@ -425,6 +506,13 @@ void Qgs3DMapScene::addLayerEntity( QgsMapLayer *layer )
|
||||
{
|
||||
newEntity->setParent( this );
|
||||
mLayerEntities.insert( layer, newEntity );
|
||||
|
||||
if ( !mPickHandlers.isEmpty() )
|
||||
{
|
||||
Qt3DRender::QObjectPicker *picker = new Qt3DRender::QObjectPicker( newEntity );
|
||||
newEntity->addComponent( picker );
|
||||
connect( picker, &Qt3DRender::QObjectPicker::pressed, this, &Qgs3DMapScene::onLayerEntityPickEvent );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ namespace Qt3DRender
|
||||
{
|
||||
class QRenderSettings;
|
||||
class QCamera;
|
||||
class QPickEvent;
|
||||
}
|
||||
|
||||
namespace Qt3DLogic
|
||||
@ -39,6 +40,7 @@ namespace Qt3DExtras
|
||||
class QgsAbstract3DEngine;
|
||||
class QgsMapLayer;
|
||||
class QgsCameraController;
|
||||
class Qgs3DMapScenePickHandler;
|
||||
class Qgs3DMapSettings;
|
||||
class QgsTerrainEntity;
|
||||
class QgsChunkedEntity;
|
||||
@ -76,6 +78,11 @@ class _3D_EXPORT Qgs3DMapScene : public Qt3DCore::QEntity
|
||||
//! Returns the current state of the scene
|
||||
SceneState sceneState() const { return mSceneState; }
|
||||
|
||||
//! Registers an object that will get results of pick events on 3D entities. Does not take ownerhip of the pick handler. Adds object picker components to 3D entities.
|
||||
void registerPickHandler( Qgs3DMapScenePickHandler *pickHandler );
|
||||
//! Unregisters previously registered pick handler. Pick handler is not deleted. Also removes object picker components from 3D entities.
|
||||
void unregisterPickHandler( Qgs3DMapScenePickHandler *pickHandler );
|
||||
|
||||
signals:
|
||||
//! Emitted when the current terrain entity is replaced by a new one
|
||||
void terrainEntityChanged();
|
||||
@ -92,6 +99,7 @@ class _3D_EXPORT Qgs3DMapScene : public Qt3DCore::QEntity
|
||||
void onLayersChanged();
|
||||
void createTerrainDeferred();
|
||||
void onBackgroundColorChanged();
|
||||
void onLayerEntityPickEvent( Qt3DRender::QPickEvent *event );
|
||||
|
||||
private:
|
||||
void addLayerEntity( QgsMapLayer *layer );
|
||||
@ -116,6 +124,8 @@ class _3D_EXPORT Qgs3DMapScene : public Qt3DCore::QEntity
|
||||
QMap<QgsMapLayer *, Qt3DCore::QEntity *> mLayerEntities;
|
||||
bool mTerrainUpdateScheduled = false;
|
||||
SceneState mSceneState = Ready;
|
||||
//! List of currently registered pick handlers (used by identify tool)
|
||||
QList<Qgs3DMapScenePickHandler *> mPickHandlers;
|
||||
};
|
||||
|
||||
#endif // QGS3DMAPSCENE_H
|
||||
|
28
src/3d/qgs3dmapscenepickhandler.h
Normal file
28
src/3d/qgs3dmapscenepickhandler.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef QGS3DMAPSCENEPICKHANDLER_H
|
||||
#define QGS3DMAPSCENEPICKHANDLER_H
|
||||
|
||||
#include "qgsfeatureid.h"
|
||||
|
||||
class QVector3D;
|
||||
class QgsVectorLayer;
|
||||
|
||||
/**
|
||||
* \ingroup 3d
|
||||
* Abstract base class for handlers that process pick events from a 3D map scene.
|
||||
* 3D entities in map scene get QObjectPicker components assigned and mouse press events trigger virtual methods
|
||||
* or pick handlers.
|
||||
*
|
||||
* This is used for identify tool to be able to identify entities coming from 3D renderers assigned to map layers.
|
||||
*
|
||||
* \since QGIS 3.4
|
||||
*/
|
||||
class Qgs3DMapScenePickHandler
|
||||
{
|
||||
public:
|
||||
virtual ~Qgs3DMapScenePickHandler() {}
|
||||
|
||||
//! Called when user clicked a 3D entity belonging to a feature of a vector layer
|
||||
virtual void handlePickOnVectorLayer( QgsVectorLayer *vlayer, QgsFeatureId id, const QVector3D &worldIntersection ) = 0;
|
||||
};
|
||||
|
||||
#endif // QGS3DMAPSCENEPICKHANDLER_H
|
@ -56,11 +56,20 @@ QgsTessellatedPolygonGeometry::QgsTessellatedPolygonGeometry( QNode *parent )
|
||||
}
|
||||
}
|
||||
|
||||
void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &polygons, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon )
|
||||
void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &polygons, const QList<QgsFeatureId> &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon )
|
||||
{
|
||||
Q_ASSERT( polygons.count() == featureIds.count() );
|
||||
mTriangleIndexStartingIndices.reserve( polygons.count() );
|
||||
mTriangleIndexFids.reserve( polygons.count() );
|
||||
|
||||
QgsTessellator tessellator( origin.x(), origin.y(), mWithNormals, mInvertNormals, mAddBackFaces );
|
||||
for ( int i = 0; i < polygons.count(); ++i )
|
||||
{
|
||||
Q_ASSERT( tessellator.dataVerticesCount() % 3 == 0 );
|
||||
uint startingTriangleIndex = static_cast<uint>( tessellator.dataVerticesCount() / 3 );
|
||||
mTriangleIndexStartingIndices.append( startingTriangleIndex );
|
||||
mTriangleIndexFids.append( featureIds[i] );
|
||||
|
||||
QgsPolygon *polygon = polygons.at( i );
|
||||
float extr = extrusionHeightPerPolygon.isEmpty() ? extrusionHeight : extrusionHeightPerPolygon.at( i );
|
||||
tessellator.addPolygon( *polygon, extr );
|
||||
@ -76,3 +85,38 @@ void QgsTessellatedPolygonGeometry::setPolygons( const QList<QgsPolygon *> &poly
|
||||
if ( mNormalAttribute )
|
||||
mNormalAttribute->setCount( nVerts );
|
||||
}
|
||||
|
||||
|
||||
// run binary search on a sorted array, return index i where data[i] <= x < data[i+1]
|
||||
static int binary_search( uint v, const uint *data, int count )
|
||||
{
|
||||
int idx0 = 0;
|
||||
int idx1 = count - 1;
|
||||
|
||||
if ( v < data[0] )
|
||||
return -1; // not in the array
|
||||
|
||||
while ( idx0 != idx1 )
|
||||
{
|
||||
int idxPivot = ( idx0 + idx1 ) / 2;
|
||||
uint pivot = data[idxPivot];
|
||||
if ( pivot <= v )
|
||||
{
|
||||
if ( data[idxPivot + 1] > v )
|
||||
return idxPivot; // we're done!
|
||||
else // continue searching values greater than the pivot
|
||||
idx0 = idxPivot;
|
||||
}
|
||||
else // continue searching values lower than the pivot
|
||||
idx1 = idxPivot;
|
||||
}
|
||||
return idx0;
|
||||
}
|
||||
|
||||
|
||||
QgsFeatureId QgsTessellatedPolygonGeometry::triangleIndexToFeatureId( uint triangleIndex )
|
||||
{
|
||||
int i = binary_search( triangleIndex, mTriangleIndexStartingIndices.constData(), mTriangleIndexStartingIndices.count() );
|
||||
Q_ASSERT( i != -1 );
|
||||
return mTriangleIndexFids[i];
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
#ifndef QGSTESSELLATEDPOLYGONGEOMETRY_H
|
||||
#define QGSTESSELLATEDPOLYGONGEOMETRY_H
|
||||
|
||||
#include "qgsfeatureid.h"
|
||||
#include "qgspolygon.h"
|
||||
|
||||
#include <Qt3DRender/QGeometry>
|
||||
@ -36,6 +37,7 @@ namespace Qt3DRender
|
||||
*/
|
||||
class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
//! Constructor
|
||||
QgsTessellatedPolygonGeometry( QNode *parent = nullptr );
|
||||
@ -58,7 +60,10 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
|
||||
void setAddBackFaces( bool add ) { mAddBackFaces = add; }
|
||||
|
||||
//! Initializes vertex buffer from given polygons. Takes ownership of passed polygon geometries
|
||||
void setPolygons( const QList<QgsPolygon *> &polygons, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon = QList<float>() );
|
||||
void setPolygons( const QList<QgsPolygon *> &polygons, const QList<QgsFeatureId> &featureIds, const QgsPointXY &origin, float extrusionHeight, const QList<float> &extrusionHeightPerPolygon = QList<float>() );
|
||||
|
||||
//! Returns ID of the feature to which given triangle index belongs (used for picking)
|
||||
QgsFeatureId triangleIndexToFeatureId( uint triangleIndex );
|
||||
|
||||
private:
|
||||
|
||||
@ -66,6 +71,9 @@ class QgsTessellatedPolygonGeometry : public Qt3DRender::QGeometry
|
||||
Qt3DRender::QAttribute *mNormalAttribute = nullptr;
|
||||
Qt3DRender::QBuffer *mVertexBuffer = nullptr;
|
||||
|
||||
QVector<QgsFeatureId> mTriangleIndexFids;
|
||||
QVector<uint> mTriangleIndexStartingIndices;
|
||||
|
||||
bool mWithNormals = true;
|
||||
bool mInvertNormals = false;
|
||||
bool mAddBackFaces = false;
|
||||
|
@ -85,6 +85,7 @@ void QgsLine3DSymbolEntity::addEntityForNotSelectedLines( const Qgs3DMapSettings
|
||||
|
||||
// build the entity
|
||||
QgsLine3DSymbolEntityNode *entity = new QgsLine3DSymbolEntityNode( map, layer, symbol, req );
|
||||
entity->findChild<Qt3DRender::QGeometryRenderer *>()->setObjectName( "main" ); // temporary measure to distinguish between "selected" and "main"
|
||||
entity->addComponent( mat );
|
||||
entity->setParent( this );
|
||||
}
|
||||
@ -106,6 +107,7 @@ Qt3DRender::QGeometryRenderer *QgsLine3DSymbolEntityNode::renderer( const Qgs3DM
|
||||
double mitreLimit = 0;
|
||||
|
||||
QList<QgsPolygon *> polygons;
|
||||
QList<QgsFeatureId> fids;
|
||||
QgsFeature f;
|
||||
QgsFeatureIterator fi = layer->getFeatures( request );
|
||||
while ( fi.nextFeature( f ) )
|
||||
@ -129,6 +131,7 @@ Qt3DRender::QGeometryRenderer *QgsLine3DSymbolEntityNode::renderer( const Qgs3DM
|
||||
QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( buffered );
|
||||
Qgs3DUtils::clampAltitudes( polyBuffered, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), map );
|
||||
polygons.append( polyBuffered );
|
||||
fids.append( f.id() );
|
||||
}
|
||||
else if ( QgsWkbTypes::flatType( buffered->wkbType() ) == QgsWkbTypes::MultiPolygon )
|
||||
{
|
||||
@ -140,13 +143,14 @@ Qt3DRender::QGeometryRenderer *QgsLine3DSymbolEntityNode::renderer( const Qgs3DM
|
||||
QgsPolygon *polyBuffered = static_cast<QgsPolygon *>( partBuffered )->clone(); // need to clone individual geometry parts
|
||||
Qgs3DUtils::clampAltitudes( polyBuffered, symbol.altitudeClamping(), symbol.altitudeBinding(), symbol.height(), map );
|
||||
polygons.append( polyBuffered );
|
||||
fids.append( f.id() );
|
||||
}
|
||||
delete buffered;
|
||||
}
|
||||
}
|
||||
|
||||
mGeometry = new QgsTessellatedPolygonGeometry;
|
||||
mGeometry->setPolygons( polygons, origin, symbol.extrusionHeight() );
|
||||
mGeometry->setPolygons( polygons, fids, origin, symbol.extrusionHeight() );
|
||||
|
||||
Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
|
||||
renderer->setGeometry( mGeometry );
|
||||
|
@ -101,6 +101,7 @@ void QgsPolygon3DSymbolEntity::addEntityForNotSelectedPolygons( const Qgs3DMapSe
|
||||
|
||||
// build the entity
|
||||
QgsPolygon3DSymbolEntityNode *entity = new QgsPolygon3DSymbolEntityNode( map, layer, symbol, req );
|
||||
entity->findChild<Qt3DRender::QGeometryRenderer *>()->setObjectName( "main" ); // temporary measure to distinguish between "selected" and "main"
|
||||
entity->addComponent( mat );
|
||||
entity->addComponent( tform );
|
||||
entity->setParent( this );
|
||||
@ -141,6 +142,7 @@ Qt3DRender::QGeometryRenderer *QgsPolygon3DSymbolEntityNode::renderer( const Qgs
|
||||
{
|
||||
QgsPointXY origin( map.origin().x(), map.origin().y() );
|
||||
QList<QgsPolygon *> polygons;
|
||||
QList<QgsFeatureId> fids;
|
||||
QList<float> extrusionHeightPerPolygon; // will stay empty if not needed per polygon
|
||||
|
||||
QgsExpressionContext ctx( _expressionContext3D() );
|
||||
@ -178,6 +180,7 @@ Qt3DRender::QGeometryRenderer *QgsPolygon3DSymbolEntityNode::renderer( const Qgs
|
||||
QgsPolygon *polyClone = poly->clone();
|
||||
Qgs3DUtils::clampAltitudes( polyClone, symbol.altitudeClamping(), symbol.altitudeBinding(), height, map );
|
||||
polygons.append( polyClone );
|
||||
fids.append( f.id() );
|
||||
if ( hasDDExtrusion )
|
||||
extrusionHeightPerPolygon.append( extrusionHeight );
|
||||
}
|
||||
@ -190,6 +193,7 @@ Qt3DRender::QGeometryRenderer *QgsPolygon3DSymbolEntityNode::renderer( const Qgs
|
||||
QgsPolygon *polyClone = static_cast< const QgsPolygon *>( g2 )->clone();
|
||||
Qgs3DUtils::clampAltitudes( polyClone, symbol.altitudeClamping(), symbol.altitudeBinding(), height, map );
|
||||
polygons.append( polyClone );
|
||||
fids.append( f.id() );
|
||||
if ( hasDDExtrusion )
|
||||
extrusionHeightPerPolygon.append( extrusionHeight );
|
||||
}
|
||||
@ -201,7 +205,7 @@ Qt3DRender::QGeometryRenderer *QgsPolygon3DSymbolEntityNode::renderer( const Qgs
|
||||
mGeometry = new QgsTessellatedPolygonGeometry;
|
||||
mGeometry->setInvertNormals( symbol.invertNormals() );
|
||||
mGeometry->setAddBackFaces( symbol.addBackFaces() );
|
||||
mGeometry->setPolygons( polygons, origin, symbol.extrusionHeight(), extrusionHeightPerPolygon );
|
||||
mGeometry->setPolygons( polygons, fids, origin, symbol.extrusionHeight(), extrusionHeightPerPolygon );
|
||||
|
||||
Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer;
|
||||
renderer->setGeometry( mGeometry );
|
||||
|
@ -26,10 +26,42 @@
|
||||
#include <Qt3DRender/QObjectPicker>
|
||||
#include <Qt3DRender/QPickEvent>
|
||||
|
||||
|
||||
#include "qgs3dmapscenepickhandler.h"
|
||||
|
||||
class Qgs3DMapToolIdentifyPickHandler : public Qgs3DMapScenePickHandler
|
||||
{
|
||||
public:
|
||||
Qgs3DMapToolIdentifyPickHandler( Qgs3DMapToolIdentify *identifyTool ): mIdentifyTool( identifyTool ) {}
|
||||
void handlePickOnVectorLayer( QgsVectorLayer *vlayer, QgsFeatureId id, const QVector3D &worldIntersection ) override;
|
||||
private:
|
||||
Qgs3DMapToolIdentify *mIdentifyTool = nullptr;
|
||||
};
|
||||
|
||||
|
||||
void Qgs3DMapToolIdentifyPickHandler::handlePickOnVectorLayer( QgsVectorLayer *vlayer, QgsFeatureId id, const QVector3D &worldIntersection )
|
||||
{
|
||||
QgsVector3D mapCoords = Qgs3DUtils::worldToMapCoordinates( worldIntersection, mIdentifyTool->mCanvas->map()->origin() );
|
||||
QgsPoint pt( mapCoords.x(), mapCoords.y(), mapCoords.z() );
|
||||
|
||||
QgsMapToolIdentifyAction *identifyTool2D = QgisApp::instance()->identifyMapTool();
|
||||
identifyTool2D->showResultsForFeature( vlayer, id, pt );
|
||||
}
|
||||
|
||||
|
||||
//////
|
||||
|
||||
|
||||
Qgs3DMapToolIdentify::Qgs3DMapToolIdentify( Qgs3DMapCanvas *canvas )
|
||||
: Qgs3DMapTool( canvas )
|
||||
{
|
||||
connect( mCanvas->scene(), &Qgs3DMapScene::terrainEntityChanged, this, &Qgs3DMapToolIdentify::onTerrainEntityChanged );
|
||||
|
||||
mPickHandler.reset( new Qgs3DMapToolIdentifyPickHandler( this ) );
|
||||
}
|
||||
|
||||
Qgs3DMapToolIdentify::~Qgs3DMapToolIdentify()
|
||||
{
|
||||
}
|
||||
|
||||
void Qgs3DMapToolIdentify::mousePressEvent( QMouseEvent *event )
|
||||
@ -44,16 +76,23 @@ void Qgs3DMapToolIdentify::activate()
|
||||
{
|
||||
Qt3DRender::QObjectPicker *picker = mCanvas->scene()->terrainEntity()->terrainPicker();
|
||||
connect( picker, &Qt3DRender::QObjectPicker::pressed, this, &Qgs3DMapToolIdentify::onTerrainPicked );
|
||||
|
||||
mCanvas->scene()->registerPickHandler( mPickHandler.get() );
|
||||
}
|
||||
|
||||
void Qgs3DMapToolIdentify::deactivate()
|
||||
{
|
||||
Qt3DRender::QObjectPicker *picker = mCanvas->scene()->terrainEntity()->terrainPicker();
|
||||
disconnect( picker, &Qt3DRender::QObjectPicker::pressed, this, &Qgs3DMapToolIdentify::onTerrainPicked );
|
||||
|
||||
mCanvas->scene()->unregisterPickHandler( mPickHandler.get() );
|
||||
}
|
||||
|
||||
void Qgs3DMapToolIdentify::onTerrainPicked( Qt3DRender::QPickEvent *event )
|
||||
{
|
||||
if ( event->button() != Qt3DRender::QPickEvent::LeftButton )
|
||||
return;
|
||||
|
||||
QgsVector3D mapCoords = Qgs3DUtils::worldToMapCoordinates( event->worldIntersection(), mCanvas->map()->origin() );
|
||||
|
||||
QgsGeometry geom = QgsGeometry::fromPointXY( QgsPointXY( mapCoords.x(), mapCoords.y() ) );
|
||||
|
@ -18,11 +18,15 @@
|
||||
|
||||
#include "qgs3dmaptool.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Qt3DRender
|
||||
{
|
||||
class QPickEvent;
|
||||
}
|
||||
|
||||
class Qgs3DMapToolIdentifyPickHandler;
|
||||
|
||||
|
||||
class Qgs3DMapToolIdentify : public Qgs3DMapTool
|
||||
{
|
||||
@ -30,6 +34,7 @@ class Qgs3DMapToolIdentify : public Qgs3DMapTool
|
||||
|
||||
public:
|
||||
Qgs3DMapToolIdentify( Qgs3DMapCanvas *canvas );
|
||||
~Qgs3DMapToolIdentify() override;
|
||||
|
||||
void mousePressEvent( QMouseEvent *event ) override;
|
||||
void mouseReleaseEvent( QMouseEvent *event ) override { Q_UNUSED( event );}
|
||||
@ -43,7 +48,9 @@ class Qgs3DMapToolIdentify : public Qgs3DMapTool
|
||||
void onTerrainEntityChanged();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Qgs3DMapToolIdentifyPickHandler> mPickHandler;
|
||||
|
||||
friend class Qgs3DMapToolIdentifyPickHandler;
|
||||
};
|
||||
|
||||
#endif // QGS3DMAPTOOLIDENTIFY_H
|
||||
|
@ -222,6 +222,19 @@ void QgsMapToolIdentifyAction::clearResults()
|
||||
resultsDialog()->clear();
|
||||
}
|
||||
|
||||
void QgsMapToolIdentifyAction::showResultsForFeature( QgsVectorLayer *vlayer, QgsFeatureId fid, const QgsPoint &pt )
|
||||
{
|
||||
QgsFeature feature = vlayer->getFeature( fid );
|
||||
QMap< QString, QString > derivedAttributes = derivedAttributesForPoint( pt );
|
||||
// TODO: private in QgsMapToolIdentify
|
||||
//derivedAttributes.unite( featureDerivedAttributes( feature, vlayer, QgsPointXY( pt ) ) );
|
||||
|
||||
resultsDialog()->addFeature( IdentifyResult( vlayer, feature, derivedAttributes ) );
|
||||
resultsDialog()->show();
|
||||
// update possible view modes
|
||||
resultsDialog()->updateViewModes();
|
||||
}
|
||||
|
||||
QgsUnitTypes::DistanceUnit QgsMapToolIdentifyAction::displayDistanceUnits() const
|
||||
{
|
||||
return QgsProject::instance()->distanceUnits();
|
||||
|
@ -65,6 +65,8 @@ class APP_EXPORT QgsMapToolIdentifyAction : public QgsMapToolIdentify
|
||||
void identifyAndShowResults( const QgsGeometry &geom );
|
||||
//! Clears any previous results from the GUI
|
||||
void clearResults();
|
||||
//! Looks up feature by its ID and outputs the result in GUI
|
||||
void showResultsForFeature( QgsVectorLayer *vlayer, QgsFeatureId fid, const QgsPoint &pt );
|
||||
|
||||
public slots:
|
||||
void handleCopyToClipboard( QgsFeatureStore & );
|
||||
|
@ -542,7 +542,7 @@ QgsPoint getPointFromData( QVector< float >::const_iterator &it )
|
||||
|
||||
int QgsTessellator::dataVerticesCount() const
|
||||
{
|
||||
return mData.size() / 3;
|
||||
return mData.size() / ( mAddNormals ? 6 : 3 );
|
||||
}
|
||||
|
||||
std::unique_ptr<QgsMultiPolygon> QgsTessellator::asMultiPolygon() const
|
||||
|
@ -215,6 +215,16 @@ bool QgsMapToolIdentify::identifyVectorLayer( QList<QgsMapToolIdentify::Identify
|
||||
return identifyVectorLayer( results, layer, QgsGeometry::fromPointXY( point ) );
|
||||
}
|
||||
|
||||
QMap<QString, QString> QgsMapToolIdentify::derivedAttributesForPoint( const QgsPoint &point )
|
||||
{
|
||||
QMap< QString, QString > derivedAttributes;
|
||||
derivedAttributes.insert( tr( "(clicked coordinate X)" ), formatXCoordinate( point ) );
|
||||
derivedAttributes.insert( tr( "(clicked coordinate Y)" ), formatYCoordinate( point ) );
|
||||
if ( point.is3D() )
|
||||
derivedAttributes.insert( tr( "(clicked coordinate Z)" ), QString::number( point.z(), 'f' ) );
|
||||
return derivedAttributes;
|
||||
}
|
||||
|
||||
bool QgsMapToolIdentify::identifyVectorLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorLayer *layer, const QgsGeometry &geometry )
|
||||
{
|
||||
if ( !layer || !layer->isSpatial() )
|
||||
@ -239,8 +249,7 @@ bool QgsMapToolIdentify::identifyVectorLayer( QList<QgsMapToolIdentify::Identify
|
||||
isPointOrRectangle = true;
|
||||
point = selectionGeom.asPoint();
|
||||
|
||||
commonDerivedAttributes.insert( tr( "(clicked coordinate X)" ), formatXCoordinate( point ) );
|
||||
commonDerivedAttributes.insert( tr( "(clicked coordinate Y)" ), formatYCoordinate( point ) );
|
||||
commonDerivedAttributes = derivedAttributesForPoint( QgsPoint( point ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -649,8 +658,7 @@ bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, Qg
|
||||
identifyResult = dprovider->identify( point, format, viewExtent, width, height );
|
||||
}
|
||||
|
||||
derivedAttributes.insert( tr( "(clicked coordinate X)" ), formatXCoordinate( pointInCanvasCrs ) );
|
||||
derivedAttributes.insert( tr( "(clicked coordinate Y)" ), formatYCoordinate( pointInCanvasCrs ) );
|
||||
derivedAttributes.unite( derivedAttributesForPoint( QgsPoint( pointInCanvasCrs ) ) );
|
||||
|
||||
if ( identifyResult.isValid() )
|
||||
{
|
||||
|
@ -165,6 +165,9 @@ class GUI_EXPORT QgsMapToolIdentify : public QgsMapTool
|
||||
bool identifyRasterLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsRasterLayer *layer, QgsPointXY point, const QgsRectangle &viewExtent, double mapUnitsPerPixel );
|
||||
bool identifyVectorLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsVectorLayer *layer, const QgsPointXY &point );
|
||||
|
||||
//! Returns derived attributes map for a clicked point in map coordinates. May be 2D or 3D point.
|
||||
QMap< QString, QString > derivedAttributesForPoint( const QgsPoint &point );
|
||||
|
||||
private:
|
||||
|
||||
bool identifyLayer( QList<QgsMapToolIdentify::IdentifyResult> *results, QgsMapLayer *layer, const QgsGeometry &geometry, const QgsRectangle &viewExtent, double mapUnitsPerPixel, QgsMapToolIdentify::LayerType layerType = AllLayers );
|
||||
|
Loading…
x
Reference in New Issue
Block a user