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