QGIS API Documentation 4.1.0-Master (01362494303)
Loading...
Searching...
No Matches
qgspointcloudlayerchunkloader_p.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloudlayerchunkloader_p.cpp
3 --------------------------------------
4 Date : October 2020
5 Copyright : (C) 2020 by Peter Petrik
6 Email : zilolv 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 <memory>
19
20#include "qgs3dutils.h"
21#include "qgsbox3d.h"
22#include "qgschunknode.h"
23#include "qgseventtracing.h"
24#include "qgslogger.h"
28#include "qgspointcloudindex.h"
29#include "qgspointcloudlayer.h"
32#include "qgsray3d.h"
33#include "qgsraycastcontext.h"
34#include "qgsraycastingutils.h"
35
36#include <QPointSize>
37#include <QString>
38#include <Qt3DCore/QAttribute>
39#include <Qt3DRender/QGraphicsApiFilter>
40#include <Qt3DRender/QShaderProgram>
41#include <Qt3DRender/QTechnique>
42#include <QtConcurrentRun>
43
44#include "moc_qgspointcloudlayerchunkloader_p.cpp"
45
46using namespace Qt::StringLiterals;
47
49
50
52
53QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader(
54 const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr<QgsPointCloud3DSymbol> symbol, const QgsCoordinateTransform &coordinateTransform, double zValueScale, double zValueOffset
55)
56 : QgsChunkLoader( node )
57 , mFactory( factory )
58 , mContext( factory->mRenderContext, coordinateTransform, std::move( symbol ), zValueScale, zValueOffset )
59{}
60
61void QgsPointCloudLayerChunkLoader::start()
62{
63 QgsChunkNode *node = chunk();
64 QgsPointCloudIndex pc = mFactory->mPointCloudIndex;
65 mContext.setAttributes( pc.attributes() );
66
67 const QgsChunkNodeId nodeId = node->tileId();
68 const QgsPointCloudNodeId pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
69
70 Q_ASSERT( pc.hasNode( pcNode ) );
71
72 QgsDebugMsgLevel( u"loading entity %1"_s.arg( node->tileId().text() ), 2 );
73
74 if ( mContext.symbol()->symbolType() == "single-color"_L1 )
75 mHandler = std::make_unique<QgsSingleColorPointCloud3DSymbolHandler>();
76 else if ( mContext.symbol()->symbolType() == "color-ramp"_L1 )
77 mHandler = std::make_unique<QgsColorRampPointCloud3DSymbolHandler>();
78 else if ( mContext.symbol()->symbolType() == "rgb"_L1 )
79 mHandler = std::make_unique<QgsRGBPointCloud3DSymbolHandler>();
80 else if ( mContext.symbol()->symbolType() == "classification"_L1 )
81 {
82 mHandler = std::make_unique<QgsClassificationPointCloud3DSymbolHandler>();
83 const QgsClassificationPointCloud3DSymbol *classificationSymbol = dynamic_cast<const QgsClassificationPointCloud3DSymbol *>( mContext.symbol() );
84 mContext.setFilteredOutCategories( classificationSymbol->getFilteredOutCategories() );
85 }
86
87 //
88 // this will be run in a background thread
89 //
90 mFutureWatcher = new QFutureWatcher<void>( this );
91 connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
92
93 const QgsBox3D box3D = node->box3D();
94 const QFuture<void> future = QtConcurrent::run( [pc = std::move( pc ), pcNode, box3D, this] {
95 const QgsEventTracing::ScopedEvent e( u"3D"_s, u"PC chunk load"_s );
96
97 if ( mContext.isCanceled() )
98 {
99 QgsDebugMsgLevel( u"canceled"_s, 2 );
100 return;
101 }
102
103 QgsPointCloudIndex pc2 = pc; // Copy to discard const
104 mHandler->processNode( pc2, pcNode, mContext );
105
106 if ( mContext.isCanceled() )
107 {
108 QgsDebugMsgLevel( u"canceled"_s, 2 );
109 return;
110 }
111
112 if ( mContext.symbol()->renderAsTriangles() )
113 mHandler->triangulate( pc2, pcNode, mContext, box3D );
114 } );
115
116 // emit finished() as soon as the handler is populated with features
117 mFutureWatcher->setFuture( future );
118}
119
120QgsPointCloudLayerChunkLoader::~QgsPointCloudLayerChunkLoader()
121{
122 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
123 {
124 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
125 mContext.cancelRendering();
126 mFutureWatcher->waitForFinished();
127 }
128}
129
130void QgsPointCloudLayerChunkLoader::cancel()
131{
132 mContext.cancelRendering();
133}
134
135Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
136{
137 QgsPointCloudIndex pc = mFactory->mPointCloudIndex;
138 const QgsChunkNodeId nodeId = mNode->tileId();
139 const QgsPointCloudNodeId pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
140 Q_ASSERT( pc.hasNode( pcNode ) );
141
142 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
143 mHandler->finalize( entity, mContext );
144 return entity;
145}
146
148
149
150QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory(
151 const Qgs3DRenderContext &context, const QgsCoordinateTransform &coordinateTransform, QgsPointCloudIndex pc, QgsPointCloud3DSymbol *symbol, double zValueScale, double zValueOffset, int pointBudget
152)
153 : mRenderContext( context )
154 , mCoordinateTransform( coordinateTransform )
155 , mPointCloudIndex( std::move( pc ) )
156 , mZValueScale( zValueScale )
157 , mZValueOffset( zValueOffset )
158 , mPointBudget( pointBudget )
159{
160 mSymbol.reset( symbol );
161
162 if ( context.crs().type() != Qgis::CrsType::Geocentric ) // extent is not used for globe
163 {
164 try
165 {
166 mExtent = mCoordinateTransform.transformBoundingBox( mRenderContext.extent(), Qgis::TransformDirection::Reverse );
167 }
168 catch ( const QgsCsException & )
169 {
170 // bad luck, can't reproject for some reason
171 QgsDebugError( u"Transformation of extent failed."_s );
172 }
173 }
174}
175
176QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
177{
178 const QgsChunkNodeId id = node->tileId();
179
180 Q_ASSERT( mPointCloudIndex.hasNode( QgsPointCloudNodeId( id.d, id.x, id.y, id.z ) ) );
181 QgsPointCloud3DSymbol *symbol = static_cast<QgsPointCloud3DSymbol *>( mSymbol->clone() );
182 return new QgsPointCloudLayerChunkLoader( this, node, std::unique_ptr<QgsPointCloud3DSymbol>( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
183}
184
185int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node ) const
186{
187 const QgsChunkNodeId id = node->tileId();
188 const QgsPointCloudNodeId n( id.d, id.x, id.y, id.z );
189 Q_ASSERT( mPointCloudIndex.hasNode( n ) );
190 return mPointCloudIndex.getNode( n ).pointCount();
191}
192
193bool QgsPointCloudLayerChunkLoaderFactory::canCreateChildren( QgsChunkNode *node )
194{
195 const QgsChunkNodeId id = node->tileId();
196 const QgsPointCloudNodeId n( id.d, id.x, id.y, id.z );
197 if ( mFutureHierarchyFetches.contains( n ) || mPendingHierarchyFetches.contains( n ) )
198 {
199 return false;
200 }
201
202 if ( mPointCloudIndex.needsHierarchyFetching( n ) )
203 {
204 mFutureHierarchyFetches.insert( n );
205 return false;
206 }
207
208 return true;
209}
210
211void QgsPointCloudLayerChunkLoaderFactory::prepareChildren( QgsChunkNode *node )
212{
213 const QgsChunkNodeId id = node->tileId();
214 const QgsPointCloudNodeId n( id.d, id.x, id.y, id.z );
215 if ( mFutureHierarchyFetches.contains( n ) )
216 {
217 fetchHierarchyForNode( n, node );
218 }
219}
220
221void QgsPointCloudLayerChunkLoaderFactory::fetchHierarchyForNode( const QgsPointCloudNodeId &nodeId, QgsChunkNode *origNode )
222{
223 Q_ASSERT( !mPendingHierarchyFetches.contains( nodeId ) );
224 mFutureHierarchyFetches.remove( nodeId );
225 mPendingHierarchyFetches.insert( nodeId );
226
227 QFutureWatcher<void> *futureWatcher = new QFutureWatcher<void>( this );
228 connect( futureWatcher, &QFutureWatcher<void>::finished, this, [this, origNode, nodeId, futureWatcher] {
229 mPendingHierarchyFetches.remove( nodeId );
230 emit childrenPrepared( origNode );
231 futureWatcher->deleteLater();
232 } );
233 futureWatcher->setFuture( QtConcurrent::run( [this, nodeId] {
234 // we need to make sure that hierarchy exists for this node, children and grand children,
235 // so that createChildren() will not trigger hierarchy fetching
236 // hasNode() will trigger fetching the hierarchy for this node and any missing ancestors
237 ( void ) mPointCloudIndex.hasNode( nodeId );
238 const QVector<QgsPointCloudNodeId> children = nodeId.childrenNodes();
239 for ( const QgsPointCloudNodeId &child : children )
240 {
241 ( void ) mPointCloudIndex.hasNode( child );
242
243 const QVector<QgsPointCloudNodeId> grandchildren = child.childrenNodes();
244 for ( const QgsPointCloudNodeId &grandChild : grandchildren )
245 {
246 ( void ) mPointCloudIndex.hasNode( grandChild );
247 }
248 }
249 } ) );
250}
251
252
253static QgsBox3D nodeBoundsToBox3D( QgsBox3D nodeBounds, const QgsCoordinateTransform &coordinateTransform, double zValueOffset, double zValueScale )
254{
255 QgsVector3D extentMin3D( nodeBounds.xMinimum(), nodeBounds.yMinimum(), nodeBounds.zMinimum() * zValueScale + zValueOffset );
256 QgsVector3D extentMax3D( nodeBounds.xMaximum(), nodeBounds.yMaximum(), nodeBounds.zMaximum() * zValueScale + zValueOffset );
257 QgsCoordinateTransform extentTransform = coordinateTransform;
258 extentTransform.setBallparkTransformsAreAppropriate( true );
259 try
260 {
261 extentMin3D = extentTransform.transform( extentMin3D );
262 extentMax3D = extentTransform.transform( extentMax3D );
263 }
264 catch ( QgsCsException & )
265 {
266 QgsDebugError( u"Error transforming node bounds coordinate"_s );
267 }
268 return QgsBox3D( extentMin3D.x(), extentMin3D.y(), extentMin3D.z(), extentMax3D.x(), extentMax3D.y(), extentMax3D.z() );
269}
270
271
272QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode() const
273{
274 const QgsPointCloudNode pcNode = mPointCloudIndex.getNode( mPointCloudIndex.root() );
275 const QgsBox3D rootNodeBounds = pcNode.bounds();
276 QgsBox3D rootNodeBox3D = nodeBoundsToBox3D( rootNodeBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
277
278 const float error = pcNode.error();
279 QgsChunkNode *node = new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, error );
280 node->setRefinementProcess( mSymbol->renderAsTriangles() ? Qgis::TileRefinementProcess::Replacement : Qgis::TileRefinementProcess::Additive );
281 return node;
282}
283
284QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node ) const
285{
286 QVector<QgsChunkNode *> children;
287 const QgsChunkNodeId nodeId = node->tileId();
288 const float childError = node->error() / 2;
289 const QgsPointCloudNodeId pcId( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
290
291 const QVector<QgsPointCloudNodeId> childrenPcIds = pcId.childrenNodes();
292 for ( const QgsPointCloudNodeId &childPcId : childrenPcIds )
293 {
294 const QgsChunkNodeId childId( childPcId.d(), childPcId.x(), childPcId.y(), childPcId.z() );
295 if ( !mPointCloudIndex.hasNode( childPcId ) )
296 continue;
297 const QgsPointCloudNode childNode = mPointCloudIndex.getNode( childPcId );
298 const QgsBox3D childBounds = childNode.bounds();
299 if ( !mExtent.isEmpty() && !childBounds.toRectangle().intersects( mExtent ) )
300 continue;
301
302 QgsBox3D childBox3D = nodeBoundsToBox3D( childBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
303
304 QgsChunkNode *child = new QgsChunkNode( childId, childBox3D, childError, node );
305 child->setRefinementProcess( mSymbol->renderAsTriangles() ? Qgis::TileRefinementProcess::Replacement : Qgis::TileRefinementProcess::Additive );
306 children << child;
307 }
308 return children;
309}
310
312
313
314static QgsChunkNode *findChunkNodeFromNodeId( QgsChunkNode *rootNode, QgsPointCloudNodeId nodeId )
315{
316 // find path from the node to the root
317 QVector<QgsPointCloudNodeId> parentIds;
318 while ( nodeId.d() > 0 )
319 {
320 parentIds << nodeId;
321 nodeId = nodeId.parentNode();
322 }
323
324 // now descend from the root to the node in the QgsChunkNode hierarchy
325 QgsChunkNode *chunk = rootNode;
326 while ( !parentIds.empty() )
327 {
328 QgsPointCloudNodeId p = parentIds.takeLast();
329 QgsChunkNodeId childNodeId( p.d(), p.x(), p.y(), p.z() );
330
331 if ( !chunk->hasChildrenPopulated() )
332 return nullptr;
333
334 QgsChunkNode *chunkChild = nullptr;
335 QgsChunkNode *const *children = chunk->children();
336 for ( int i = 0; i < chunk->childCount(); ++i )
337 {
338 if ( children[i]->tileId() == childNodeId )
339 {
340 chunkChild = children[i];
341 break;
342 }
343 }
344 Q_ASSERT( chunkChild );
345 chunk = chunkChild;
346 }
347 return chunk;
348}
349
350
351QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity(
352 Qgs3DMapSettings *map,
354 const int indexPosition,
355 const QgsCoordinateTransform &coordinateTransform,
356 QgsPointCloud3DSymbol *symbol,
357 float maximumScreenSpaceError,
358 bool showBoundingBoxes,
359 double zValueScale,
360 double zValueOffset,
361 int pointBudget
362)
363 : QgsChunkedEntity( map, maximumScreenSpaceError, new QgsPointCloudLayerChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), coordinateTransform, resolveIndex( pcl, indexPosition ), symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
364 , mLayer( pcl )
365 , mIndexPosition( indexPosition )
366{
367 setShowBoundingBoxes( showBoundingBoxes );
368
369 if ( pcl->supportsEditing() )
370 {
371 mChunkUpdaterFactory = std::make_unique<QgsChunkUpdaterFactory>( mChunkLoaderFactory );
372 // when editing starts or stops, we need to update our index to use the editing index (or not)
373 if ( ( pcl->isVpc() && indexPosition >= 0 ) || ( !pcl->isVpc() ) )
374 {
375 connect( pcl, &QgsPointCloudLayer::editingStarted, this, &QgsPointCloudLayerChunkedEntity::updateIndex );
376 connect( pcl, &QgsPointCloudLayer::editingStopped, this, &QgsPointCloudLayerChunkedEntity::updateIndex );
377 connect( pcl, &QgsPointCloudLayer::chunkAttributeValuesChanged, this, [this]( QgsPointCloudNodeId n, int indexPosition ) {
378 if ( indexPosition != mIndexPosition )
379 return;
380
381 QgsChunkNode *node = findChunkNodeFromNodeId( mRootNode, n );
382 if ( node )
383 {
384 updateNodes( QList<QgsChunkNode *>() << node, mChunkUpdaterFactory.get() );
385 }
386 } );
387 }
388 }
389}
390
391QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
392{
393 // cancel / wait for jobs
394 cancelActiveJobs();
395}
396
397QgsPointCloudIndex QgsPointCloudLayerChunkedEntity::resolveIndex( const QgsPointCloudLayer *pcl, int indexPosition )
398{
399 if ( indexPosition >= 0 && pcl->isVpc() )
400 return pcl->subIndexes().at( indexPosition ).index();
401 else if ( indexPosition == -1 && !pcl->isVpc() )
402 return pcl->index();
403 else if ( indexPosition < -1 && pcl->isVpc() )
404 {
405 const int ovId = -indexPosition - 2;
406 return pcl->overviews().at( ovId );
407 }
408 else
409 return QgsPointCloudIndex();
410}
411
412void QgsPointCloudLayerChunkedEntity::updateIndex()
413{
414 if ( mLayer->isVpc() )
415 {
416 if ( mIndexPosition >= 0 )
417 static_cast<QgsPointCloudLayerChunkLoaderFactory *>( mChunkLoaderFactory )->mPointCloudIndex = mLayer->subIndexes().at( mIndexPosition ).index();
418 }
419 else
420 static_cast<QgsPointCloudLayerChunkLoaderFactory *>( mChunkLoaderFactory )->mPointCloudIndex = mLayer->index();
421}
422
423QList<QgsRayCastHit> QgsPointCloudLayerChunkedEntity::rayIntersection( const QgsRay3D &ray, const QgsRayCastContext &context ) const
424{
425 QList<QgsRayCastHit> result;
426 QgsPointCloudLayerChunkLoaderFactory *factory = static_cast<QgsPointCloudLayerChunkLoaderFactory *>( mChunkLoaderFactory );
427
428 const QgsPointCloud3DSymbol *symbol = factory->mSymbol.get();
429 // Symbol can be null in case of no rendering enabled
430 if ( !symbol )
431 return result;
432
433 // transform ray
434 const QgsVector3D rayOriginMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() );
435 const QgsVector3D pointMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() + ray.origin().length() * ray.direction().normalized() );
436 QgsVector3D rayDirectionMapCoords = pointMapCoords - rayOriginMapCoords;
437 rayDirectionMapCoords.normalize();
438
439 // We're using the angle as a tolerance, effectively meaning we're fetching points intersecting a cone.
440 // This may be revisited to use a cylinder instead, if the balance between near/far points does not scale
441 // well with different point sizes, screen sizes and fov values.
442 const double limitAngle = context.angleThreshold() * M_PI / 180.;
443
444 // adjust ray to elevation properties
445 const QgsVector3D adjustedRayOrigin = QgsVector3D( rayOriginMapCoords.x(), rayOriginMapCoords.y(), ( rayOriginMapCoords.z() - factory->mZValueOffset ) / factory->mZValueScale );
446 QgsVector3D adjustedRayDirection = QgsVector3D( rayDirectionMapCoords.x(), rayDirectionMapCoords.y(), rayDirectionMapCoords.z() / factory->mZValueScale );
447 adjustedRayDirection.normalize();
448
449 QgsPointCloudIndex index = factory->mPointCloudIndex;
450
451 const QgsPointCloudAttributeCollection attributeCollection = index.attributes();
452 QgsPointCloudRequest request;
453 request.setAttributes( attributeCollection );
454
455 double minDist = -1.;
456 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
457 for ( QgsChunkNode *node : activeNodes )
458 {
459 const QgsChunkNodeId id = node->tileId();
460 const QgsPointCloudNodeId n( id.d, id.x, id.y, id.z );
461
462 if ( !index.hasNode( n ) )
463 continue;
464
465 const QgsAABB nodeBbox = Qgs3DUtils::mapToWorldExtent( node->box3D(), mMapSettings->origin() );
466 if ( !QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
467 continue;
468
469 std::unique_ptr<QgsPointCloudBlock> block( index.nodeData( n, request ) );
470 if ( !block )
471 continue;
472
473 const QgsVector3D blockScale = block->scale();
474 const QgsVector3D blockOffset = block->offset();
475
476 const char *ptr = block->data();
477 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
478 const std::size_t recordSize = blockAttributes.pointRecordSize();
479 int xOffset = 0, yOffset = 0, zOffset = 0;
480 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( u"X"_s, xOffset )->type();
481 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( u"Y"_s, yOffset )->type();
482 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( u"Z"_s, zOffset )->type();
483 for ( int i = 0; i < block->pointCount(); ++i )
484 {
485 double x, y, z;
486 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, blockScale, blockOffset, x, y, z );
487 const QgsVector3D point( x, y, z );
488
489 // check whether point is in front of the ray
490 // similar to QgsRay3D::isInFront(), but using doubles
491 QgsVector3D vectorToPoint = point - adjustedRayOrigin;
492 vectorToPoint.normalize();
493 if ( QgsVector3D::dotProduct( vectorToPoint, adjustedRayDirection ) < 0.0 )
494 continue;
495
496 // calculate the angle between the point and the projected point
497 // similar to QgsRay3D::angleToPoint(), but using doubles
498 const QgsVector3D projPoint = adjustedRayOrigin + adjustedRayDirection * QgsVector3D::dotProduct( point - adjustedRayOrigin, adjustedRayDirection );
499
500 const double d1 = projPoint.distance( adjustedRayOrigin );
501 const double d2 = projPoint.distance( point );
502 const double angle = std::atan2( d2, d1 );
503
504 if ( angle > limitAngle )
505 continue;
506
507 const double dist = rayOriginMapCoords.distance( point );
508
509 if ( minDist < 0 || dist < minDist )
510 {
511 minDist = dist;
512 }
513 else if ( context.singleResult() )
514 {
515 continue;
516 }
517
518 // Note : applying elevation properties is done in fromPointCloudIdentificationToIdentifyResults
519 QVariantMap pointAttr = QgsPointCloudAttribute::getAttributeMap( ptr, i * recordSize, blockAttributes );
520 pointAttr[u"X"_s] = x;
521 pointAttr[u"Y"_s] = y;
522 pointAttr[u"Z"_s] = z;
523
524 QgsRayCastHit hit;
525 hit.setDistance( dist );
526 hit.setMapCoordinates( point );
527 hit.setProperties( pointAttr );
528 if ( context.singleResult() )
529 result.clear();
530 result.append( hit );
531 }
532 }
533 return result;
534}
@ Geocentric
Geocentric CRS.
Definition qgis.h:2465
@ Additive
When tile is refined its content should be used alongside its children simultaneously.
Definition qgis.h:6158
@ Replacement
When tile is refined then its children should be used in place of itself.
Definition qgis.h:6157
Definition of the world.
Rendering context for preparation of 3D entities.
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system used in the 3D scene.
static QgsAABB mapToWorldExtent(const QgsRectangle &extent, double zMin, double zMax, const QgsVector3D &mapOrigin)
Converts map extent to axis aligned bounding box in 3D world coordinates.
Axis-aligned bounding box - in world coords.
Definition qgsaabb.h:33
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
double yMaximum() const
Returns the maximum y value.
Definition qgsbox3d.h:240
double xMinimum() const
Returns the minimum x value.
Definition qgsbox3d.h:205
double zMaximum() const
Returns the maximum z value.
Definition qgsbox3d.h:268
double xMaximum() const
Returns the maximum x value.
Definition qgsbox3d.h:212
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:388
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:261
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:233
3D symbol that draws point cloud geometries as 3D objects using classification of the dataset.
QgsPointCloudCategoryList getFilteredOutCategories() const
Gets the list of categories of the classification that should not be rendered.
Qgis::CrsType type() const
Returns the type of the CRS.
Handles coordinate transforms between two coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
void editingStarted()
Emitted when editing on this layer has started.
3D symbol that draws point cloud geometries as 3D objects.
A collection of point cloud attributes.
int pointRecordSize() const
Returns total size of record.
const QgsPointCloudAttribute * find(const QString &attributeName, int &offset) const
Finds the attribute with the name.
QVector< QgsPointCloudAttribute > attributes() const
Returns all attributes.
DataType
Systems of unit measurement.
static void getPointXYZ(const char *ptr, int i, std::size_t pointRecordSize, int xOffset, QgsPointCloudAttribute::DataType xType, int yOffset, QgsPointCloudAttribute::DataType yType, int zOffset, QgsPointCloudAttribute::DataType zType, const QgsVector3D &indexScale, const QgsVector3D &indexOffset, double &x, double &y, double &z)
Retrieves the x, y, z values for the point at index i.
static QVariantMap getAttributeMap(const char *data, std::size_t recordOffset, const QgsPointCloudAttributeCollection &attributeCollection)
Retrieves all the attributes of a point.
DataType type() const
Returns the data type.
Smart pointer for QgsAbstractPointCloudIndex.
std::unique_ptr< QgsPointCloudBlock > nodeData(QgsPointCloudNodeId n, const QgsPointCloudRequest &request)
Returns node data block.
bool hasNode(QgsPointCloudNodeId id) const
Returns whether the octree contain given node.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
Represents a map layer supporting display of point clouds.
void chunkAttributeValuesChanged(QgsPointCloudNodeId n, const int position)
Emitted when a node gets some attribute values of some points changed.
QgsPointCloudIndex index() const
Returns the point cloud index associated with the layer.
bool isVpc() const
Returns whether the layer has a virtual point cloud data provider or not.
QVector< QgsPointCloudIndex > overviews() const
Returns a list of all overview point cloud indexes associated with the layer (only if the layer has a...
QVector< QgsPointCloudSubIndex > subIndexes() const
Returns point cloud indexes associated with the layer (only if the layer has a virtual point cloud da...
bool supportsEditing() const override
Returns whether the layer supports editing or not.
Represents an indexed point cloud node's position in octree.
int d() const
Returns d.
int y() const
Returns y.
int x() const
Returns x.
int z() const
Returns z.
QgsPointCloudNodeId parentNode() const
Returns the parent of the node.
QVector< QgsPointCloudNodeId > childrenNodes() const
Returns the node's 8 direct child nodes.
Keeps metadata for an indexed point cloud node.
float error() const
Returns node's error in map units (used to determine in whether the node has enough detail for the cu...
QgsBox3D bounds() const
Returns node's bounding cube in CRS coords.
Point cloud data request.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.
A representation of a ray in 3D.
Definition qgsray3d.h:31
QVector3D origin() const
Returns the origin of the ray.
Definition qgsray3d.h:43
QVector3D direction() const
Returns the direction of the ray see setDirection().
Definition qgsray3d.h:49
Responsible for defining parameters of the ray casting operations in 3D map canvases.
float angleThreshold() const
Sets an angle threshold in degrees for ray intersections, effectively turning a ray into a cone.
bool singleResult() const
Returns whether to fetch only the closest hit for each layer or entity type.
Contains details about the ray intersecting entities when ray casting in a 3D map canvas.
void setProperties(const QVariantMap &attributes)
Sets the point cloud point attributes, empty map if hit was not on a point cloud point.
void setMapCoordinates(const QgsVector3D &point)
Sets the hit point position in 3d map coordinates.
void setDistance(double distance)
Sets the hit's distance from the ray's origin.
bool intersects(const QgsRectangle &rect) const
Returns true when rectangle intersects with other rectangle.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:33
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:60
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:62
double distance(const QgsVector3D &other) const
Returns the distance with the other QgsVector3D.
static double dotProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the dot product of two vectors.
double x() const
Returns X coordinate.
Definition qgsvector3d.h:58
void normalize()
Normalizes the current vector in place.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored).
bool rayBoxIntersection(const QgsRay3D &ray, const QgsAABB &nodeBbox)
Tests whether an axis aligned box is intersected by a ray.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59