QGIS API Documentation 3.29.0-Master (006c3c0232)
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 "qgsstyle.h"
26#include "qgscolorramp.h"
27#include "qgselevationmap.h"
32#include "qgslogger.h"
34#include "qgsmessagelog.h"
35#include "qgscircle.h"
36#include "qgsmapclippingutils.h"
38
40 : QgsMapLayerRenderer( layer->id(), &context )
41 , mLayer( layer )
42 , mLayerAttributes( layer->attributes() )
43 , mFeedback( new QgsFeedback )
44{
45 // TODO: we must not keep pointer to mLayer (it's dangerous) - we must copy anything we need for rendering
46 // or use some locking to prevent read/write from multiple threads
47 if ( !mLayer || !mLayer->dataProvider() || !mLayer->renderer() )
48 return;
49
50 mRenderer.reset( mLayer->renderer()->clone() );
51
52 if ( mLayer->dataProvider()->index() )
53 {
54 mScale = mLayer->dataProvider()->index()->scale();
55 mOffset = mLayer->dataProvider()->index()->offset();
56 }
57
58 if ( const QgsPointCloudLayerElevationProperties *elevationProps = qobject_cast< const QgsPointCloudLayerElevationProperties * >( mLayer->elevationProperties() ) )
59 {
60 mZOffset = elevationProps->zOffset();
61 mZScale = elevationProps->zScale();
62 }
63
64 mCloudExtent = mLayer->dataProvider()->polygonBounds();
65
67
68 mReadyToCompose = false;
69}
70
72{
73 QgsPointCloudRenderContext context( *renderContext(), mScale, mOffset, mZScale, mZOffset, mFeedback.get() );
74
75 // Set up the render configuration options
76 QPainter *painter = context.renderContext().painter();
77 bool applyEdl = mRenderer && mRenderer->eyeDomeLightingEnabled();
78
79 if ( QImage *painterImage = dynamic_cast<QImage *>( painter->device() ) )
80 {
81 if ( applyEdl )
82 context.setElevationMap( new QgsElevationMap( painterImage->size() ) );
83 }
84
85 QgsScopedQPainterState painterState( painter );
86 context.renderContext().setPainterFlagsUsingContext( painter );
87
88 if ( !mClippingRegions.empty() )
89 {
90 bool needsPainterClipPath = false;
91 const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), QgsMapLayerType::VectorTileLayer, needsPainterClipPath );
92 if ( needsPainterClipPath )
93 renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
94 }
95
96 if ( mRenderer->type() == QLatin1String( "extent" ) )
97 {
98 // special case for extent only renderer!
99 mRenderer->startRender( context );
100 static_cast< QgsPointCloudExtentRenderer * >( mRenderer.get() )->renderExtent( mCloudExtent, context );
101 mRenderer->stopRender( context );
102 mReadyToCompose = true;
103 return true;
104 }
105
106 // TODO cache!?
107 QgsPointCloudIndex *pc = mLayer->dataProvider()->index();
108 if ( !pc || !pc->isValid() )
109 {
110 mReadyToCompose = true;
111 return false;
112 }
113
114 // if the previous layer render was relatively quick (e.g. less than 3 seconds), the we show any previously
115 // cached version of the layer during rendering instead of the usual progressive updates
116 if ( mRenderTimeHint > 0 && mRenderTimeHint <= MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
117 {
118 mBlockRenderUpdates = true;
119 mElapsedTimer.start();
120 }
121
122 mRenderer->startRender( context );
123
124 mAttributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
125 mAttributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
126
127 if ( !context.renderContext().zRange().isInfinite() ||
128 mRenderer->drawOrder2d() == Qgis::PointCloudDrawOrder::BottomToTop ||
129 mRenderer->drawOrder2d() == Qgis::PointCloudDrawOrder::TopToBottom ||
130 applyEdl )
131 mAttributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
132
133 // collect attributes required by renderer
134 QSet< QString > rendererAttributes = mRenderer->usedAttributes( context );
135
136
137 for ( const QString &attribute : std::as_const( rendererAttributes ) )
138 {
139 if ( mAttributes.indexOf( attribute ) >= 0 )
140 continue; // don't re-add attributes we are already going to fetch
141
142 const int layerIndex = mLayerAttributes.indexOf( attribute );
143 if ( layerIndex < 0 )
144 {
145 QgsMessageLog::logMessage( QObject::tr( "Required attribute %1 not found in layer" ).arg( attribute ), QObject::tr( "Point Cloud" ) );
146 continue;
147 }
148
149 mAttributes.push_back( mLayerAttributes.at( layerIndex ) );
150 }
151
153
154#ifdef QGISDEBUG
155 QElapsedTimer t;
156 t.start();
157#endif
158
159 const IndexedPointCloudNode root = pc->root();
160
161 const double maximumError = context.renderContext().convertToPainterUnits( mRenderer->maximumScreenError(), mRenderer->maximumScreenErrorUnit() );// in pixels
162
163 const QgsRectangle rootNodeExtentLayerCoords = pc->nodeMapExtent( root );
164 QgsRectangle rootNodeExtentMapCoords;
166 {
167 try
168 {
169 QgsCoordinateTransform extentTransform = context.renderContext().coordinateTransform();
170 extentTransform.setBallparkTransformsAreAppropriate( true );
171 rootNodeExtentMapCoords = extentTransform.transformBoundingBox( rootNodeExtentLayerCoords );
172 }
173 catch ( QgsCsException & )
174 {
175 QgsDebugMsg( QStringLiteral( "Could not transform node extent to map CRS" ) );
176 rootNodeExtentMapCoords = rootNodeExtentLayerCoords;
177 }
178 }
179 else
180 {
181 rootNodeExtentMapCoords = rootNodeExtentLayerCoords;
182 }
183
184 const double rootErrorInMapCoordinates = rootNodeExtentMapCoords.width() / pc->span(); // in map coords
185
186 double mapUnitsPerPixel = context.renderContext().mapToPixel().mapUnitsPerPixel();
187 if ( ( rootErrorInMapCoordinates < 0.0 ) || ( mapUnitsPerPixel < 0.0 ) || ( maximumError < 0.0 ) )
188 {
189 QgsDebugMsg( QStringLiteral( "invalid screen error" ) );
190 mReadyToCompose = true;
191 return false;
192 }
193 double rootErrorPixels = rootErrorInMapCoordinates / mapUnitsPerPixel; // in pixels
194 const QVector<IndexedPointCloudNode> nodes = traverseTree( pc, context.renderContext(), pc->root(), maximumError, rootErrorPixels );
195
196 QgsPointCloudRequest request;
197 request.setAttributes( mAttributes );
198
199 // drawing
200 int nodesDrawn = 0;
201 bool canceled = false;
202
203 switch ( mRenderer->drawOrder2d() )
204 {
207 {
208 nodesDrawn += renderNodesSorted( nodes, pc, context, request, canceled, mRenderer->drawOrder2d() );
209 break;
210 }
212 {
213 switch ( pc->accessType() )
214 {
215 case QgsPointCloudIndex::AccessType::Local:
216 {
217 nodesDrawn += renderNodesSync( nodes, pc, context, request, canceled );
218 break;
219 }
220 case QgsPointCloudIndex::AccessType::Remote:
221 {
222 nodesDrawn += renderNodesAsync( nodes, pc, context, request, canceled );
223 break;
224 }
225 }
226 }
227 }
228
229#ifdef QGISDEBUG
230 QgsDebugMsgLevel( QStringLiteral( "totals: %1 nodes | %2 points | %3ms" ).arg( nodesDrawn )
231 .arg( context.pointsRendered() )
232 .arg( t.elapsed() ), 2 );
233#else
234 ( void )nodesDrawn;
235#endif
236
237 mRenderer->stopRender( context );
238
239 if ( applyEdl )
240 {
241 if ( QImage *drawnImage = dynamic_cast<QImage *>( painter->device() ) )
242 {
243 double strength = mRenderer->eyeDomeLightingStrength();
244 double distanceDouble = context.renderContext().convertToPainterUnits(
245 mRenderer->eyeDomeLightingDistance(), mRenderer->eyeDomeLightingDistanceUnit() );
246 int distance = static_cast<int>( std::round( distanceDouble ) );
247 context.elevationMap()->applyEyeDomeLighting( *drawnImage, distance, strength, context.renderContext().rendererScale() );
248 }
249 }
250
251 mReadyToCompose = true;
252 return !canceled;
253}
254
255int QgsPointCloudLayerRenderer::renderNodesSync( const QVector<IndexedPointCloudNode> &nodes, QgsPointCloudIndex *pc, QgsPointCloudRenderContext &context, QgsPointCloudRequest &request, bool &canceled )
256{
257 int nodesDrawn = 0;
258 for ( const IndexedPointCloudNode &n : nodes )
259 {
260 if ( context.renderContext().renderingStopped() )
261 {
262 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
263 canceled = true;
264 break;
265 }
266 std::unique_ptr<QgsPointCloudBlock> block( pc->nodeData( n, request ) );
267
268 if ( !block )
269 continue;
270
271 QgsVector3D contextScale = context.scale();
272 QgsVector3D contextOffset = context.offset();
273
274 context.setScale( block->scale() );
275 context.setOffset( block->offset() );
276
277 context.setAttributes( block->attributes() );
278
279 mRenderer->renderBlock( block.get(), context );
280
281 context.setScale( contextScale );
282 context.setOffset( contextOffset );
283
284 ++nodesDrawn;
285
286 // as soon as first block is rendered, we can start showing layer updates.
287 // but if we are blocking render updates (so that a previously cached image is being shown), we wait
288 // at most e.g. 3 seconds before we start forcing progressive updates.
289 if ( !mBlockRenderUpdates || mElapsedTimer.elapsed() > MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
290 {
291 mReadyToCompose = true;
292 }
293 }
294 return nodesDrawn;
295}
296
297int QgsPointCloudLayerRenderer::renderNodesAsync( const QVector<IndexedPointCloudNode> &nodes, QgsPointCloudIndex *pc, QgsPointCloudRenderContext &context, QgsPointCloudRequest &request, bool &canceled )
298{
299 int nodesDrawn = 0;
300
301 if ( context.feedback() && context.feedback()->isCanceled() )
302 return 0;
303
304 // Async loading of nodes
305 QVector<QgsPointCloudBlockRequest *> blockRequests;
306 QEventLoop loop;
307 if ( context.feedback() )
308 QObject::connect( context.feedback(), &QgsFeedback::canceled, &loop, &QEventLoop::quit );
309
310 for ( int i = 0; i < nodes.size(); ++i )
311 {
312 const IndexedPointCloudNode &n = nodes[i];
313 const QString nStr = n.toString();
314 QgsPointCloudBlockRequest *blockRequest = pc->asyncNodeData( n, request );
315 blockRequests.append( blockRequest );
316 QObject::connect( blockRequest, &QgsPointCloudBlockRequest::finished, &loop,
317 [ this, &canceled, &nodesDrawn, &loop, &blockRequests, &context, nStr, blockRequest ]()
318 {
319 blockRequests.removeOne( blockRequest );
320
321 // If all blocks are loaded, exit the event loop
322 if ( blockRequests.isEmpty() )
323 loop.exit();
324
325 std::unique_ptr<QgsPointCloudBlock> block( blockRequest->block() );
326
327 blockRequest->deleteLater();
328
329 if ( context.feedback() && context.feedback()->isCanceled() )
330 {
331 canceled = true;
332 return;
333 }
334
335 if ( !block )
336 {
337 QgsDebugMsg( QStringLiteral( "Unable to load node %1, error: %2" ).arg( nStr, blockRequest->errorStr() ) );
338 return;
339 }
340
341 QgsVector3D contextScale = context.scale();
342 QgsVector3D contextOffset = context.offset();
343
344 context.setScale( block->scale() );
345 context.setOffset( block->offset() );
346 context.setAttributes( block->attributes() );
347
348 mRenderer->renderBlock( block.get(), context );
349
350 context.setScale( contextScale );
351 context.setOffset( contextOffset );
352
353 ++nodesDrawn;
354
355 // as soon as first block is rendered, we can start showing layer updates.
356 // but if we are blocking render updates (so that a previously cached image is being shown), we wait
357 // at most e.g. 3 seconds before we start forcing progressive updates.
358 if ( !mBlockRenderUpdates || mElapsedTimer.elapsed() > MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
359 {
360 mReadyToCompose = true;
361 }
362
363 } );
364 }
365
366 // Wait for all point cloud nodes to finish loading
367 loop.exec();
368
369 // Rendering may have got canceled and the event loop exited before finished()
370 // was called for all blocks, so let's clean up anything that is left
371 for ( QgsPointCloudBlockRequest *blockRequest : std::as_const( blockRequests ) )
372 {
373 delete blockRequest->block();
374 blockRequest->deleteLater();
375 }
376
377 return nodesDrawn;
378}
379
380int QgsPointCloudLayerRenderer::renderNodesSorted( const QVector<IndexedPointCloudNode> &nodes, QgsPointCloudIndex *pc, QgsPointCloudRenderContext &context, QgsPointCloudRequest &request, bool &canceled, Qgis::PointCloudDrawOrder order )
381{
382 int blockCount = 0;
383 int pointCount = 0;
384
385 QgsVector3D blockScale;
386 QgsVector3D blockOffset;
387 QgsPointCloudAttributeCollection blockAttributes;
388 int recordSize = 0;
389
390 // We'll collect byte array data from all blocks
391 QByteArray allByteArrays;
392 // And pairs of byte array start positions paired with their Z values for sorting
393 QVector<QPair<int, double>> allPairs;
394
395 for ( const IndexedPointCloudNode &n : nodes )
396 {
397 if ( context.renderContext().renderingStopped() )
398 {
399 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
400 canceled = true;
401 break;
402 }
403 std::unique_ptr<QgsPointCloudBlock> block( pc->nodeData( n, request ) );
404
405 if ( !block )
406 continue;
407
408 // Individual nodes may have different offset values than the root node
409 // we'll calculate the differences and translate x,y,z values to use the root node's offset
410 QgsVector3D offsetDifference = QgsVector3D( 0, 0, 0 );
411 if ( blockCount == 0 )
412 {
413 blockScale = block->scale();
414 blockOffset = block->offset();
415 blockAttributes = block->attributes();
416 }
417 else
418 {
419 offsetDifference = blockOffset - block->offset();
420 }
421
422 const char *ptr = block->data();
423
424 context.setScale( block->scale() );
425 context.setOffset( block->offset() );
426 context.setAttributes( block->attributes() );
427
428 recordSize = context.pointRecordSize();
429
430 for ( int i = 0; i < block->pointCount(); ++i )
431 {
432 allByteArrays.append( ptr + i * recordSize, recordSize );
433
434 // Calculate the translated values only for axes that have a different offset
435 if ( offsetDifference.x() != 0 )
436 {
437 qint32 ix = *reinterpret_cast< const qint32 * >( ptr + i * recordSize + context.xOffset() );
438 ix -= std::lround( offsetDifference.x() / context.scale().x() );
439 const char *xPtr = reinterpret_cast< const char * >( &ix );
440 allByteArrays.replace( pointCount * recordSize + context.xOffset(), 4, QByteArray( xPtr, 4 ) );
441 }
442 if ( offsetDifference.y() != 0 )
443 {
444 qint32 iy = *reinterpret_cast< const qint32 * >( ptr + i * recordSize + context.yOffset() );
445 iy -= std::lround( offsetDifference.y() / context.scale().y() );
446 const char *yPtr = reinterpret_cast< const char * >( &iy );
447 allByteArrays.replace( pointCount * recordSize + context.yOffset(), 4, QByteArray( yPtr, 4 ) );
448 }
449 // We need the Z value regardless of the node's offset
450 qint32 iz = *reinterpret_cast< const qint32 * >( ptr + i * recordSize + context.zOffset() );
451 if ( offsetDifference.z() != 0 )
452 {
453 iz -= std::lround( offsetDifference.z() / context.scale().z() );
454 const char *zPtr = reinterpret_cast< const char * >( &iz );
455 allByteArrays.replace( pointCount * recordSize + context.zOffset(), 4, QByteArray( zPtr, 4 ) );
456 }
457 allPairs.append( qMakePair( pointCount, double( iz ) + block->offset().z() ) );
458
459 ++pointCount;
460 }
461 ++blockCount;
462 }
463
464 if ( pointCount == 0 )
465 return 0;
466
467 switch ( order )
468 {
470 std::sort( allPairs.begin(), allPairs.end(), []( QPair<int, double> a, QPair<int, double> b ) { return a.second < b.second; } );
471 break;
473 std::sort( allPairs.begin(), allPairs.end(), []( QPair<int, double> a, QPair<int, double> b ) { return a.second > b.second; } );
474 break;
476 break;
477 }
478
479 // Now we can reconstruct a byte array sorted by Z value
480 QByteArray sortedByteArray;
481 sortedByteArray.reserve( allPairs.size() );
482 for ( QPair<int, double> pair : allPairs )
483 sortedByteArray.append( allByteArrays.mid( pair.first * recordSize, recordSize ) );
484
485 std::unique_ptr<QgsPointCloudBlock> bigBlock { new QgsPointCloudBlock( pointCount,
486 blockAttributes,
487 sortedByteArray,
488 blockScale,
489 blockOffset ) };
490
491 QgsVector3D contextScale = context.scale();
492 QgsVector3D contextOffset = context.offset();
493
494 context.setScale( bigBlock->scale() );
495 context.setOffset( bigBlock->offset() );
496 context.setAttributes( bigBlock->attributes() );
497
498 mRenderer->renderBlock( bigBlock.get(), context );
499
500 context.setScale( contextScale );
501 context.setOffset( contextOffset );
502
503 return blockCount;
504}
505
507{
508 // unless we are using the extent only renderer, point cloud layers should always be rasterized -- we don't want to export points as vectors
509 // to formats like PDF!
510 return mRenderer ? mRenderer->type() != QLatin1String( "extent" ) : false;
511}
512
514{
515 mRenderTimeHint = time;
516}
517
518QVector<IndexedPointCloudNode> QgsPointCloudLayerRenderer::traverseTree( const QgsPointCloudIndex *pc,
519 const QgsRenderContext &context,
521 double maxErrorPixels,
522 double nodeErrorPixels )
523{
524 QVector<IndexedPointCloudNode> nodes;
525
526 if ( context.renderingStopped() )
527 {
528 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
529 return nodes;
530 }
531
532 if ( !context.extent().intersects( pc->nodeMapExtent( n ) ) )
533 return nodes;
534
535 const QgsDoubleRange nodeZRange = pc->nodeZRange( n );
536 const QgsDoubleRange adjustedNodeZRange = QgsDoubleRange( nodeZRange.lower() + mZOffset, nodeZRange.upper() + mZOffset );
537 if ( !context.zRange().isInfinite() && !context.zRange().overlaps( adjustedNodeZRange ) )
538 return nodes;
539
540 if ( pc->nodePointCount( n ) > 0 )
541 nodes.append( n );
542
543 double childrenErrorPixels = nodeErrorPixels / 2.0;
544 if ( childrenErrorPixels < maxErrorPixels )
545 return nodes;
546
547 const QList<IndexedPointCloudNode> children = pc->nodeChildren( n );
548 for ( const IndexedPointCloudNode &nn : children )
549 {
550 nodes += traverseTree( pc, context, nn, maxErrorPixels, childrenErrorPixels );
551 }
552
553 return nodes;
554}
555
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:2301
@ 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.
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent.
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:66
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
Stores digital elevation model in a raster image which may get updated as a part of map layer renderi...
void applyEyeDomeLighting(QImage &img, int distance, float strength, float rendererScale)
Applies eye dome lighting effect to the given image.
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 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.
static QPainterPath calculatePainterClipRegion(const QList< QgsMapClippingRegion > &regions, const QgsRenderContext &context, QgsMapLayerType layerType, bool &shouldClip)
Returns a QPainterPath representing the intersection of clipping regions from context which should be...
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.
double mapUnitsPerPixel() const
Returns the current map units per pixel.
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.
Represents packaged data bounds.
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.
void setElevationMap(QgsElevationMap *elevationMap)
Sets elevation map that will be used to record elevation of rendered points.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
long pointsRendered() const
Returns the total number of points rendered.
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.
QgsElevationMap * elevationMap()
Returns elevation map.
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.
double rendererScale() const
Returns the renderer map scale.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
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
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38