QGIS API Documentation 3.99.0-Master (357b655ed83)
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
43
45{
46 qDeleteAll( mHighlightHandlers );
47 for ( auto it = mHighlightRuleBasedHandlers.constBegin(); it != mHighlightRuleBasedHandlers.constEnd(); ++it )
48 {
49 qDeleteAll( it.value() );
50 }
51}
52
54{
55 switch ( layer->type() )
56 {
57 // we only support point clouds and vector for now
65 return;
66
68 {
69 QgsAbstract3DRenderer *renderer = layer->renderer3D();
70 if ( !renderer )
71 return;
72
73 QgsVectorLayer *vLayer = static_cast<QgsVectorLayer *>( layer );
74 QgsExpressionContext exprContext;
76 exprContext.setFields( vLayer->fields() );
77 exprContext.setFeature( feature );
78 Qgs3DRenderContext renderContext = Qgs3DRenderContext::fromMapSettings( mScene->mapSettings() );
79 renderContext.setExpressionContext( exprContext );
80
81 if ( renderContext.crs() != layer->crs() )
82 {
83 QgsCoordinateTransform ct( layer->crs(), renderContext.crs(), renderContext.transformContext() );
84 QgsGeometry geom = feature.geometry();
85 try
86 {
87 geom.transform( ct );
88 }
89 catch ( QgsCsException & )
90 {
91 QgsDebugError( u"Could not reproject identified feature to 3d view crs"_s );
92 return;
93 }
94 feature.setGeometry( geom );
95 exprContext.setFeature( feature );
96 }
97
98 if ( renderer->type() == "vector"_L1 )
99 {
100 if ( !mHighlightHandlers.contains( layer ) )
101 {
102 QgsVectorLayer3DRenderer *renderer3d = static_cast<QgsVectorLayer3DRenderer *>( renderer );
103
104 if ( !renderer3d || !renderer3d->symbol() )
105 {
106 return;
107 }
108
109 std::unique_ptr<QgsFeature3DHandler> handler( QgsApplication::symbol3DRegistry()->createHandlerForSymbol( vLayer, renderer3d->symbol() ) );
110 if ( !handler )
111 {
112 QgsDebugError( u"Unknown 3D symbol type for vector layer: "_s + renderer3d->symbol()->type() );
113 return;
114 }
115
116 QSet<QString> attributeNames;
117 const QgsGeometry geom = feature.geometry();
118 // We want to ignore the geometry's elevation and use a box with symmetric +Z/-Z extents so the chunk origin has Z == 0.
119 QgsBox3D box = geom.boundingBox().toBox3d( 0, 0 );
120 if ( !handler->prepare( renderContext, attributeNames, box ) )
121 {
122 QgsDebugError( u"Failed to prepare 3D feature handler!"_s );
123 return;
124 }
125
126 mHighlightHandlers[layer] = handler.release();
127 }
128
129 mHighlightHandlers[layer]->processFeature( feature, renderContext );
130 }
131 else if ( renderer->type() == "rulebased"_L1 )
132 {
133 QgsRuleBased3DRenderer *ruleBasedRenderer = static_cast<QgsRuleBased3DRenderer *>( renderer );
134 QgsRuleBased3DRenderer::Rule *rootRule = ruleBasedRenderer->rootRule();
135 if ( !mHighlightRuleBasedHandlers.contains( layer ) )
136 {
138 rootRule->createHandlers( vLayer, handlers );
139 QSet<QString> attributeNames;
140 const QgsGeometry geom = feature.geometry();
141 // We want to ignore the geometry's elevation and use a box with symmetric +Z/-Z extents so the chunk origin has Z == 0.
142 QgsBox3D box = geom.boundingBox().toBox3d( 0, 0 );
143 rootRule->prepare( renderContext, attributeNames, box, handlers );
144 mHighlightRuleBasedHandlers[layer] = handlers;
145 }
146
147 rootRule->registerFeature( feature, renderContext, mHighlightRuleBasedHandlers[layer] );
148 }
149
150 // We'll use a singleshot timer to handle the entity creation, it will be served once we stop receiving highlighted features
151 if ( !mHighlightHandlerTimer && ( !mHighlightHandlers.isEmpty() || !mHighlightRuleBasedHandlers.isEmpty() ) )
152 {
153 mHighlightHandlerTimer = std::make_unique<QTimer>();
154 mHighlightHandlerTimer->setSingleShot( true );
155 mHighlightHandlerTimer->setInterval( 0 );
156 connect( mHighlightHandlerTimer.get(), &QTimer::timeout, this, [this, renderContext] {
157 for ( auto it = mHighlightHandlers.constBegin(); it != mHighlightHandlers.constEnd(); ++it )
158 {
159 Qgs3DMapSceneEntity *entity = new Qgs3DMapSceneEntity( mScene->mapSettings(), nullptr );
160 entity->setObjectName( u"%1_highlight"_s.arg( it.key()->name() ) );
161 QgsFeature3DHandler *handler = it.value();
162 handler->setHighlightingEnabled( true );
163 handler->finalize( entity, renderContext );
164
165 finalizeAndAddToScene( entity );
166 }
167
168 for ( auto rulesIt = mHighlightRuleBasedHandlers.constBegin(); rulesIt != mHighlightRuleBasedHandlers.constEnd(); ++rulesIt )
169 {
170 Qgs3DMapSceneEntity *entity = new Qgs3DMapSceneEntity( mScene->mapSettings(), nullptr );
171 entity->setObjectName( u"%1_highlight"_s.arg( rulesIt.key()->name() ) );
172
173 for ( auto handlersIt = rulesIt.value().constBegin(); handlersIt != rulesIt.value().constEnd(); ++handlersIt )
174 {
175 QgsFeature3DHandler *handler = handlersIt.value();
176 handler->setHighlightingEnabled( true );
177 handler->finalize( entity, renderContext );
178 }
179
180 finalizeAndAddToScene( entity );
181 }
182 } );
183 mHighlightHandlerTimer->start();
184 }
185
186 return;
187 }
188
190 {
191 const QgsGeometry geom = feature.geometry();
192 const QgsPoint pt( geom.vertexAt( 0 ) );
193
194 if ( !mRubberBands.contains( layer ) )
195 {
196 QgsRubberBand3D *band = new QgsRubberBand3D( *mScene->mapSettings(), mScene->engine(), mScene->engine()->frameGraph()->rubberBandsRootEntity(), Qgis::GeometryType::Point );
197
198 const QgsSettings settings;
199 const QColor color = QColor( settings.value( u"Map/highlight/color"_s, Qgis::DEFAULT_HIGHLIGHT_COLOR.name() ).toString() );
200 band->setColor( color );
201 band->setMarkerType( QgsRubberBand3D::MarkerType::Square );
202 if ( QgsPointCloudLayer3DRenderer *pcRenderer = dynamic_cast<QgsPointCloudLayer3DRenderer *>( layer->renderer3D() ) )
203 {
204 band->setWidth( pcRenderer->symbol()->pointSize() + 1 );
205 }
206 mRubberBands.insert( layer, band );
207
208 connect( layer, &QgsMapLayer::renderer3DChanged, this, &Qgs3DHighlightFeatureHandler::onRenderer3DChanged );
209 }
210 mRubberBands[layer]->addPoint( pt );
211 return;
212 }
213 }
214}
215
216void Qgs3DHighlightFeatureHandler::onRenderer3DChanged()
217{
218 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() ) )
219 {
220 if ( QgsPointCloudLayer3DRenderer *rnd = dynamic_cast<QgsPointCloudLayer3DRenderer *>( layer->renderer3D() ) )
221 {
222 if ( mRubberBands.contains( layer ) )
223 {
224 mRubberBands[layer]->setWidth( rnd->symbol()->pointSize() + 1 );
225 }
226 }
227 }
228}
229
230void Qgs3DHighlightFeatureHandler::finalizeAndAddToScene( Qgs3DMapSceneEntity *entity )
231{
232 // We need the highlights layer to force rendering on highlights renderview only
233 QgsFrameGraph *frameGraph = mScene->engine()->frameGraph();
234 entity->addComponent( frameGraph->highlightsRenderView().highlightsLayer() );
235
236 mScene->addSceneEntity( entity );
237 mHighlightEntities.append( entity );
238}
239
241{
242 mHighlightHandlerTimer.reset();
243 qDeleteAll( mHighlightHandlers );
244 mHighlightHandlers.clear();
245 for ( auto it = mHighlightRuleBasedHandlers.constBegin(); it != mHighlightRuleBasedHandlers.constEnd(); ++it )
246 {
247 qDeleteAll( it.value() );
248 }
249 mHighlightRuleBasedHandlers.clear();
250 // vector layer highlights
251 for ( Qt3DCore::QEntity *e : std::as_const( mHighlightEntities ) )
252 {
253 mScene->removeSceneEntity( static_cast<Qgs3DMapSceneEntity *>( e ) );
254 }
255 mHighlightEntities.clear();
256
257 // point cloud layer highlights
258 for ( auto it = mRubberBands.keyBegin(); it != mRubberBands.keyEnd(); it++ )
259 {
260 disconnect( it.base().key(), &QgsMapLayer::renderer3DChanged, this, &Qgs3DHighlightFeatureHandler::onRenderer3DChanged );
261 }
262
263 qDeleteAll( mRubberBands );
264 mRubberBands.clear();
265}
static const QColor DEFAULT_HIGHLIGHT_COLOR
Default highlight color.
Definition qgis.h:6496
@ Point
Points.
Definition qgis.h:366
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:201
@ Plugin
Plugin based layer.
Definition qgis.h:196
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:202
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:199
@ Vector
Vector layer.
Definition qgis.h:194
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:198
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:197
@ Raster
Raster layer.
Definition qgis.h:195
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:200
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