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