QGIS API Documentation 4.3.0-Master (c8cdfe19d7b)
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 "qgschunkloader.h"
23#include "qgschunknode.h"
24#include "qgseventtracing.h"
25#include "qgslogger.h"
29#include "qgspointcloudindex.h"
30#include "qgspointcloudlayer.h"
33#include "qgsray3d.h"
34#include "qgsraycastcontext.h"
35#include "qgsraycastingutils.h"
36#include "qgsthreadingutils.h"
37
38#include <QPointSize>
39#include <QString>
40#include <Qt3DCore/QAttribute>
41#include <Qt3DRender/QGraphicsApiFilter>
42#include <Qt3DRender/QShaderProgram>
43#include <Qt3DRender/QTechnique>
44#include <QtConcurrentRun>
45
46#include "moc_qgspointcloudlayerchunkloader_p.cpp"
47
48using namespace Qt::StringLiterals;
49
51
52QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader(
53 const Qgs3DRenderContext &context, const QgsCoordinateTransform &coordinateTransform, QgsPointCloudIndex pc, QgsPointCloud3DSymbol *symbol, double zValueScale, double zValueOffset
54)
55 : mRenderContext( context )
56 , mCoordinateTransform( coordinateTransform )
57 , mPointCloudIndex( std::move( pc ) )
58 , mZValueScale( zValueScale )
59 , mZValueOffset( zValueOffset )
60{
61 mSymbol.reset( symbol );
62
63 if ( context.crs().type() != Qgis::CrsType::Geocentric ) // extent is not used for globe
64 {
65 try
66 {
67 mExtent = mCoordinateTransform.transformBoundingBox( mRenderContext.extent(), Qgis::TransformDirection::Reverse );
68 }
69 catch ( const QgsCsException & )
70 {
71 // bad luck, can't reproject for some reason
72 QgsDebugError( u"Transformation of extent failed."_s );
73 }
74 }
75}
76
77QFuture<QgsChunkLoaderResult> QgsPointCloudLayerChunkLoader::loadChunk( QgsChunkNode *node )
78{
79 const QgsChunkNodeId id = node->tileId();
80
81 Q_ASSERT( mPointCloudIndex.hasNode( QgsPointCloudNodeId( id.d, id.x, id.y, id.z ) ) );
82 QgsPointCloud3DSymbol *symbol = static_cast<QgsPointCloud3DSymbol *>( mSymbol->clone() );
83
84 // Wrapped in shared_ptr to allow copying
85 auto context = std::make_shared<QgsPointCloud3DRenderContext>( mRenderContext, mCoordinateTransform, std::unique_ptr<QgsPointCloud3DSymbol>( symbol ), mZValueScale, mZValueOffset );
86
87 QgsPointCloudIndex pc = mPointCloudIndex;
88 context->setAttributes( pc.attributes() );
89
90 const QgsChunkNodeId nodeId = node->tileId();
91 const QgsPointCloudNodeId pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
92
93 Q_ASSERT( pc.hasNode( pcNode ) );
94
95 QgsDebugMsgLevel( u"loading entity %1"_s.arg( node->tileId().text() ), 2 );
96
97 std::shared_ptr<QgsPointCloud3DSymbolHandler> handler;
98 if ( context->symbol()->symbolType() == "single-color"_L1 )
99 handler = std::make_shared<QgsSingleColorPointCloud3DSymbolHandler>();
100 else if ( context->symbol()->symbolType() == "color-ramp"_L1 )
101 handler = std::make_shared<QgsColorRampPointCloud3DSymbolHandler>();
102 else if ( context->symbol()->symbolType() == "rgb"_L1 )
103 handler = std::make_shared<QgsRGBPointCloud3DSymbolHandler>();
104 else if ( context->symbol()->symbolType() == "classification"_L1 )
105 {
106 handler = std::make_shared<QgsClassificationPointCloud3DSymbolHandler>();
107 const QgsClassificationPointCloud3DSymbol *classificationSymbol = dynamic_cast<const QgsClassificationPointCloud3DSymbol *>( context->symbol() );
108 context->setFilteredOutCategories( classificationSymbol->getFilteredOutCategories() );
109 }
110
111 const QgsBox3D box3D = node->box3D();
112 return QtConcurrent::run( [pc = std::move( pc ), node, pcNode, box3D, context, handler]( QPromise<QgsChunkLoaderResult> &promise ) mutable {
113 const QgsScopedEvent e( u"3D"_s, u"PC chunk load"_s );
114
115 if ( promise.isCanceled() )
116 {
117 QgsDebugMsgLevel( u"canceled"_s, 2 );
118 promise.addResult( QgsChunkLoaderResult::sEmpty );
119 return;
120 }
121
122 handler->processNode( pc, pcNode, *context );
123
124 if ( promise.isCanceled() )
125 {
126 QgsDebugMsgLevel( u"canceled"_s, 2 );
127 promise.addResult( QgsChunkLoaderResult::sEmpty );
128 return;
129 }
130
131 if ( context->symbol()->renderAsTriangles() )
132 handler->triangulate( pc, pcNode, *context, box3D );
133
134 promise.addResult( QgsChunkLoaderResult { [node, handler, context, pc]( Qt3DCore::QEntity *parent ) -> Qt3DCore::QEntity * {
136 const QgsChunkNodeId nodeId = node->tileId();
137 const QgsPointCloudNodeId pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
138 Q_ASSERT( pc.hasNode( pcNode ) );
139
140 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
141 handler->finalize( entity, *context );
142 return entity;
143 } } );
144 } );
145}
146
147int QgsPointCloudLayerChunkLoader::primitivesCount( QgsChunkNode *node ) const
148{
149 const QgsChunkNodeId id = node->tileId();
150 const QgsPointCloudNodeId n( id.d, id.x, id.y, id.z );
151 Q_ASSERT( mPointCloudIndex.hasNode( n ) );
152 return mPointCloudIndex.getNode( n ).pointCount();
153}
154
155QFuture<void> QgsPointCloudLayerChunkLoader::fetchHierarchyForNode( const QgsPointCloudNodeId &nodeId )
156{
157 return QtConcurrent::run( [index = mPointCloudIndex, nodeId] {
158 // we need to make sure that hierarchy exists for this node, children and grand children,
159 // so that createChildren() will not trigger hierarchy fetching
160 // hasNode() will trigger fetching the hierarchy for this node and any missing ancestors
161 ( void ) index.hasNode( nodeId );
162 const QVector<QgsPointCloudNodeId> children = nodeId.childrenNodes();
163 for ( const QgsPointCloudNodeId &child : children )
164 {
165 ( void ) index.hasNode( child );
166
167 const QVector<QgsPointCloudNodeId> grandchildren = child.childrenNodes();
168 for ( const QgsPointCloudNodeId &grandChild : grandchildren )
169 {
170 ( void ) index.hasNode( grandChild );
171 }
172 }
173 } );
174}
175
176
177static QgsBox3D nodeBoundsToBox3D( QgsBox3D nodeBounds, const QgsCoordinateTransform &coordinateTransform, double zValueOffset, double zValueScale )
178{
179 QgsVector3D extentMin3D( nodeBounds.xMinimum(), nodeBounds.yMinimum(), nodeBounds.zMinimum() * zValueScale + zValueOffset );
180 QgsVector3D extentMax3D( nodeBounds.xMaximum(), nodeBounds.yMaximum(), nodeBounds.zMaximum() * zValueScale + zValueOffset );
181 QgsCoordinateTransform extentTransform = coordinateTransform;
182 extentTransform.setBallparkTransformsAreAppropriate( true );
183 try
184 {
185 extentMin3D = extentTransform.transform( extentMin3D );
186 extentMax3D = extentTransform.transform( extentMax3D );
187 }
188 catch ( QgsCsException & )
189 {
190 QgsDebugError( u"Error transforming node bounds coordinate"_s );
191 }
192 return QgsBox3D( extentMin3D.x(), extentMin3D.y(), extentMin3D.z(), extentMax3D.x(), extentMax3D.y(), extentMax3D.z() );
193}
194
195
196QgsChunkNode *QgsPointCloudLayerChunkLoader::createRootNode() const
197{
198 const QgsPointCloudNode pcNode = mPointCloudIndex.getNode( mPointCloudIndex.root() );
199 const QgsBox3D rootNodeBounds = pcNode.bounds();
200 QgsBox3D rootNodeBox3D = nodeBoundsToBox3D( rootNodeBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
201
202 const float error = pcNode.error();
203 QgsChunkNode *node = new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, error );
204 node->setRefinementProcess( mSymbol->renderAsTriangles() ? Qgis::TileRefinementProcess::Replacement : Qgis::TileRefinementProcess::Additive );
205 return node;
206}
207
208QFuture<QVector<QgsChunkNode *>> QgsPointCloudLayerChunkLoader::createChildren( QgsChunkNode *node )
209{
210 const QgsChunkNodeId id = node->tileId();
211 const QgsPointCloudNodeId n( id.d, id.x, id.y, id.z );
212 if ( mPointCloudIndex.needsHierarchyFetching( n ) )
213 return fetchHierarchyForNode( n ).then( this, [this, node]() { return createChildrenInner( node ); } );
214 else
215 return QtFuture::makeReadyValueFuture( createChildrenInner( node ) );
216}
217
218QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoader::createChildrenInner( QgsChunkNode *node )
219{
220 QVector<QgsChunkNode *> children;
221 const QgsChunkNodeId nodeId = node->tileId();
222 const float childError = node->error() / 2;
223 const QgsPointCloudNodeId pcId( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
224
225 const QVector<QgsPointCloudNodeId> childrenPcIds = pcId.childrenNodes();
226 for ( const QgsPointCloudNodeId &childPcId : childrenPcIds )
227 {
228 const QgsChunkNodeId childId( childPcId.d(), childPcId.x(), childPcId.y(), childPcId.z() );
229 if ( !mPointCloudIndex.hasNode( childPcId ) )
230 continue;
231 const QgsPointCloudNode childNode = mPointCloudIndex.getNode( childPcId );
232 const QgsBox3D childBounds = childNode.bounds();
233 if ( !mExtent.isEmpty() && !childBounds.toRectangle().intersects( mExtent ) )
234 continue;
235
236 QgsBox3D childBox3D = nodeBoundsToBox3D( childBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
237
238 QgsChunkNode *child = new QgsChunkNode( childId, childBox3D, childError, node );
239 child->setRefinementProcess( mSymbol->renderAsTriangles() ? Qgis::TileRefinementProcess::Replacement : Qgis::TileRefinementProcess::Additive );
240 children << child;
241 }
242 return children;
243}
244
246
247
248static QgsChunkNode *findChunkNodeFromNodeId( QgsChunkNode *rootNode, QgsPointCloudNodeId nodeId )
249{
250 // find path from the node to the root
251 QVector<QgsPointCloudNodeId> parentIds;
252 while ( nodeId.d() > 0 )
253 {
254 parentIds << nodeId;
255 nodeId = nodeId.parentNode();
256 }
257
258 // now descend from the root to the node in the QgsChunkNode hierarchy
259 QgsChunkNode *chunk = rootNode;
260 while ( !parentIds.empty() )
261 {
262 QgsPointCloudNodeId p = parentIds.takeLast();
263 QgsChunkNodeId childNodeId( p.d(), p.x(), p.y(), p.z() );
264
265 if ( !chunk->hasChildrenPopulated() )
266 return nullptr;
267
268 QgsChunkNode *chunkChild = nullptr;
269 QgsChunkNode *const *children = chunk->children();
270 for ( int i = 0; i < chunk->childCount(); ++i )
271 {
272 if ( children[i]->tileId() == childNodeId )
273 {
274 chunkChild = children[i];
275 break;
276 }
277 }
278 Q_ASSERT( chunkChild );
279 chunk = chunkChild;
280 }
281 return chunk;
282}
283
284
285QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity(
286 Qgs3DMapSettings *map,
288 const int indexPosition,
289 const QgsCoordinateTransform &coordinateTransform,
290 QgsPointCloud3DSymbol *symbol,
291 float maximumScreenSpaceError,
292 bool showBoundingBoxes,
293 double zValueScale,
294 double zValueOffset,
295 int pointBudget
296)
297 : QgsChunkedEntity( map, maximumScreenSpaceError, new QgsPointCloudLayerChunkLoader( Qgs3DRenderContext::fromMapSettings( map ), coordinateTransform, resolveIndex( pcl, indexPosition ), symbol, zValueScale, zValueOffset ), true, pointBudget )
298 , mLayer( pcl )
299 , mIndexPosition( indexPosition )
300{
301 setShowBoundingBoxes( showBoundingBoxes );
302
303 if ( pcl->supportsEditing() )
304 {
305 // when editing starts or stops, we need to update our index to use the editing index (or not)
306 if ( ( pcl->isVpc() && indexPosition >= 0 ) || ( !pcl->isVpc() ) )
307 {
308 connect( pcl, &QgsPointCloudLayer::editingStarted, this, &QgsPointCloudLayerChunkedEntity::updateIndex );
309 connect( pcl, &QgsPointCloudLayer::editingStopped, this, &QgsPointCloudLayerChunkedEntity::updateIndex );
310 connect( pcl, &QgsPointCloudLayer::chunkAttributeValuesChanged, this, [this]( QgsPointCloudNodeId n, int indexPosition ) {
311 if ( indexPosition != mIndexPosition )
312 return;
313
314 QgsChunkNode *node = findChunkNodeFromNodeId( mRootNode.get(), n );
315 if ( node )
316 {
317 updateNodes( QList<QgsChunkNode *>() << node );
318 }
319 } );
320 }
321 }
322}
323
324QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
325{
326 // cancel / wait for jobs
327 cancelActiveJobs();
328}
329
330QgsPointCloudIndex QgsPointCloudLayerChunkedEntity::resolveIndex( const QgsPointCloudLayer *pcl, int indexPosition )
331{
332 if ( indexPosition >= 0 && pcl->isVpc() )
333 return pcl->subIndexes().at( indexPosition ).index();
334 else if ( indexPosition == -1 && !pcl->isVpc() )
335 return pcl->index();
336 else if ( indexPosition < -1 && pcl->isVpc() )
337 {
338 const int ovId = -indexPosition - 2;
339 return pcl->overviews().at( ovId );
340 }
341 else
342 return QgsPointCloudIndex();
343}
344
345void QgsPointCloudLayerChunkedEntity::updateIndex()
346{
347 if ( mLayer->isVpc() )
348 {
349 if ( mIndexPosition >= 0 )
350 static_cast<QgsPointCloudLayerChunkLoader *>( mChunkLoader )->mPointCloudIndex = mLayer->subIndexes().at( mIndexPosition ).index();
351 }
352 else
353 static_cast<QgsPointCloudLayerChunkLoader *>( mChunkLoader )->mPointCloudIndex = mLayer->index();
354}
355
356QList<QgsRayCastHit> QgsPointCloudLayerChunkedEntity::rayIntersection( const QgsRay3D &ray, const QgsRayCastContext &context ) const
357{
358 QList<QgsRayCastHit> result;
359 QgsPointCloudLayerChunkLoader *loader = static_cast<QgsPointCloudLayerChunkLoader *>( mChunkLoader );
360
361 const QgsPointCloud3DSymbol *symbol = loader->mSymbol.get();
362 // Symbol can be null in case of no rendering enabled
363 if ( !symbol )
364 return result;
365
366 // transform ray
367 const QgsVector3D rayOriginMapCoords = mMapSettings->worldToMapCoordinates( ray.origin() );
368 const QgsVector3D pointMapCoords = mMapSettings->worldToMapCoordinates( ray.point( ray.origin().length() ) );
369 QgsVector3D rayDirectionMapCoords = pointMapCoords - rayOriginMapCoords;
370 rayDirectionMapCoords.normalize();
371
372 // We're using the angle as a tolerance, effectively meaning we're fetching points intersecting a cone.
373 // This may be revisited to use a cylinder instead, if the balance between near/far points does not scale
374 // well with different point sizes, screen sizes and fov values.
375 const double limitAngle = context.angleThreshold() * M_PI / 180.;
376
377 // adjust ray to elevation properties
378 const QgsVector3D adjustedRayOrigin = QgsVector3D( rayOriginMapCoords.x(), rayOriginMapCoords.y(), ( rayOriginMapCoords.z() - loader->mZValueOffset ) / loader->mZValueScale );
379 QgsVector3D adjustedRayDirection = QgsVector3D( rayDirectionMapCoords.x(), rayDirectionMapCoords.y(), rayDirectionMapCoords.z() / loader->mZValueScale );
380 adjustedRayDirection.normalize();
381
382 QgsPointCloudIndex index = loader->mPointCloudIndex;
383
384 const QgsPointCloudAttributeCollection attributeCollection = index.attributes();
385 QgsPointCloudRequest request;
386 request.setAttributes( attributeCollection );
387
388 double minDist = -1.;
389 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
390 for ( QgsChunkNode *node : activeNodes )
391 {
392 const QgsChunkNodeId id = node->tileId();
393 const QgsPointCloudNodeId n( id.d, id.x, id.y, id.z );
394
395 if ( !index.hasNode( n ) )
396 continue;
397
398 const QgsAABB nodeBbox = Qgs3DUtils::mapToWorldExtent( node->box3D(), mMapSettings->origin() );
399 if ( !QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
400 continue;
401
402 std::unique_ptr<QgsPointCloudBlock> block( index.nodeData( n, request ) );
403 if ( !block )
404 continue;
405
406 const QgsVector3D blockScale = block->scale();
407 const QgsVector3D blockOffset = block->offset();
408
409 const char *ptr = block->data();
410 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
411 const std::size_t recordSize = blockAttributes.pointRecordSize();
412 int xOffset = 0, yOffset = 0, zOffset = 0;
413 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( u"X"_s, xOffset )->type();
414 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( u"Y"_s, yOffset )->type();
415 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( u"Z"_s, zOffset )->type();
416 for ( int i = 0; i < block->pointCount(); ++i )
417 {
418 double x, y, z;
419 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, blockScale, blockOffset, x, y, z );
420 const QgsVector3D point( x, y, z );
421
422 // check whether point is in front of the ray
423 // similar to QgsRay3D::isInFront(), but using doubles
424 QgsVector3D vectorToPoint = point - adjustedRayOrigin;
425 vectorToPoint.normalize();
426 if ( QgsVector3D::dotProduct( vectorToPoint, adjustedRayDirection ) < 0.0 )
427 continue;
428
429 // calculate the angle between the point and the projected point
430 // similar to QgsRay3D::angleToPoint(), but using doubles
431 const QgsVector3D projPoint = adjustedRayOrigin + adjustedRayDirection * QgsVector3D::dotProduct( point - adjustedRayOrigin, adjustedRayDirection );
432
433 const double d1 = projPoint.distance( adjustedRayOrigin );
434 const double d2 = projPoint.distance( point );
435 const double angle = std::atan2( d2, d1 );
436
437 if ( angle > limitAngle )
438 continue;
439
440 const double dist = rayOriginMapCoords.distance( point );
441
442 if ( minDist < 0 || dist < minDist )
443 {
444 minDist = dist;
445 }
446 else if ( context.singleResult() )
447 {
448 continue;
449 }
450
451 // Note : applying elevation properties is done in fromPointCloudIdentificationToIdentifyResults
452 QVariantMap pointAttr = QgsPointCloudAttribute::getAttributeMap( ptr, i * recordSize, blockAttributes );
453 pointAttr[u"X"_s] = x;
454 pointAttr[u"Y"_s] = y;
455 pointAttr[u"Z"_s] = z;
456
457 QgsRayCastHit hit;
458 hit.setDistance( dist );
459 hit.setMapCoordinates( point );
460 hit.setProperties( pointAttr );
461 if ( context.singleResult() )
462 result.clear();
463 result.append( hit );
464 }
465 }
466 return result;
467}
@ Geocentric
Geocentric CRS.
Definition qgis.h:2516
@ Additive
When tile is refined its content should be used alongside its children simultaneously.
Definition qgis.h:6490
@ Replacement
When tile is refined then its children should be used in place of itself.
Definition qgis.h:6489
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 point(float distance) const
Returns the point along the ray with the specified distance from the ray's origin.
Definition qgsray3d.cpp:62
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:80
#define QgsDebugError(str)
Definition qgslogger.h:71
#define QGIS_CHECK_MAIN_THREAD_ACCESS