QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
•All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgspointcloudlayerrenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloudlayerrenderer.cpp
3 --------------------
4 begin : October 2020
5 copyright : (C) 2020 by Peter Petrik
6 email : zilolv at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include <QElapsedTimer>
19#include <QPointer>
20
22#include "qgspointcloudlayer.h"
23#include "qgsrendercontext.h"
24#include "qgspointcloudindex.h"
25#include "qgscolorramp.h"
26#include "qgselevationmap.h"
31#include "qgslogger.h"
33#include "qgsmessagelog.h"
34#include "qgsmapclippingutils.h"
36
38 : QgsMapLayerRenderer( layer->id(), &context )
39 , mLayer( layer )
40 , mLayerAttributes( layer->attributes() )
41 , mSubIndexes( layer && layer->dataProvider() ? layer->dataProvider()->subIndexes() : QVector<QgsPointCloudSubIndex>() )
42 , mFeedback( new QgsFeedback )
43{
44 // TODO: we must not keep pointer to mLayer (it's dangerous) - we must copy anything we need for rendering
45 // or use some locking to prevent read/write from multiple threads
46 if ( !mLayer || !mLayer->dataProvider() || !mLayer->renderer() )
47 return;
48
49 mRenderer.reset( mLayer->renderer()->clone() );
50 if ( !mSubIndexes.isEmpty() )
51 mSubIndexExtentRenderer.reset( new QgsPointCloudExtentRenderer() );
52
53 if ( mLayer->dataProvider()->index() )
54 {
55 mScale = mLayer->dataProvider()->index()->scale();
56 mOffset = mLayer->dataProvider()->index()->offset();
57 }
58
59 if ( const QgsPointCloudLayerElevationProperties *elevationProps = qobject_cast< const QgsPointCloudLayerElevationProperties * >( mLayer->elevationProperties() ) )
60 {
61 mZOffset = elevationProps->zOffset();
62 mZScale = elevationProps->zScale();
63 }
64
65 mCloudExtent = mLayer->dataProvider()->polygonBounds();
66
68
69 mReadyToCompose = false;
70}
71
73{
74 QgsPointCloudRenderContext context( *renderContext(), mScale, mOffset, mZScale, mZOffset, mFeedback.get() );
75
76 // Set up the render configuration options
77 QPainter *painter = context.renderContext().painter();
78
79 QgsScopedQPainterState painterState( painter );
80 context.renderContext().setPainterFlagsUsingContext( painter );
81
82 if ( !mClippingRegions.empty() )
83 {
84 bool needsPainterClipPath = false;
85 const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), Qgis::LayerType::VectorTile, needsPainterClipPath );
86 if ( needsPainterClipPath )
87 renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
88 }
89
90 if ( mRenderer->type() == QLatin1String( "extent" ) )
91 {
92 // special case for extent only renderer!
93 mRenderer->startRender( context );
94 static_cast< QgsPointCloudExtentRenderer * >( mRenderer.get() )->renderExtent( mCloudExtent, context );
95 mRenderer->stopRender( context );
96 mReadyToCompose = true;
97 return true;
98 }
99
100 // TODO cache!?
101 QgsPointCloudIndex *pc = mLayer->dataProvider()->index();
102 if ( mSubIndexes.isEmpty() &&
103 ( !pc || !pc->isValid() ) )
104 {
105 mReadyToCompose = true;
106 return false;
107 }
108
109 // if the previous layer render was relatively quick (e.g. less than 3 seconds), the we show any previously
110 // cached version of the layer during rendering instead of the usual progressive updates
111 if ( mRenderTimeHint > 0 && mRenderTimeHint <= MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
112 {
113 mBlockRenderUpdates = true;
114 mElapsedTimer.start();
115 }
116
117 mRenderer->startRender( context );
118
119 mAttributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
120 mAttributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
121
122 if ( !context.renderContext().zRange().isInfinite() ||
123 mRenderer->drawOrder2d() == Qgis::PointCloudDrawOrder::BottomToTop ||
124 mRenderer->drawOrder2d() == Qgis::PointCloudDrawOrder::TopToBottom ||
126 mAttributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
127
128 // collect attributes required by renderer
129 QSet< QString > rendererAttributes = mRenderer->usedAttributes( context );
130
131
132 for ( const QString &attribute : std::as_const( rendererAttributes ) )
133 {
134 if ( mAttributes.indexOf( attribute ) >= 0 )
135 continue; // don't re-add attributes we are already going to fetch
136
137 const int layerIndex = mLayerAttributes.indexOf( attribute );
138 if ( layerIndex < 0 )
139 {
140 QgsMessageLog::logMessage( QObject::tr( "Required attribute %1 not found in layer" ).arg( attribute ), QObject::tr( "Point Cloud" ) );
141 continue;
142 }
143
144 mAttributes.push_back( mLayerAttributes.at( layerIndex ) );
145 }
146
147 QgsRectangle renderExtent;
148 try
149 {
150 renderExtent = renderContext()->coordinateTransform().transformBoundingBox( renderContext()->mapExtent(), Qgis::TransformDirection::Reverse );
151 }
152 catch ( QgsCsException & )
153 {
154 QgsDebugError( QStringLiteral( "Transformation of extent failed!" ) );
155 }
156
157 bool canceled = false;
158 if ( mSubIndexes.isEmpty() )
159 {
160 canceled = !renderIndex( pc );
161 }
162 else
163 {
164 mSubIndexExtentRenderer->startRender( context );
165 for ( const auto &si : mSubIndexes )
166 {
167 if ( canceled )
168 break;
169
170 QgsPointCloudIndex *pc = si.index();
171
172 if ( !renderExtent.intersects( si.extent() ) )
173 continue;
174
175 if ( !pc || !pc->isValid() || renderExtent.width() > si.extent().width() )
176 {
177 // when dealing with virtual point clouds, we want to render the individual extents when zoomed out
178 // and only use the selected renderer when zoomed in
179 mSubIndexExtentRenderer->renderExtent( si.polygonBounds(), context );
180 }
181 else
182 {
183 canceled = !renderIndex( pc );
184 }
185 }
186 mSubIndexExtentRenderer->stopRender( context );
187 }
188
189 mRenderer->stopRender( context );
190 mReadyToCompose = true;
191 return !canceled;
192}
193
194bool QgsPointCloudLayerRenderer::renderIndex( QgsPointCloudIndex *pc )
195{
197 pc->scale(),
198 pc->offset(),
199 mZScale,
200 mZOffset,
201 mFeedback.get() );
202
203
204#ifdef QGISDEBUG
205 QElapsedTimer t;
206 t.start();
207#endif
208
209 const IndexedPointCloudNode root = pc->root();
210
211 const double maximumError = context.renderContext().convertToPainterUnits( mRenderer->maximumScreenError(), mRenderer->maximumScreenErrorUnit() );// in pixels
212
213 const QgsRectangle rootNodeExtentLayerCoords = pc->nodeMapExtent( root );
214 QgsRectangle rootNodeExtentMapCoords;
215 if ( !context.renderContext().coordinateTransform().isShortCircuited() )
216 {
217 try
218 {
219 QgsCoordinateTransform extentTransform = context.renderContext().coordinateTransform();
220 extentTransform.setBallparkTransformsAreAppropriate( true );
221 rootNodeExtentMapCoords = extentTransform.transformBoundingBox( rootNodeExtentLayerCoords );
222 }
223 catch ( QgsCsException & )
224 {
225 QgsDebugError( QStringLiteral( "Could not transform node extent to map CRS" ) );
226 rootNodeExtentMapCoords = rootNodeExtentLayerCoords;
227 }
228 }
229 else
230 {
231 rootNodeExtentMapCoords = rootNodeExtentLayerCoords;
232 }
233
234 const double rootErrorInMapCoordinates = rootNodeExtentMapCoords.width() / pc->span(); // in map coords
235
236 double mapUnitsPerPixel = context.renderContext().mapToPixel().mapUnitsPerPixel();
237 if ( ( rootErrorInMapCoordinates < 0.0 ) || ( mapUnitsPerPixel < 0.0 ) || ( maximumError < 0.0 ) )
238 {
239 QgsDebugError( QStringLiteral( "invalid screen error" ) );
240 return false;
241 }
242 double rootErrorPixels = rootErrorInMapCoordinates / mapUnitsPerPixel; // in pixels
243 const QVector<IndexedPointCloudNode> nodes = traverseTree( pc, context.renderContext(), pc->root(), maximumError, rootErrorPixels );
244
245 QgsPointCloudRequest request;
246 request.setAttributes( mAttributes );
247
248 // drawing
249 int nodesDrawn = 0;
250 bool canceled = false;
251
252 switch ( mRenderer->drawOrder2d() )
253 {
256 {
257 nodesDrawn += renderNodesSorted( nodes, pc, context, request, canceled, mRenderer->drawOrder2d() );
258 break;
259 }
261 {
262 switch ( pc->accessType() )
263 {
264 case QgsPointCloudIndex::AccessType::Local:
265 {
266 nodesDrawn += renderNodesSync( nodes, pc, context, request, canceled );
267 break;
268 }
269 case QgsPointCloudIndex::AccessType::Remote:
270 {
271 nodesDrawn += renderNodesAsync( nodes, pc, context, request, canceled );
272 break;
273 }
274 }
275 }
276 }
277
278#ifdef QGISDEBUG
279 QgsDebugMsgLevel( QStringLiteral( "totals: %1 nodes | %2 points | %3ms" ).arg( nodesDrawn )
280 .arg( context.pointsRendered() )
281 .arg( t.elapsed() ), 2 );
282#else
283 ( void )nodesDrawn;
284#endif
285
286 return !canceled;
287}
288
289int QgsPointCloudLayerRenderer::renderNodesSync( const QVector<IndexedPointCloudNode> &nodes, QgsPointCloudIndex *pc, QgsPointCloudRenderContext &context, QgsPointCloudRequest &request, bool &canceled )
290{
291 int nodesDrawn = 0;
292 for ( const IndexedPointCloudNode &n : nodes )
293 {
294 if ( context.renderContext().renderingStopped() )
295 {
296 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
297 canceled = true;
298 break;
299 }
300 std::unique_ptr<QgsPointCloudBlock> block( pc->nodeData( n, request ) );
301
302 if ( !block )
303 continue;
304
305 QgsVector3D contextScale = context.scale();
306 QgsVector3D contextOffset = context.offset();
307
308 context.setScale( block->scale() );
309 context.setOffset( block->offset() );
310
311 context.setAttributes( block->attributes() );
312
313 mRenderer->renderBlock( block.get(), context );
314
315 context.setScale( contextScale );
316 context.setOffset( contextOffset );
317
318 ++nodesDrawn;
319
320 // as soon as first block is rendered, we can start showing layer updates.
321 // but if we are blocking render updates (so that a previously cached image is being shown), we wait
322 // at most e.g. 3 seconds before we start forcing progressive updates.
323 if ( !mBlockRenderUpdates || mElapsedTimer.elapsed() > MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
324 {
325 mReadyToCompose = true;
326 }
327 }
328 return nodesDrawn;
329}
330
331int QgsPointCloudLayerRenderer::renderNodesAsync( const QVector<IndexedPointCloudNode> &nodes, QgsPointCloudIndex *pc, QgsPointCloudRenderContext &context, QgsPointCloudRequest &request, bool &canceled )
332{
333 int nodesDrawn = 0;
334
335 if ( context.feedback() && context.feedback()->isCanceled() )
336 return 0;
337
338 // Async loading of nodes
339 QVector<QgsPointCloudBlockRequest *> blockRequests;
340 QEventLoop loop;
341 if ( context.feedback() )
342 QObject::connect( context.feedback(), &QgsFeedback::canceled, &loop, &QEventLoop::quit );
343
344 for ( int i = 0; i < nodes.size(); ++i )
345 {
346 const IndexedPointCloudNode &n = nodes[i];
347 const QString nStr = n.toString();
348 QgsPointCloudBlockRequest *blockRequest = pc->asyncNodeData( n, request );
349 blockRequests.append( blockRequest );
350 QObject::connect( blockRequest, &QgsPointCloudBlockRequest::finished, &loop,
351 [ this, &canceled, &nodesDrawn, &loop, &blockRequests, &context, nStr, blockRequest ]()
352 {
353 blockRequests.removeOne( blockRequest );
354
355 // If all blocks are loaded, exit the event loop
356 if ( blockRequests.isEmpty() )
357 loop.exit();
358
359 std::unique_ptr<QgsPointCloudBlock> block( blockRequest->block() );
360
361 blockRequest->deleteLater();
362
363 if ( context.feedback() && context.feedback()->isCanceled() )
364 {
365 canceled = true;
366 return;
367 }
368
369 if ( !block )
370 {
371 QgsDebugError( QStringLiteral( "Unable to load node %1, error: %2" ).arg( nStr, blockRequest->errorStr() ) );
372 return;
373 }
374
375 QgsVector3D contextScale = context.scale();
376 QgsVector3D contextOffset = context.offset();
377
378 context.setScale( block->scale() );
379 context.setOffset( block->offset() );
380 context.setAttributes( block->attributes() );
381
382 mRenderer->renderBlock( block.get(), context );
383
384 context.setScale( contextScale );
385 context.setOffset( contextOffset );
386
387 ++nodesDrawn;
388
389 // as soon as first block is rendered, we can start showing layer updates.
390 // but if we are blocking render updates (so that a previously cached image is being shown), we wait
391 // at most e.g. 3 seconds before we start forcing progressive updates.
392 if ( !mBlockRenderUpdates || mElapsedTimer.elapsed() > MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
393 {
394 mReadyToCompose = true;
395 }
396
397 } );
398 }
399
400 // Wait for all point cloud nodes to finish loading
401 loop.exec();
402
403 // Rendering may have got canceled and the event loop exited before finished()
404 // was called for all blocks, so let's clean up anything that is left
405 for ( QgsPointCloudBlockRequest *blockRequest : std::as_const( blockRequests ) )
406 {
407 delete blockRequest->block();
408 blockRequest->deleteLater();
409 }
410
411 return nodesDrawn;
412}
413
414int QgsPointCloudLayerRenderer::renderNodesSorted( const QVector<IndexedPointCloudNode> &nodes, QgsPointCloudIndex *pc, QgsPointCloudRenderContext &context, QgsPointCloudRequest &request, bool &canceled, Qgis::PointCloudDrawOrder order )
415{
416 int blockCount = 0;
417 int pointCount = 0;
418
419 QgsVector3D blockScale;
420 QgsVector3D blockOffset;
421 QgsPointCloudAttributeCollection blockAttributes;
422 int recordSize = 0;
423
424 // We'll collect byte array data from all blocks
425 QByteArray allByteArrays;
426 // And pairs of byte array start positions paired with their Z values for sorting
427 QVector<QPair<int, double>> allPairs;
428
429 for ( const IndexedPointCloudNode &n : nodes )
430 {
431 if ( context.renderContext().renderingStopped() )
432 {
433 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
434 canceled = true;
435 break;
436 }
437 std::unique_ptr<QgsPointCloudBlock> block( pc->nodeData( n, request ) );
438
439 if ( !block )
440 continue;
441
442 // Individual nodes may have different offset values than the root node
443 // we'll calculate the differences and translate x,y,z values to use the root node's offset
444 QgsVector3D offsetDifference = QgsVector3D( 0, 0, 0 );
445 if ( blockCount == 0 )
446 {
447 blockScale = block->scale();
448 blockOffset = block->offset();
449 blockAttributes = block->attributes();
450 }
451 else
452 {
453 offsetDifference = blockOffset - block->offset();
454 }
455
456 const char *ptr = block->data();
457
458 context.setScale( block->scale() );
459 context.setOffset( block->offset() );
460 context.setAttributes( block->attributes() );
461
462 recordSize = context.pointRecordSize();
463
464 for ( int i = 0; i < block->pointCount(); ++i )
465 {
466 allByteArrays.append( ptr + i * recordSize, recordSize );
467
468 // Calculate the translated values only for axes that have a different offset
469 if ( offsetDifference.x() != 0 )
470 {
471 qint32 ix = *reinterpret_cast< const qint32 * >( ptr + i * recordSize + context.xOffset() );
472 ix -= std::lround( offsetDifference.x() / context.scale().x() );
473 const char *xPtr = reinterpret_cast< const char * >( &ix );
474 allByteArrays.replace( pointCount * recordSize + context.xOffset(), 4, QByteArray( xPtr, 4 ) );
475 }
476 if ( offsetDifference.y() != 0 )
477 {
478 qint32 iy = *reinterpret_cast< const qint32 * >( ptr + i * recordSize + context.yOffset() );
479 iy -= std::lround( offsetDifference.y() / context.scale().y() );
480 const char *yPtr = reinterpret_cast< const char * >( &iy );
481 allByteArrays.replace( pointCount * recordSize + context.yOffset(), 4, QByteArray( yPtr, 4 ) );
482 }
483 // We need the Z value regardless of the node's offset
484 qint32 iz = *reinterpret_cast< const qint32 * >( ptr + i * recordSize + context.zOffset() );
485 if ( offsetDifference.z() != 0 )
486 {
487 iz -= std::lround( offsetDifference.z() / context.scale().z() );
488 const char *zPtr = reinterpret_cast< const char * >( &iz );
489 allByteArrays.replace( pointCount * recordSize + context.zOffset(), 4, QByteArray( zPtr, 4 ) );
490 }
491 allPairs.append( qMakePair( pointCount, double( iz ) + block->offset().z() ) );
492
493 ++pointCount;
494 }
495 ++blockCount;
496 }
497
498 if ( pointCount == 0 )
499 return 0;
500
501 switch ( order )
502 {
504 std::sort( allPairs.begin(), allPairs.end(), []( QPair<int, double> a, QPair<int, double> b ) { return a.second < b.second; } );
505 break;
507 std::sort( allPairs.begin(), allPairs.end(), []( QPair<int, double> a, QPair<int, double> b ) { return a.second > b.second; } );
508 break;
510 break;
511 }
512
513 // Now we can reconstruct a byte array sorted by Z value
514 QByteArray sortedByteArray;
515 sortedByteArray.reserve( allPairs.size() );
516 for ( QPair<int, double> pair : allPairs )
517 sortedByteArray.append( allByteArrays.mid( pair.first * recordSize, recordSize ) );
518
519 std::unique_ptr<QgsPointCloudBlock> bigBlock { new QgsPointCloudBlock( pointCount,
520 blockAttributes,
521 sortedByteArray,
522 blockScale,
523 blockOffset ) };
524
525 QgsVector3D contextScale = context.scale();
526 QgsVector3D contextOffset = context.offset();
527
528 context.setScale( bigBlock->scale() );
529 context.setOffset( bigBlock->offset() );
530 context.setAttributes( bigBlock->attributes() );
531
532 mRenderer->renderBlock( bigBlock.get(), context );
533
534 context.setScale( contextScale );
535 context.setOffset( contextOffset );
536
537 return blockCount;
538}
539
541{
542 // unless we are using the extent only renderer, point cloud layers should always be rasterized -- we don't want to export points as vectors
543 // to formats like PDF!
544 return mRenderer ? mRenderer->type() != QLatin1String( "extent" ) : false;
545}
546
548{
549 mRenderTimeHint = time;
550}
551
552QVector<IndexedPointCloudNode> QgsPointCloudLayerRenderer::traverseTree( const QgsPointCloudIndex *pc,
553 const QgsRenderContext &context,
555 double maxErrorPixels,
556 double nodeErrorPixels )
557{
558 QVector<IndexedPointCloudNode> nodes;
559
560 if ( context.renderingStopped() )
561 {
562 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
563 return nodes;
564 }
565
566 if ( !context.extent().intersects( pc->nodeMapExtent( n ) ) )
567 return nodes;
568
569 const QgsDoubleRange nodeZRange = pc->nodeZRange( n );
570 const QgsDoubleRange adjustedNodeZRange = QgsDoubleRange( nodeZRange.lower() + mZOffset, nodeZRange.upper() + mZOffset );
571 if ( !context.zRange().isInfinite() && !context.zRange().overlaps( adjustedNodeZRange ) )
572 return nodes;
573
574 if ( pc->nodePointCount( n ) > 0 )
575 nodes.append( n );
576
577 double childrenErrorPixels = nodeErrorPixels / 2.0;
578 if ( childrenErrorPixels < maxErrorPixels )
579 return nodes;
580
581 const QList<IndexedPointCloudNode> children = pc->nodeChildren( n );
582 for ( const IndexedPointCloudNode &nn : children )
583 {
584 nodes += traverseTree( pc, context, nn, maxErrorPixels, childrenErrorPixels );
585 }
586
587 return nodes;
588}
589
Represents a indexed point cloud node in octree.
QString toString() const
Encode node to string.
PointCloudDrawOrder
Pointcloud rendering order for 2d views.
Definition: qgis.h:2702
@ BottomToTop
Draw points with larger Z values last.
@ Default
Draw points in the order they are stored.
@ TopToBottom
Draw points with larger Z values first.
Class for doing transforms between two map coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:67
QgsRange which stores a range of double values.
Definition: qgsrange.h:203
bool isInfinite() const
Returns true if the range consists of all possible values.
Definition: qgsrange.h:247
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:45
void canceled()
Internal routines can connect to this signal if they use event loop.
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
static QPainterPath calculatePainterClipRegion(const QList< QgsMapClippingRegion > &regions, const QgsRenderContext &context, Qgis::LayerType layerType, bool &shouldClip)
Returns a QPainterPath representing the intersection of clipping regions from context which should be...
static QList< QgsMapClippingRegion > collectClippingRegionsForLayer(const QgsRenderContext &context, const QgsMapLayer *layer)
Collects the list of map clipping regions from a context which apply to a map layer.
Base class for utility classes that encapsulate information necessary for rendering of map layers.
bool mReadyToCompose
The flag must be set to false in renderer's constructor if wants to use the smarter map redraws funct...
static constexpr int MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE
Maximum time (in ms) to allow display of a previously cached preview image while rendering layers,...
QgsRenderContext * renderContext()
Returns the render context associated with the renderer.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Collection of point cloud attributes.
void push_back(const QgsPointCloudAttribute &attribute)
Adds extra attribute.
const QgsPointCloudAttribute & at(int index) const
Returns the attribute at the specified index.
QVector< QgsPointCloudAttribute > attributes() const
Returns all attributes.
int indexOf(const QString &name) const
Returns the index of the attribute with the specified name.
Attribute for point cloud data pair of name and size in bytes.
Base class for handling loading QgsPointCloudBlock asynchronously.
QString errorStr()
Returns the error message string of the request.
QgsPointCloudBlock * block()
Returns the requested block.
void finished()
Emitted when the request processing has finished.
Base class for storing raw data from point cloud nodes.
virtual QgsPointCloudIndex * index() const
Returns the point cloud index associated with the provider.
virtual QgsGeometry polygonBounds() const
Returns the polygon bounds of the layer.
A renderer for 2d visualisation of point clouds which shows the dataset's extents using a fill symbol...
Represents a indexed point clouds data in octree.
int span() const
Returns the number of points in one direction in a single node.
virtual qint64 nodePointCount(const IndexedPointCloudNode &n) const
Returns the number of points of a given node n.
QgsRectangle nodeMapExtent(const IndexedPointCloudNode &node) const
Returns the extent of a node in map coordinates.
virtual QgsPointCloudBlockRequest * asyncNodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns a handle responsible for loading a node data block.
virtual QList< IndexedPointCloudNode > nodeChildren(const IndexedPointCloudNode &n) const
Returns all children of node.
QgsVector3D offset() const
Returns offset.
QgsVector3D scale() const
Returns scale.
virtual AccessType accessType() const =0
Returns the access type of the data If the access type is Remote, data will be fetched from an HTTP s...
virtual bool isValid() const =0
Returns whether index is loaded and valid.
virtual QgsPointCloudBlock * nodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns node data block.
IndexedPointCloudNode root()
Returns root node of the index.
QgsDoubleRange nodeZRange(const IndexedPointCloudNode &node) const
Returns the z range of a node.
Point cloud layer specific subclass of QgsMapLayerElevationProperties.
bool forceRasterRender() const override
Returns true if the renderer must be rendered to a raster paint device (e.g.
QgsPointCloudLayerRenderer(QgsPointCloudLayer *layer, QgsRenderContext &context)
Ctor.
void setLayerRenderingTimeHint(int time) override
Sets approximate render time (in ms) for the layer to render.
bool render() override
Do the rendering (based on data stored in the class).
Represents a map layer supporting display of point clouds.
QgsMapLayerElevationProperties * elevationProperties() override
Returns the layer's elevation properties.
QgsPointCloudRenderer * renderer()
Returns the 2D renderer for the point cloud.
QgsPointCloudDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
Encapsulates the render context for a 2D point cloud rendering operation.
int yOffset() const
Returns the offset for the y value in a point record.
QgsVector3D offset() const
Returns the offset of the layer's int32 coordinates compared to CRS coords.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
void setOffset(const QgsVector3D &offset)
Sets the offset of the layer's int32 coordinates compared to CRS coords.
void setScale(const QgsVector3D &scale)
Sets the scale of the layer's int32 coordinates compared to CRS coords.
int pointRecordSize() const
Returns the size of a single point record.
int xOffset() const
Returns the offset for the x value in a point record.
QgsVector3D scale() const
Returns the scale of the layer's int32 coordinates compared to CRS coords.
int zOffset() const
Returns the offset for the y value in a point record.
QgsFeedback * feedback() const
Returns the feedback object used to cancel rendering.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Sets the attributes associated with the rendered block.
virtual QgsPointCloudRenderer * clone() const =0
Create a deep copy of this renderer.
Point cloud data request.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.
bool overlaps(const QgsRange< T > &other) const
Returns true if this range overlaps another range.
Definition: qgsrange.h:147
T lower() const
Returns the lower bound of the range.
Definition: qgsrange.h:66
T upper() const
Returns the upper bound of the range.
Definition: qgsrange.h:73
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool intersects(const QgsRectangle &rect) const SIP_HOLDGIL
Returns true when rectangle intersects with other rectangle.
Definition: qgsrectangle.h:349
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsElevationMap * elevationMap() const
Returns the destination elevation map for the render operation.
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
QgsDoubleRange zRange() const
Returns the range of z-values which should be rendered.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Scoped object for saving and restoring a QPainter object's state.
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:51
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:53
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:49
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38