QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
qgs3dhighlightfeaturehandler.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgs3dhighlightfeaturehandler.cpp
3 ---------------------
4 begin : December 2025
5 copyright : (C) 2025 by Stefanos Natsis
6 email : uclaros at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgs3dmapscene.h"
19#include "qgs3dmapsceneentity.h"
20#include "qgs3dsymbolregistry.h"
21#include "qgsabstract3dengine.h"
22#include "qgsapplication.h"
25#include "qgsframegraph.h"
28#include "qgsrubberband3d.h"
29#include "qgsvectorlayer.h"
31
32#include <QString>
33#include <QTimer>
34
35#include "moc_qgs3dhighlightfeaturehandler.cpp"
36
37using namespace Qt::StringLiterals;
38
42
44{
45 qDeleteAll( mHighlightHandlers );
46 for ( auto it = mHighlightRuleBasedHandlers.constBegin(); it != mHighlightRuleBasedHandlers.constEnd(); ++it )
47 {
48 qDeleteAll( it.value() );
49 }
50}
51
53{
54 switch ( layer->type() )
55 {
56 // we only support point clouds and vector for now
64 return;
65
67 {
68 QgsAbstract3DRenderer *renderer = layer->renderer3D();
69 if ( !renderer )
70 return;
71
72 QgsVectorLayer *vLayer = static_cast<QgsVectorLayer *>( layer );
73 QgsExpressionContext exprContext;
75 exprContext.setFields( vLayer->fields() );
76 exprContext.setFeature( feature );
77 Qgs3DRenderContext renderContext = Qgs3DRenderContext::fromMapSettings( mScene->mapSettings() );
78 renderContext.setExpressionContext( exprContext );
79
80 if ( renderContext.crs() != layer->crs() )
81 {
82 QgsCoordinateTransform ct( layer->crs(), renderContext.crs(), renderContext.transformContext() );
83 QgsGeometry geom = feature.geometry();
84 try
85 {
86 geom.transform( ct );
87 }
88 catch ( QgsCsException & )
89 {
90 QgsDebugError( u"Could not reproject identified feature to 3d view crs"_s );
91 return;
92 }
93 feature.setGeometry( geom );
94 exprContext.setFeature( feature );
95 }
96
97 if ( renderer->type() == "vector"_L1 )
98 {
99 if ( !mHighlightHandlers.contains( layer ) )
100 {
101 QgsVectorLayer3DRenderer *renderer3d = static_cast<QgsVectorLayer3DRenderer *>( renderer );
102
103 if ( !renderer3d || !renderer3d->symbol() )
104 {
105 return;
106 }
107
108 std::unique_ptr<QgsFeature3DHandler> handler( QgsApplication::symbol3DRegistry()->createHandlerForSymbol( vLayer, renderer3d->symbol() ) );
109 if ( !handler )
110 {
111 QgsDebugError( u"Unknown 3D symbol type for vector layer: "_s + renderer3d->symbol()->type() );
112 return;
113 }
114
115 QSet<QString> attributeNames;
116 const QgsGeometry geom = feature.geometry();
117 // We want to ignore the geometry's elevation and use a box with symmetric +Z/-Z extents so the chunk origin has Z == 0.
118 QgsBox3D box = geom.boundingBox().toBox3d( 0, 0 );
119 if ( !handler->prepare( renderContext, attributeNames, box ) )
120 {
121 QgsDebugError( u"Failed to prepare 3D feature handler!"_s );
122 return;
123 }
124
125 mHighlightHandlers[layer] = handler.release();
126 }
127
128 mHighlightHandlers[layer]->processFeature( feature, renderContext );
129 }
130 else if ( renderer->type() == "rulebased"_L1 )
131 {
132 QgsRuleBased3DRenderer *ruleBasedRenderer = static_cast<QgsRuleBased3DRenderer *>( renderer );
133 QgsRuleBased3DRenderer::Rule *rootRule = ruleBasedRenderer->rootRule();
134 if ( !mHighlightRuleBasedHandlers.contains( layer ) )
135 {
137 rootRule->createHandlers( vLayer, handlers );
138 QSet<QString> attributeNames;
139 const QgsGeometry geom = feature.geometry();
140 // We want to ignore the geometry's elevation and use a box with symmetric +Z/-Z extents so the chunk origin has Z == 0.
141 QgsBox3D box = geom.boundingBox().toBox3d( 0, 0 );
142 rootRule->prepare( renderContext, attributeNames, box, handlers );
143 mHighlightRuleBasedHandlers[layer] = handlers;
144 }
145
146 rootRule->registerFeature( feature, renderContext, mHighlightRuleBasedHandlers[layer] );
147 }
148
149 // We'll use a singleshot timer to handle the entity creation, it will be served once we stop receiving highlighted features
150 if ( !mHighlightHandlerTimer && ( !mHighlightHandlers.isEmpty() || !mHighlightRuleBasedHandlers.isEmpty() ) )
151 {
152 mHighlightHandlerTimer = std::make_unique<QTimer>();
153 mHighlightHandlerTimer->setSingleShot( true );
154 mHighlightHandlerTimer->setInterval( 0 );
155 connect( mHighlightHandlerTimer.get(), &QTimer::timeout, this, [this, renderContext] {
156 for ( auto it = mHighlightHandlers.constBegin(); it != mHighlightHandlers.constEnd(); ++it )
157 {
158 Qgs3DMapSceneEntity *entity = new Qgs3DMapSceneEntity( mScene->mapSettings(), nullptr );
159 entity->setObjectName( u"%1_highlight"_s.arg( it.key()->name() ) );
160 QgsFeature3DHandler *handler = it.value();
161 handler->setHighlightingEnabled( true );
162 handler->finalize( entity, renderContext );
163
164 finalizeAndAddToScene( entity );
165 }
166
167 for ( auto rulesIt = mHighlightRuleBasedHandlers.constBegin(); rulesIt != mHighlightRuleBasedHandlers.constEnd(); ++rulesIt )
168 {
169 Qgs3DMapSceneEntity *entity = new Qgs3DMapSceneEntity( mScene->mapSettings(), nullptr );
170 entity->setObjectName( u"%1_highlight"_s.arg( rulesIt.key()->name() ) );
171
172 for ( auto handlersIt = rulesIt.value().constBegin(); handlersIt != rulesIt.value().constEnd(); ++handlersIt )
173 {
174 QgsFeature3DHandler *handler = handlersIt.value();
175 handler->setHighlightingEnabled( true );
176 handler->finalize( entity, renderContext );
177 }
178
179 finalizeAndAddToScene( entity );
180 }
181 } );
182 mHighlightHandlerTimer->start();
183 }
184
185 return;
186 }
187
189 {
190 const QgsGeometry geom = feature.geometry();
191 const QgsPoint pt( geom.vertexAt( 0 ) );
192
193 if ( !mRubberBands.contains( layer ) )
194 {
195 QgsRubberBand3D *band = new QgsRubberBand3D( *mScene->mapSettings(), mScene->engine(), mScene->engine()->frameGraph()->rubberBandsRootEntity(), Qgis::GeometryType::Point );
196
197 const QgsSettings settings;
198 const QColor color = QColor( settings.value( u"Map/highlight/color"_s, Qgis::DEFAULT_HIGHLIGHT_COLOR.name() ).toString() );
199 band->setColor( color );
200 band->setMarkerType( QgsRubberBand3D::MarkerType::Square );
201 if ( QgsPointCloudLayer3DRenderer *pcRenderer = dynamic_cast<QgsPointCloudLayer3DRenderer *>( layer->renderer3D() ) )
202 {
203 band->setWidth( pcRenderer->symbol()->pointSize() + 1 );
204 }
205 mRubberBands.insert( layer, band );
206
207 connect( layer, &QgsMapLayer::renderer3DChanged, this, &Qgs3DHighlightFeatureHandler::onRenderer3DChanged );
208 }
209 mRubberBands[layer]->addPoint( pt );
210 return;
211 }
212 }
213}
214
215void Qgs3DHighlightFeatureHandler::onRenderer3DChanged()
216{
217 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() ) )
218 {
219 if ( QgsPointCloudLayer3DRenderer *rnd = dynamic_cast<QgsPointCloudLayer3DRenderer *>( layer->renderer3D() ) )
220 {
221 if ( mRubberBands.contains( layer ) )
222 {
223 mRubberBands[layer]->setWidth( rnd->symbol()->pointSize() + 1 );
224 }
225 }
226 }
227}
228
229void Qgs3DHighlightFeatureHandler::finalizeAndAddToScene( Qgs3DMapSceneEntity *entity )
230{
231 // We need the highlights layer to force rendering on highlights renderview only
232 QgsFrameGraph *frameGraph = mScene->engine()->frameGraph();
233 entity->addComponent( frameGraph->highlightsRenderView().highlightsLayer() );
234
235 mScene->addSceneEntity( entity );
236 mHighlightEntities.append( entity );
237}
238
240{
241 mHighlightHandlerTimer.reset();
242 qDeleteAll( mHighlightHandlers );
243 mHighlightHandlers.clear();
244 for ( auto it = mHighlightRuleBasedHandlers.constBegin(); it != mHighlightRuleBasedHandlers.constEnd(); ++it )
245 {
246 qDeleteAll( it.value() );
247 }
248 mHighlightRuleBasedHandlers.clear();
249 // vector layer highlights
250 for ( Qt3DCore::QEntity *e : std::as_const( mHighlightEntities ) )
251 {
252 mScene->removeSceneEntity( static_cast<Qgs3DMapSceneEntity *>( e ) );
253 }
254 mHighlightEntities.clear();
255
256 // point cloud layer highlights
257 for ( auto it = mRubberBands.keyBegin(); it != mRubberBands.keyEnd(); it++ )
258 {
259 disconnect( it.base().key(), &QgsMapLayer::renderer3DChanged, this, &Qgs3DHighlightFeatureHandler::onRenderer3DChanged );
260 }
261
262 qDeleteAll( mRubberBands );
263 mRubberBands.clear();
264}
static const QColor DEFAULT_HIGHLIGHT_COLOR
Default highlight color.
Definition qgis.h:6553
@ Point
Points.
Definition qgis.h:380
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:214
@ Plugin
Plugin based layer.
Definition qgis.h:209
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:215
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:212
@ Vector
Vector layer.
Definition qgis.h:207
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:211
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:210
@ Raster
Raster layer.
Definition qgis.h:208
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:213
Qgs3DHighlightFeatureHandler(Qgs3DMapScene *scene)
Constructor.
void highlightFeature(QgsFeature feature, QgsMapLayer *layer)
Highlights feature of layer in the 3d scene When multiple features are identified,...
void clearHighlights()
Clears all highlights.
Entity that encapsulates our 3D scene - contains all other entities (such as terrain) as children.
Rendering context for preparation of 3D entities.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system used in the 3D scene.
static Qgs3DRenderContext fromMapSettings(const Qgs3DMapSettings *mapSettings)
Creates an initialized Qgs3DRenderContext instance from given Qgs3DMapSettings.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
Base class for all renderers that participate in 3D views.
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass).
virtual QString type() const =0
Returns identifier of symbol type. Each 3D symbol implementation should return a different type.
static Qgs3DSymbolRegistry * symbol3DRegistry()
Returns registry of available 3D symbols.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsGeometry geometry
Definition qgsfeature.h:71
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
QgsHighlightsRenderView & highlightsRenderView()
Returns the highlights renderview, used for rendering highlight overlays of identified features.
A geometry is the spatial representation of a feature.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qt3DRender::QLayer * highlightsLayer()
Returns a layer that should be attached to entities meant to be rendered by QgsHighlightsRenderView.
Base class for all map layer types.
Definition qgsmaplayer.h:83
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
void renderer3DChanged()
Signal emitted when 3D renderer associated with the layer has changed.
Qgis::LayerType type
Definition qgsmaplayer.h:93
3D renderer that renders all points from a point cloud layer.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
QgsBox3D toBox3d(double zMin, double zMax) const
Converts the rectangle to a 3D box, with the specified zMin and zMax z values.
A child rule for a QgsRuleBased3DRenderer.
void prepare(const Qgs3DRenderContext &context, QSet< QString > &attributeNames, const QgsBox3D &chunkExtent, RuleToHandlerMap &handlers) const
call prepare() on handlers and populate attributeNames
RegisterResult registerFeature(const QgsFeature &feature, Qgs3DRenderContext &context, const RuleToHandlerMap &handlers) const
register individual features
void createHandlers(QgsVectorLayer *layer, RuleToHandlerMap &handlers) const
add handlers
Rule-based 3D renderer.
QHash< const QgsRuleBased3DRenderer::Rule *, QgsFeature3DHandler * > RuleToHandlerMap
QgsRuleBased3DRenderer::Rule * rootRule()
Returns pointer to the root rule.
Stores settings for use within QGIS.
Definition qgssettings.h:68
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
3D renderer that renders all features of a vector layer with the same 3D symbol.
const QgsAbstract3DSymbol * symbol() const
Returns 3D symbol associated with the renderer.
Represents a vector layer which manages a vector based dataset.
#define QgsDebugError(str)
Definition qgslogger.h:59