QGIS API Documentation 3.99.0-Master (752b475928d)
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 "qgs3dutils.h"
19#include "qgsbox3d.h"
20#include "qgschunknode.h"
21#include "qgseventtracing.h"
22#include "qgslogger.h"
26#include "qgspointcloudindex.h"
29#include "qgsray3d.h"
30#include "qgsraycastcontext.h"
31#include "qgsraycastingutils.h"
32
33#include <QtConcurrent>
34
35#include "moc_qgspointcloudlayerchunkloader_p.cpp"
36
37#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
38#include <Qt3DRender/QAttribute>
39#else
40#include <Qt3DCore/QAttribute>
41#endif
42#include <Qt3DRender/QTechnique>
43#include <Qt3DRender/QShaderProgram>
44#include <Qt3DRender/QGraphicsApiFilter>
45#include <QPointSize>
46#include <memory>
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( QStringLiteral( "loading entity %1" ).arg( node->tileId().text() ), 2 );
72
73 // suppress false positive clang tidy warning
74 // NOLINTBEGIN(bugprone-branch-clone)
75 if ( mContext.symbol()->symbolType() == QLatin1String( "single-color" ) )
76 mHandler = std::make_unique<QgsSingleColorPointCloud3DSymbolHandler>();
77 else if ( mContext.symbol()->symbolType() == QLatin1String( "color-ramp" ) )
78 mHandler = std::make_unique<QgsColorRampPointCloud3DSymbolHandler>();
79 else if ( mContext.symbol()->symbolType() == QLatin1String( "rgb" ) )
80 mHandler = std::make_unique<QgsRGBPointCloud3DSymbolHandler>();
81 else if ( mContext.symbol()->symbolType() == QLatin1String( "classification" ) )
82 {
83 mHandler = std::make_unique<QgsClassificationPointCloud3DSymbolHandler>();
84 const QgsClassificationPointCloud3DSymbol *classificationSymbol = dynamic_cast<const QgsClassificationPointCloud3DSymbol *>( mContext.symbol() );
85 mContext.setFilteredOutCategories( classificationSymbol->getFilteredOutCategories() );
86 }
87 // NOLINTEND(bugprone-branch-clone)
88
89 //
90 // this will be run in a background thread
91 //
92 mFutureWatcher = new QFutureWatcher<void>( this );
93 connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
94
95 const QgsBox3D box3D = node->box3D();
96 const QFuture<void> future = QtConcurrent::run( [pc = std::move( pc ), pcNode, box3D, this] {
97 const QgsEventTracing::ScopedEvent e( QStringLiteral( "3D" ), QStringLiteral( "PC chunk load" ) );
98
99 if ( mContext.isCanceled() )
100 {
101 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
102 return;
103 }
104
105 QgsPointCloudIndex pc2 = pc; // Copy to discard const
106 mHandler->processNode( pc2, pcNode, mContext );
107
108 if ( mContext.isCanceled() )
109 {
110 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
111 return;
112 }
113
114 if ( mContext.symbol()->renderAsTriangles() )
115 mHandler->triangulate( pc2, pcNode, mContext, box3D );
116 } );
117
118 // emit finished() as soon as the handler is populated with features
119 mFutureWatcher->setFuture( future );
120}
121
122QgsPointCloudLayerChunkLoader::~QgsPointCloudLayerChunkLoader()
123{
124 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
125 {
126 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
127 mContext.cancelRendering();
128 mFutureWatcher->waitForFinished();
129 }
130}
131
132void QgsPointCloudLayerChunkLoader::cancel()
133{
134 mContext.cancelRendering();
135}
136
137Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
138{
139 QgsPointCloudIndex pc = mFactory->mPointCloudIndex;
140 const QgsChunkNodeId nodeId = mNode->tileId();
141 const QgsPointCloudNodeId pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
142 Q_ASSERT( pc.hasNode( pcNode ) );
143
144 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
145 mHandler->finalize( entity, mContext );
146 return entity;
147}
148
150
151
152QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory( const Qgs3DRenderContext &context, const QgsCoordinateTransform &coordinateTransform, QgsPointCloudIndex pc, QgsPointCloud3DSymbol *symbol, double zValueScale, double zValueOffset, int pointBudget )
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( QStringLiteral( "Transformation of extent failed." ) );
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
193
194static QgsBox3D nodeBoundsToBox3D( QgsBox3D nodeBounds, const QgsCoordinateTransform &coordinateTransform, double zValueOffset, double zValueScale )
195{
196 QgsVector3D extentMin3D( nodeBounds.xMinimum(), nodeBounds.yMinimum(), nodeBounds.zMinimum() * zValueScale + zValueOffset );
197 QgsVector3D extentMax3D( nodeBounds.xMaximum(), nodeBounds.yMaximum(), nodeBounds.zMaximum() * zValueScale + zValueOffset );
198 QgsCoordinateTransform extentTransform = coordinateTransform;
199 extentTransform.setBallparkTransformsAreAppropriate( true );
200 try
201 {
202 extentMin3D = extentTransform.transform( extentMin3D );
203 extentMax3D = extentTransform.transform( extentMax3D );
204 }
205 catch ( QgsCsException & )
206 {
207 QgsDebugError( QStringLiteral( "Error transforming node bounds coordinate" ) );
208 }
209 return QgsBox3D( extentMin3D.x(), extentMin3D.y(), extentMin3D.z(), extentMax3D.x(), extentMax3D.y(), extentMax3D.z() );
210}
211
212
213QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode() const
214{
215 const QgsPointCloudNode pcNode = mPointCloudIndex.getNode( mPointCloudIndex.root() );
216 const QgsBox3D rootNodeBounds = pcNode.bounds();
217 QgsBox3D rootNodeBox3D = nodeBoundsToBox3D( rootNodeBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
218
219 const float error = pcNode.error();
220 QgsChunkNode *node = new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, error );
221 node->setRefinementProcess( mSymbol->renderAsTriangles() ? Qgis::TileRefinementProcess::Replacement : Qgis::TileRefinementProcess::Additive );
222 return node;
223}
224
225QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node ) const
226{
227 QVector<QgsChunkNode *> children;
228 const QgsChunkNodeId nodeId = node->tileId();
229 const float childError = node->error() / 2;
230
231 for ( int i = 0; i < 8; ++i )
232 {
233 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
234 const QgsChunkNodeId childId( nodeId.d + 1, nodeId.x * 2 + dx, nodeId.y * 2 + dy, nodeId.z * 2 + dz );
235 const QgsPointCloudNodeId childPcId( childId.d, childId.x, childId.y, childId.z );
236 if ( !mPointCloudIndex.hasNode( childPcId ) )
237 continue;
238 const QgsPointCloudNode childNode = mPointCloudIndex.getNode( childPcId );
239 const QgsBox3D childBounds = childNode.bounds();
240 if ( !mExtent.isEmpty() && !childBounds.toRectangle().intersects( mExtent ) )
241 continue;
242
243 QgsBox3D childBox3D = nodeBoundsToBox3D( childBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
244
245 QgsChunkNode *child = new QgsChunkNode( childId, childBox3D, childError, node );
246 child->setRefinementProcess( mSymbol->renderAsTriangles() ? Qgis::TileRefinementProcess::Replacement : Qgis::TileRefinementProcess::Additive );
247 children << child;
248 }
249 return children;
250}
251
253
254
255static QgsChunkNode *findChunkNodeFromNodeId( QgsChunkNode *rootNode, QgsPointCloudNodeId nodeId )
256{
257 // find path from the node to the root
258 QVector<QgsPointCloudNodeId> parentIds;
259 while ( nodeId.d() > 0 )
260 {
261 parentIds << nodeId;
262 nodeId = nodeId.parentNode();
263 }
264
265 // now descend from the root to the node in the QgsChunkNode hierarchy
266 QgsChunkNode *chunk = rootNode;
267 while ( !parentIds.empty() )
268 {
269 QgsPointCloudNodeId p = parentIds.takeLast();
270 QgsChunkNodeId childNodeId( p.d(), p.x(), p.y(), p.z() );
271
272 if ( !chunk->hasChildrenPopulated() )
273 return nullptr;
274
275 QgsChunkNode *chunkChild = nullptr;
276 QgsChunkNode *const *children = chunk->children();
277 for ( int i = 0; i < chunk->childCount(); ++i )
278 {
279 if ( children[i]->tileId() == childNodeId )
280 {
281 chunkChild = children[i];
282 break;
283 }
284 }
285 Q_ASSERT( chunkChild );
286 chunk = chunkChild;
287 }
288 return chunk;
289}
290
291
292QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity( Qgs3DMapSettings *map, QgsPointCloudLayer *pcl, QgsPointCloudIndex index, const QgsCoordinateTransform &coordinateTransform, QgsPointCloud3DSymbol *symbol, float maximumScreenSpaceError, bool showBoundingBoxes, double zValueScale, double zValueOffset, int pointBudget )
293 : QgsChunkedEntity( map, maximumScreenSpaceError, new QgsPointCloudLayerChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), coordinateTransform, std::move( index ), symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
294 , mLayer( pcl )
295{
296 setShowBoundingBoxes( showBoundingBoxes );
297
298 if ( pcl->supportsEditing() )
299 {
300 // when editing starts or stops, we need to update our index to use the editing index (or not)
301 connect( pcl, &QgsPointCloudLayer::editingStarted, this, &QgsPointCloudLayerChunkedEntity::updateIndex );
302 connect( pcl, &QgsPointCloudLayer::editingStopped, this, &QgsPointCloudLayerChunkedEntity::updateIndex );
303
304 mChunkUpdaterFactory = std::make_unique<QgsChunkUpdaterFactory>( mChunkLoaderFactory );
305
306 connect( pcl, &QgsPointCloudLayer::chunkAttributeValuesChanged, this, [this]( const QgsPointCloudNodeId &n ) {
307 QgsChunkNode *node = findChunkNodeFromNodeId( mRootNode, n );
308 if ( node )
309 {
310 updateNodes( QList<QgsChunkNode *>() << node, mChunkUpdaterFactory.get() );
311 }
312 } );
313 }
314}
315
316QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
317{
318 // cancel / wait for jobs
319 cancelActiveJobs();
320}
321
322void QgsPointCloudLayerChunkedEntity::updateIndex()
323{
324 static_cast<QgsPointCloudLayerChunkLoaderFactory *>( mChunkLoaderFactory )->mPointCloudIndex = mLayer->index();
325}
326
327QList<QgsRayCastHit> QgsPointCloudLayerChunkedEntity::rayIntersection( const QgsRay3D &ray, const QgsRayCastContext &context ) const
328{
329 QList<QgsRayCastHit> result;
330 QgsPointCloudLayerChunkLoaderFactory *factory = static_cast<QgsPointCloudLayerChunkLoaderFactory *>( mChunkLoaderFactory );
331
332 const QgsPointCloud3DSymbol *symbol = factory->mSymbol.get();
333 // Symbol can be null in case of no rendering enabled
334 if ( !symbol )
335 return result;
336
337 // transform ray
338 const QgsVector3D rayOriginMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() );
339 const QgsVector3D pointMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() + ray.origin().length() * ray.direction().normalized() );
340 QgsVector3D rayDirectionMapCoords = pointMapCoords - rayOriginMapCoords;
341 rayDirectionMapCoords.normalize();
342
343 // We're using the angle as a tolerance, effectively meaning we're fetching points intersecting a cone.
344 // This may be revisited to use a cylinder instead, if the balance between near/far points does not scale
345 // well with different point sizes, screen sizes and fov values.
346 const double limitAngle = context.angleThreshold() * M_PI / 180.;
347
348 // adjust ray to elevation properties
349 const QgsVector3D adjustedRayOrigin = QgsVector3D( rayOriginMapCoords.x(), rayOriginMapCoords.y(), ( rayOriginMapCoords.z() - factory->mZValueOffset ) / factory->mZValueScale );
350 QgsVector3D adjustedRayDirection = QgsVector3D( rayDirectionMapCoords.x(), rayDirectionMapCoords.y(), rayDirectionMapCoords.z() / factory->mZValueScale );
351 adjustedRayDirection.normalize();
352
353 QgsPointCloudIndex index = factory->mPointCloudIndex;
354
355 const QgsPointCloudAttributeCollection attributeCollection = index.attributes();
356 QgsPointCloudRequest request;
357 request.setAttributes( attributeCollection );
358
359 double minDist = -1.;
360 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
361 for ( QgsChunkNode *node : activeNodes )
362 {
363 const QgsChunkNodeId id = node->tileId();
364 const QgsPointCloudNodeId n( id.d, id.x, id.y, id.z );
365
366 if ( !index.hasNode( n ) )
367 continue;
368
369 const QgsAABB nodeBbox = Qgs3DUtils::mapToWorldExtent( node->box3D(), mMapSettings->origin() );
370 if ( !QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
371 continue;
372
373 std::unique_ptr<QgsPointCloudBlock> block( index.nodeData( n, request ) );
374 if ( !block )
375 continue;
376
377 const QgsVector3D blockScale = block->scale();
378 const QgsVector3D blockOffset = block->offset();
379
380 const char *ptr = block->data();
381 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
382 const std::size_t recordSize = blockAttributes.pointRecordSize();
383 int xOffset = 0, yOffset = 0, zOffset = 0;
384 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( QStringLiteral( "X" ), xOffset )->type();
385 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( QStringLiteral( "Y" ), yOffset )->type();
386 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( QStringLiteral( "Z" ), zOffset )->type();
387 for ( int i = 0; i < block->pointCount(); ++i )
388 {
389 double x, y, z;
390 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, blockScale, blockOffset, x, y, z );
391 const QgsVector3D point( x, y, z );
392
393 // check whether point is in front of the ray
394 // similar to QgsRay3D::isInFront(), but using doubles
395 QgsVector3D vectorToPoint = point - adjustedRayOrigin;
396 vectorToPoint.normalize();
397 if ( QgsVector3D::dotProduct( vectorToPoint, adjustedRayDirection ) < 0.0 )
398 continue;
399
400 // calculate the angle between the point and the projected point
401 // similar to QgsRay3D::angleToPoint(), but using doubles
402 const QgsVector3D projPoint = adjustedRayOrigin + adjustedRayDirection * QgsVector3D::dotProduct( point - adjustedRayOrigin, adjustedRayDirection );
403
404 const double d1 = projPoint.distance( adjustedRayOrigin );
405 const double d2 = projPoint.distance( point );
406 const double angle = std::atan2( d2, d1 );
407
408 if ( angle > limitAngle )
409 continue;
410
411 const double dist = rayOriginMapCoords.distance( point );
412
413 if ( minDist < 0 || dist < minDist )
414 {
415 minDist = dist;
416 }
417 else if ( context.singleResult() )
418 {
419 continue;
420 }
421
422 // Note : applying elevation properties is done in fromPointCloudIdentificationToIdentifyResults
423 QVariantMap pointAttr = QgsPointCloudAttribute::getAttributeMap( ptr, i * recordSize, blockAttributes );
424 pointAttr[QStringLiteral( "X" )] = x;
425 pointAttr[QStringLiteral( "Y" )] = y;
426 pointAttr[QStringLiteral( "Z" )] = z;
427
428 QgsRayCastHit hit;
429 hit.setDistance( dist );
430 hit.setMapCoordinates( point );
431 hit.setProperties( pointAttr );
432 if ( context.singleResult() )
433 result.clear();
434 result.append( hit );
435 }
436 }
437 return result;
438}
@ Geocentric
Geocentric CRS.
Definition qgis.h:2330
@ Additive
When tile is refined its content should be used alongside its children simultaneously.
Definition qgis.h:5658
@ Replacement
When tile is refined then its children should be used in place of itself.
Definition qgis.h:5657
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:35
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:42
double yMaximum() const
Returns the maximum y value.
Definition qgsbox3d.h:230
double xMinimum() const
Returns the minimum x value.
Definition qgsbox3d.h:195
double zMaximum() const
Returns the maximum z value.
Definition qgsbox3d.h:258
double xMaximum() const
Returns the maximum x value.
Definition qgsbox3d.h:202
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:378
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:251
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:223
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.
void chunkAttributeValuesChanged(const QgsPointCloudNodeId &n)
Emitted when a node gets some attribute values of some points changed.
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.
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:30
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:49
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:51
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:47
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:61
#define QgsDebugError(str)
Definition qgslogger.h:57