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