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