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