QGIS API Documentation 4.1.0-Master (01362494303)
Loading...
Searching...
No Matches
qgsvectorlayerchunkloader_p.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectorlayerchunkloader_p.cpp
3 --------------------------------------
4 Date : July 2019
5 Copyright : (C) 2019 by Martin Dobias
6 Email : wonder dot sk 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 "qgs3dsymbolregistry.h"
19#include "qgs3dutils.h"
20#include "qgsabstract3dsymbol.h"
23#include "qgsapplication.h"
24#include "qgschunknode.h"
25#include "qgseventtracing.h"
28#include "qgsline3dsymbol.h"
29#include "qgslogger.h"
30#include "qgspoint3dsymbol.h"
31#include "qgspolygon3dsymbol.h"
32#include "qgsvectorlayer.h"
34
35#include <QString>
36#include <Qt3DCore/QTransform>
37#include <Qt3DRender/QGeometryRenderer>
38#include <QtConcurrentRun>
39
40#include "moc_qgsvectorlayerchunkloader_p.cpp"
41
42using namespace Qt::StringLiterals;
43
45
46
47QgsVectorLayerChunkLoader::QgsVectorLayerChunkLoader( const QgsVectorLayerChunkLoaderFactory *factory, QgsChunkNode *node )
48 : QgsChunkLoader( node )
49 , mFactory( factory )
50 , mRenderContext( factory->mRenderContext )
51 , mSource( new QgsVectorLayerFeatureSource( factory->mLayer ) )
52{}
53
54void QgsVectorLayerChunkLoader::start()
55{
56 QgsChunkNode *node = chunk();
57
58 QgsVectorLayer *layer = mFactory->mLayer;
59 mLayerName = mFactory->mLayer->name();
60
61 QgsFeature3DHandler *handler = QgsApplication::symbol3DRegistry()->createHandlerForSymbol( layer, mFactory->mSymbol.get() );
62 if ( !handler )
63 {
64 QgsDebugError( u"Unknown 3D symbol type for vector layer: "_s + mFactory->mSymbol->type() );
65 return;
66 }
67 mHandler.reset( handler );
68
69 QgsExpressionContext exprContext;
71 exprContext.setFields( layer->fields() );
72 mRenderContext.setExpressionContext( exprContext );
73
74 QSet<QString> attributeNames;
75 if ( !mHandler->prepare( mRenderContext, attributeNames, node->box3D() ) )
76 {
77 QgsDebugError( u"Failed to prepare 3D feature handler!"_s );
78 return;
79 }
80
81 // build the feature request
82 // only a subset of data to be queried
83 const QgsRectangle rect = node->box3D().toRectangle();
85 req.setCoordinateTransform( QgsCoordinateTransform( layer->crs3D(), mRenderContext.crs(), mRenderContext.transformContext() ) );
86 req.setSubsetOfAttributes( attributeNames, layer->fields() );
87 req.setFilterRect( rect );
88
89 //
90 // this will be run in a background thread
91 //
92 mFutureWatcher = new QFutureWatcher<void>( this );
93
94 connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, [this] {
95 if ( !mCanceled )
96 mFactory->mNodesAreLeafs[mNode->tileId().text()] = mNodeIsLeaf;
97 } );
98
99 connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
100
101 const QFuture<void> future = QtConcurrent::run( [req = std::move( req ), this] {
102 const QgsEventTracing::ScopedEvent e( u"3D"_s, u"VL chunk load"_s );
103
104 QgsFeature f;
105 QgsFeatureIterator fi = mSource->getFeatures( req );
106 int featureCount = 0;
107 bool featureLimitReached = false;
108 while ( fi.nextFeature( f ) )
109 {
110 if ( mCanceled )
111 return;
112
113 if ( ++featureCount > mFactory->mMaxFeatures )
114 {
115 featureLimitReached = true;
116 break;
117 }
118
119 mRenderContext.expressionContext().setFeature( f );
120 mHandler->processFeature( f, mRenderContext );
121 }
122
123 if ( !featureLimitReached )
124 {
125 QgsDebugMsgLevel( u"All features fetched for node: %1"_s.arg( mNode->tileId().text() ), 3 );
126
127 if ( featureCount == 0 || std::max<double>( mNode->box3D().width(), mNode->box3D().height() ) < QgsVectorLayer3DTilingSettings::maximumLeafExtent() )
128 mNodeIsLeaf = true;
129 }
130 } );
131
132 // emit finished() as soon as the handler is populated with features
133 mFutureWatcher->setFuture( future );
134}
135
136QgsVectorLayerChunkLoader::~QgsVectorLayerChunkLoader()
137{
138 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
139 {
140 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
141 mFutureWatcher->waitForFinished();
142 }
143}
144
145void QgsVectorLayerChunkLoader::cancel()
146{
147 mCanceled = true;
148}
149
150Qt3DCore::QEntity *QgsVectorLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
151{
152 if ( mHandler->featureCount() == 0 )
153 {
154 // an empty node, so we return no entity. This tags the node as having no data and effectively removes it.
155 // we just make sure first that its initial estimated vertical range does not affect its parents' bboxes calculation
156 mNode->setExactBox3D( QgsBox3D() );
157 mNode->updateParentBoundingBoxesRecursively();
158 return nullptr;
159 }
160
161 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
162 entity->setObjectName( mLayerName + "_" + mNode->tileId().text() );
163 mHandler->finalize( entity, mRenderContext );
164
165 // fix the vertical range of the node from the estimated vertical range to the true range
166 if ( mHandler->zMinimum() != std::numeric_limits<float>::max() && mHandler->zMaximum() != std::numeric_limits<float>::lowest() )
167 {
168 QgsBox3D box = mNode->box3D();
169 box.setZMinimum( mHandler->zMinimum() );
170 box.setZMaximum( mHandler->zMaximum() );
171 mNode->setExactBox3D( box );
172 mNode->updateParentBoundingBoxesRecursively();
173 }
174
175 return entity;
176}
177
178
180
181
182QgsVectorLayerChunkLoaderFactory::QgsVectorLayerChunkLoaderFactory( const Qgs3DRenderContext &context, QgsVectorLayer *vl, QgsAbstract3DSymbol *symbol, double zMin, double zMax, int maxFeatures )
183 : mRenderContext( context )
184 , mLayer( vl )
185 , mSymbol( symbol->clone() )
186 , mMaxFeatures( maxFeatures )
187{
188 if ( context.crs().type() == Qgis::CrsType::Geocentric )
189 {
190 // TODO: add support for handling of vector layers
191 // (we're using dummy quadtree here to make sure the empty extent does not break the scene completely)
192 QgsDebugError( u"Vector layers in globe scenes are not supported yet!"_s );
193 setupQuadtree( QgsBox3D( -7e6, -7e6, -7e6, 7e6, 7e6, 7e6 ), -1, 3 );
194 return;
195 }
196
197 // choose the smaller root extent between context and mLayer ones:
198 QgsRectangle extent = context.extent();
199 const QgsRectangle layerExtentInMapCrs = Qgs3DUtils::tryReprojectExtent2D( mLayer->extent(), mLayer->crs(), context.crs(), context.transformContext() );
200 if ( layerExtentInMapCrs.isValid() )
201 {
202 extent = context.extent().intersect( layerExtentInMapCrs );
203 }
204 if ( extent.isValid() )
205 {
206 QgsBox3D rootBox3D( extent, zMin, zMax );
207 // add small padding to avoid clipping of point features located at the edge of the bounding box
208 rootBox3D.grow( 1.0 );
209
210 const float rootError = static_cast<float>( std::max<double>( rootBox3D.width(), rootBox3D.height() ) * QgsVectorLayer3DTilingSettings::tileGeometryErrorRatio() );
211 setupQuadtree( rootBox3D, rootError );
212 }
213}
214
215QgsChunkLoader *QgsVectorLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
216{
217 return new QgsVectorLayerChunkLoader( this, node );
218}
219
220bool QgsVectorLayerChunkLoaderFactory::canCreateChildren( QgsChunkNode *node )
221{
222 return mNodesAreLeafs.contains( node->tileId().text() );
223}
224
225QVector<QgsChunkNode *> QgsVectorLayerChunkLoaderFactory::createChildren( QgsChunkNode *node ) const
226{
227 if ( mNodesAreLeafs.value( node->tileId().text(), false ) )
228 return {};
229
230 return QgsQuadtreeChunkLoaderFactory::createChildren( node );
231}
232
233
235
236
237QgsVectorLayerChunkedEntity::QgsVectorLayerChunkedEntity(
238 Qgs3DMapSettings *map, QgsVectorLayer *vl, double zMin, double zMax, const QgsVectorLayer3DTilingSettings &tilingSettings, QgsAbstract3DSymbol *symbol
239)
240 : QgsAbstractFeatureBasedChunkedEntity( map, 3, new QgsVectorLayerChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), vl, symbol, zMin, zMax, tilingSettings.maximumChunkFeatures() ), true )
241{
242 onTerrainElevationOffsetChanged();
243 setShowBoundingBoxes( tilingSettings.showBoundingBoxes() );
244}
245
246QgsVectorLayerChunkedEntity::~QgsVectorLayerChunkedEntity()
247{
248 // cancel / wait for jobs
249 cancelActiveJobs();
250}
251
252// if the AltitudeClamping is `Absolute`, do not apply the offset
253bool QgsVectorLayerChunkedEntity::applyTerrainOffset() const
254{
255 QgsVectorLayerChunkLoaderFactory *loaderFactory = static_cast<QgsVectorLayerChunkLoaderFactory *>( mChunkLoaderFactory );
256 if ( loaderFactory )
257 {
258 QString symbolType = loaderFactory->mSymbol.get()->type();
259 if ( symbolType == "line" )
260 {
261 QgsLine3DSymbol *lineSymbol = static_cast<QgsLine3DSymbol *>( loaderFactory->mSymbol.get() );
262 if ( lineSymbol && lineSymbol->altitudeClamping() == Qgis::AltitudeClamping::Absolute )
263 {
264 return false;
265 }
266 }
267 else if ( symbolType == "point" )
268 {
269 QgsPoint3DSymbol *pointSymbol = static_cast<QgsPoint3DSymbol *>( loaderFactory->mSymbol.get() );
270 if ( pointSymbol && pointSymbol->altitudeClamping() == Qgis::AltitudeClamping::Absolute )
271 {
272 return false;
273 }
274 }
275 else if ( symbolType == "polygon" )
276 {
277 QgsPolygon3DSymbol *polygonSymbol = static_cast<QgsPolygon3DSymbol *>( loaderFactory->mSymbol.get() );
278 if ( polygonSymbol && polygonSymbol->altitudeClamping() == Qgis::AltitudeClamping::Absolute )
279 {
280 return false;
281 }
282 }
283 else
284 {
285 QgsDebugMsgLevel( u"QgsVectorLayerChunkedEntity::applyTerrainOffset, unhandled symbol type %1"_s.arg( symbolType ), 2 );
286 }
287 }
288
289 return true;
290}
291
@ Absolute
Elevation is taken directly from feature and is independent of terrain height (final elevation = feat...
Definition qgis.h:4167
@ Geocentric
Geocentric CRS.
Definition qgis.h:2465
Definition of the world.
Rendering context for preparation of 3D entities.
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system used in the 3D scene.
QgsRectangle extent() const
Returns the 3D scene's 2D extent in the 3D scene's CRS.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
QgsFeature3DHandler * createHandlerForSymbol(QgsVectorLayer *layer, const QgsAbstract3DSymbol *symbol)
Creates a feature handler for a symbol, for the specified vector layer.
static QgsRectangle tryReprojectExtent2D(const QgsRectangle &extent, const QgsCoordinateReferenceSystem &crs1, const QgsCoordinateReferenceSystem &crs2, const QgsCoordinateTransformContext &context)
Reprojects extent from crs1 to crs2 coordinate reference system with context context.
Abstract base class for 3D symbols that are used by VectorLayer3DRenderer objects.
static Qgs3DSymbolRegistry * symbol3DRegistry()
Returns registry of available 3D symbols.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
void setZMinimum(double z)
Sets the minimum z value.
Definition qgsbox3d.cpp:94
void setZMaximum(double z)
Sets the maximum z value.
Definition qgsbox3d.cpp:99
Qgis::CrsType type() const
Returns the type of the CRS.
Handles coordinate transforms between two coordinate systems.
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 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.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setCoordinateTransform(const QgsCoordinateTransform &transform)
Sets the coordinate transform which will be used to transform the feature's geometries.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
3D symbol that draws linestring geometries as planar polygons (created from lines using a buffer with...
Qgis::AltitudeClamping altitudeClamping() const
Returns method that determines altitude (whether to clamp to feature to terrain).
QString name
Definition qgsmaplayer.h:87
QgsCoordinateReferenceSystem crs3D
Definition qgsmaplayer.h:92
3D symbol that draws point geometries as 3D objects using one of the predefined shapes.
Qgis::AltitudeClamping altitudeClamping() const
Returns method that determines altitude (whether to clamp to feature to terrain).
3D symbol that draws polygon geometries as planar polygons, optionally extruded (with added walls).
Qgis::AltitudeClamping altitudeClamping() const
Returns method that determines altitude (whether to clamp to feature to terrain).
A rectangle specified with double values.
Defines configuration of how a vector layer gets tiled for 3D rendering.
bool showBoundingBoxes() const
Returns whether to display bounding boxes of entity's tiles (for debugging).
static double maximumLeafExtent()
This is the maximum width or height a tile can have and still be considered a leaf node.
Partial snapshot of vector layer's state (only the members necessary for access to features).
Represents a vector layer which manages a vector based dataset.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59