QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
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#include "qgsruntimeprofiler.h"
37#include "qgsapplication.h"
38
40 : QgsMapLayerRenderer( layer->id(), &context )
41 , mLayer( layer )
42 , mLayerName( layer->name() )
43 , mLayerAttributes( layer->attributes() )
44 , mSubIndexes( layer && layer->dataProvider() ? layer->dataProvider()->subIndexes() : QVector<QgsPointCloudSubIndex>() )
45 , mFeedback( new QgsFeedback )
46 , mEnableProfile( context.flags() & Qgis::RenderContextFlag::RecordProfile )
47{
48 // TODO: we must not keep pointer to mLayer (it's dangerous) - we must copy anything we need for rendering
49 // or use some locking to prevent read/write from multiple threads
50 if ( !mLayer || !mLayer->dataProvider() || !mLayer->renderer() )
51 return;
52
53 QElapsedTimer timer;
54 timer.start();
55
56 mRenderer.reset( mLayer->renderer()->clone() );
57 if ( !mSubIndexes.isEmpty() )
58 mSubIndexExtentRenderer.reset( new QgsPointCloudExtentRenderer() );
59
60 if ( mLayer->dataProvider()->index() )
61 {
62 mScale = mLayer->dataProvider()->index()->scale();
63 mOffset = mLayer->dataProvider()->index()->offset();
64 }
65
66 if ( const QgsPointCloudLayerElevationProperties *elevationProps = qobject_cast< const QgsPointCloudLayerElevationProperties * >( mLayer->elevationProperties() ) )
67 {
68 mZOffset = elevationProps->zOffset();
69 mZScale = elevationProps->zScale();
70 }
71
72 mCloudExtent = mLayer->dataProvider()->polygonBounds();
73
75
76 mReadyToCompose = false;
77
78 mPreparationTime = timer.elapsed();
79}
80
82{
83 std::unique_ptr< QgsScopedRuntimeProfile > profile;
84 if ( mEnableProfile )
85 {
86 profile = std::make_unique< QgsScopedRuntimeProfile >( mLayerName, QStringLiteral( "rendering" ), layerId() );
87 if ( mPreparationTime > 0 )
88 QgsApplication::profiler()->record( QObject::tr( "Create renderer" ), mPreparationTime / 1000.0, QStringLiteral( "rendering" ) );
89 }
90
91 std::unique_ptr< QgsScopedRuntimeProfile > preparingProfile;
92 if ( mEnableProfile )
93 {
94 preparingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Preparing render" ), QStringLiteral( "rendering" ) );
95 }
96
97 QgsPointCloudRenderContext context( *renderContext(), mScale, mOffset, mZScale, mZOffset, mFeedback.get() );
98
99 // Set up the render configuration options
100 QPainter *painter = context.renderContext().painter();
101
102 QgsScopedQPainterState painterState( painter );
103 context.renderContext().setPainterFlagsUsingContext( painter );
104
105 if ( !mClippingRegions.empty() )
106 {
107 bool needsPainterClipPath = false;
108 const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), Qgis::LayerType::VectorTile, needsPainterClipPath );
109 if ( needsPainterClipPath )
110 renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
111 }
112
113 if ( mRenderer->type() == QLatin1String( "extent" ) )
114 {
115 // special case for extent only renderer!
116 mRenderer->startRender( context );
117 static_cast< QgsPointCloudExtentRenderer * >( mRenderer.get() )->renderExtent( mCloudExtent, context );
118 mRenderer->stopRender( context );
119 mReadyToCompose = true;
120 return true;
121 }
122
123 // TODO cache!?
124 QgsPointCloudIndex *pc = mLayer->dataProvider()->index();
125 if ( mSubIndexes.isEmpty() &&
126 ( !pc || !pc->isValid() ) )
127 {
128 mReadyToCompose = true;
129 return false;
130 }
131
132 // if the previous layer render was relatively quick (e.g. less than 3 seconds), the we show any previously
133 // cached version of the layer during rendering instead of the usual progressive updates
134 if ( mRenderTimeHint > 0 && mRenderTimeHint <= MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
135 {
136 mBlockRenderUpdates = true;
137 mElapsedTimer.start();
138 }
139
140 mRenderer->startRender( context );
141
142 mAttributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
143 mAttributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
144
145 if ( !context.renderContext().zRange().isInfinite() ||
146 mRenderer->drawOrder2d() == Qgis::PointCloudDrawOrder::BottomToTop ||
147 mRenderer->drawOrder2d() == Qgis::PointCloudDrawOrder::TopToBottom ||
149 mAttributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Z" ), QgsPointCloudAttribute::Int32 ) );
150
151 // collect attributes required by renderer
152 QSet< QString > rendererAttributes = mRenderer->usedAttributes( context );
153
154
155 for ( const QString &attribute : std::as_const( rendererAttributes ) )
156 {
157 if ( mAttributes.indexOf( attribute ) >= 0 )
158 continue; // don't re-add attributes we are already going to fetch
159
160 const int layerIndex = mLayerAttributes.indexOf( attribute );
161 if ( layerIndex < 0 )
162 {
163 QgsMessageLog::logMessage( QObject::tr( "Required attribute %1 not found in layer" ).arg( attribute ), QObject::tr( "Point Cloud" ) );
164 continue;
165 }
166
167 mAttributes.push_back( mLayerAttributes.at( layerIndex ) );
168 }
169
170 QgsRectangle renderExtent;
171 try
172 {
174 }
175 catch ( QgsCsException & )
176 {
177 QgsDebugError( QStringLiteral( "Transformation of extent failed!" ) );
178 }
179
180 preparingProfile.reset();
181 std::unique_ptr< QgsScopedRuntimeProfile > renderingProfile;
182 if ( mEnableProfile )
183 {
184 renderingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering" ), QStringLiteral( "rendering" ) );
185 }
186
187 bool canceled = false;
188 if ( mSubIndexes.isEmpty() )
189 {
190 canceled = !renderIndex( pc );
191 }
192 else
193 {
194 mSubIndexExtentRenderer->startRender( context );
195 for ( const auto &si : mSubIndexes )
196 {
197 if ( canceled )
198 break;
199
200 QgsPointCloudIndex *pc = si.index();
201
202 if ( !renderExtent.intersects( si.extent() ) )
203 continue;
204
205 if ( !pc || !pc->isValid() || renderExtent.width() > si.extent().width() )
206 {
207 // when dealing with virtual point clouds, we want to render the individual extents when zoomed out
208 // and only use the selected renderer when zoomed in
209 mSubIndexExtentRenderer->renderExtent( si.polygonBounds(), context );
210 }
211 else
212 {
213 canceled = !renderIndex( pc );
214 }
215 }
216 mSubIndexExtentRenderer->stopRender( context );
217 }
218
219 mRenderer->stopRender( context );
220 mReadyToCompose = true;
221 return !canceled;
222}
223
224bool QgsPointCloudLayerRenderer::renderIndex( QgsPointCloudIndex *pc )
225{
227 pc->scale(),
228 pc->offset(),
229 mZScale,
230 mZOffset,
231 mFeedback.get() );
232
233
234#ifdef QGISDEBUG
235 QElapsedTimer t;
236 t.start();
237#endif
238
239 const IndexedPointCloudNode root = pc->root();
240
241 const double maximumError = context.renderContext().convertToPainterUnits( mRenderer->maximumScreenError(), mRenderer->maximumScreenErrorUnit() );// in pixels
242
243 const QgsRectangle rootNodeExtentLayerCoords = pc->nodeMapExtent( root );
244 QgsRectangle rootNodeExtentMapCoords;
245 if ( !context.renderContext().coordinateTransform().isShortCircuited() )
246 {
247 try
248 {
249 QgsCoordinateTransform extentTransform = context.renderContext().coordinateTransform();
250 extentTransform.setBallparkTransformsAreAppropriate( true );
251 rootNodeExtentMapCoords = extentTransform.transformBoundingBox( rootNodeExtentLayerCoords );
252 }
253 catch ( QgsCsException & )
254 {
255 QgsDebugError( QStringLiteral( "Could not transform node extent to map CRS" ) );
256 rootNodeExtentMapCoords = rootNodeExtentLayerCoords;
257 }
258 }
259 else
260 {
261 rootNodeExtentMapCoords = rootNodeExtentLayerCoords;
262 }
263
264 const double rootErrorInMapCoordinates = rootNodeExtentMapCoords.width() / pc->span(); // in map coords
265
266 double mapUnitsPerPixel = context.renderContext().mapToPixel().mapUnitsPerPixel();
267 if ( ( rootErrorInMapCoordinates < 0.0 ) || ( mapUnitsPerPixel < 0.0 ) || ( maximumError < 0.0 ) )
268 {
269 QgsDebugError( QStringLiteral( "invalid screen error" ) );
270 return false;
271 }
272 double rootErrorPixels = rootErrorInMapCoordinates / mapUnitsPerPixel; // in pixels
273 const QVector<IndexedPointCloudNode> nodes = traverseTree( pc, context.renderContext(), pc->root(), maximumError, rootErrorPixels );
274
275 QgsPointCloudRequest request;
276 request.setAttributes( mAttributes );
277
278 // drawing
279 int nodesDrawn = 0;
280 bool canceled = false;
281
282 switch ( mRenderer->drawOrder2d() )
283 {
286 {
287 nodesDrawn += renderNodesSorted( nodes, pc, context, request, canceled, mRenderer->drawOrder2d() );
288 break;
289 }
291 {
292 switch ( pc->accessType() )
293 {
295 {
296 nodesDrawn += renderNodesSync( nodes, pc, context, request, canceled );
297 break;
298 }
300 {
301 nodesDrawn += renderNodesAsync( nodes, pc, context, request, canceled );
302 break;
303 }
304 }
305 }
306 }
307
308#ifdef QGISDEBUG
309 QgsDebugMsgLevel( QStringLiteral( "totals: %1 nodes | %2 points | %3ms" ).arg( nodesDrawn )
310 .arg( context.pointsRendered() )
311 .arg( t.elapsed() ), 2 );
312#else
313 ( void )nodesDrawn;
314#endif
315
316 return !canceled;
317}
318
319int QgsPointCloudLayerRenderer::renderNodesSync( const QVector<IndexedPointCloudNode> &nodes, QgsPointCloudIndex *pc, QgsPointCloudRenderContext &context, QgsPointCloudRequest &request, bool &canceled )
320{
321 int nodesDrawn = 0;
322 for ( const IndexedPointCloudNode &n : nodes )
323 {
324 if ( context.renderContext().renderingStopped() )
325 {
326 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
327 canceled = true;
328 break;
329 }
330 std::unique_ptr<QgsPointCloudBlock> block( pc->nodeData( n, request ) );
331
332 if ( !block )
333 continue;
334
335 QgsVector3D contextScale = context.scale();
336 QgsVector3D contextOffset = context.offset();
337
338 context.setScale( block->scale() );
339 context.setOffset( block->offset() );
340
341 context.setAttributes( block->attributes() );
342
343 mRenderer->renderBlock( block.get(), context );
344
345 context.setScale( contextScale );
346 context.setOffset( contextOffset );
347
348 ++nodesDrawn;
349
350 // as soon as first block is rendered, we can start showing layer updates.
351 // but if we are blocking render updates (so that a previously cached image is being shown), we wait
352 // at most e.g. 3 seconds before we start forcing progressive updates.
353 if ( !mBlockRenderUpdates || mElapsedTimer.elapsed() > MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
354 {
355 mReadyToCompose = true;
356 }
357 }
358 return nodesDrawn;
359}
360
361int QgsPointCloudLayerRenderer::renderNodesAsync( const QVector<IndexedPointCloudNode> &nodes, QgsPointCloudIndex *pc, QgsPointCloudRenderContext &context, QgsPointCloudRequest &request, bool &canceled )
362{
363 int nodesDrawn = 0;
364
365 if ( context.feedback() && context.feedback()->isCanceled() )
366 return 0;
367
368 // Async loading of nodes
369 QVector<QgsPointCloudBlockRequest *> blockRequests;
370 QEventLoop loop;
371 if ( context.feedback() )
372 QObject::connect( context.feedback(), &QgsFeedback::canceled, &loop, &QEventLoop::quit );
373
374 for ( int i = 0; i < nodes.size(); ++i )
375 {
376 const IndexedPointCloudNode &n = nodes[i];
377 const QString nStr = n.toString();
378 QgsPointCloudBlockRequest *blockRequest = pc->asyncNodeData( n, request );
379 blockRequests.append( blockRequest );
380 QObject::connect( blockRequest, &QgsPointCloudBlockRequest::finished, &loop,
381 [ this, &canceled, &nodesDrawn, &loop, &blockRequests, &context, nStr, blockRequest ]()
382 {
383 blockRequests.removeOne( blockRequest );
384
385 // If all blocks are loaded, exit the event loop
386 if ( blockRequests.isEmpty() )
387 loop.exit();
388
389 std::unique_ptr<QgsPointCloudBlock> block( blockRequest->takeBlock() );
390
391 blockRequest->deleteLater();
392
393 if ( context.feedback() && context.feedback()->isCanceled() )
394 {
395 canceled = true;
396 return;
397 }
398
399 if ( !block )
400 {
401 QgsDebugError( QStringLiteral( "Unable to load node %1, error: %2" ).arg( nStr, blockRequest->errorStr() ) );
402 return;
403 }
404
405 QgsVector3D contextScale = context.scale();
406 QgsVector3D contextOffset = context.offset();
407
408 context.setScale( block->scale() );
409 context.setOffset( block->offset() );
410 context.setAttributes( block->attributes() );
411
412 mRenderer->renderBlock( block.get(), context );
413
414 context.setScale( contextScale );
415 context.setOffset( contextOffset );
416
417 ++nodesDrawn;
418
419 // as soon as first block is rendered, we can start showing layer updates.
420 // but if we are blocking render updates (so that a previously cached image is being shown), we wait
421 // at most e.g. 3 seconds before we start forcing progressive updates.
422 if ( !mBlockRenderUpdates || mElapsedTimer.elapsed() > MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
423 {
424 mReadyToCompose = true;
425 }
426
427 } );
428 }
429
430 // Wait for all point cloud nodes to finish loading
431 loop.exec();
432
433 // Rendering may have got canceled and the event loop exited before finished()
434 // was called for all blocks, so let's clean up anything that is left
435 for ( QgsPointCloudBlockRequest *blockRequest : std::as_const( blockRequests ) )
436 {
437 std::unique_ptr<QgsPointCloudBlock> block = blockRequest->takeBlock();
438 block.reset();
439
440 blockRequest->deleteLater();
441 }
442
443 return nodesDrawn;
444}
445
446int QgsPointCloudLayerRenderer::renderNodesSorted( const QVector<IndexedPointCloudNode> &nodes, QgsPointCloudIndex *pc, QgsPointCloudRenderContext &context, QgsPointCloudRequest &request, bool &canceled, Qgis::PointCloudDrawOrder order )
447{
448 int blockCount = 0;
449 int pointCount = 0;
450
451 QgsVector3D blockScale;
452 QgsVector3D blockOffset;
453 QgsPointCloudAttributeCollection blockAttributes;
454 int recordSize = 0;
455
456 // We'll collect byte array data from all blocks
457 QByteArray allByteArrays;
458 // And pairs of byte array start positions paired with their Z values for sorting
459 QVector<QPair<int, double>> allPairs;
460
461 for ( const IndexedPointCloudNode &n : nodes )
462 {
463 if ( context.renderContext().renderingStopped() )
464 {
465 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
466 canceled = true;
467 break;
468 }
469 std::unique_ptr<QgsPointCloudBlock> block( pc->nodeData( n, request ) );
470
471 if ( !block )
472 continue;
473
474 // Individual nodes may have different offset values than the root node
475 // we'll calculate the differences and translate x,y,z values to use the root node's offset
476 QgsVector3D offsetDifference = QgsVector3D( 0, 0, 0 );
477 if ( blockCount == 0 )
478 {
479 blockScale = block->scale();
480 blockOffset = block->offset();
481 blockAttributes = block->attributes();
482 }
483 else
484 {
485 offsetDifference = blockOffset - block->offset();
486 }
487
488 const char *ptr = block->data();
489
490 context.setScale( block->scale() );
491 context.setOffset( block->offset() );
492 context.setAttributes( block->attributes() );
493
494 recordSize = context.pointRecordSize();
495
496 for ( int i = 0; i < block->pointCount(); ++i )
497 {
498 allByteArrays.append( ptr + i * recordSize, recordSize );
499
500 // Calculate the translated values only for axes that have a different offset
501 if ( offsetDifference.x() != 0 )
502 {
503 qint32 ix = *reinterpret_cast< const qint32 * >( ptr + i * recordSize + context.xOffset() );
504 ix -= std::lround( offsetDifference.x() / context.scale().x() );
505 const char *xPtr = reinterpret_cast< const char * >( &ix );
506 allByteArrays.replace( pointCount * recordSize + context.xOffset(), 4, QByteArray( xPtr, 4 ) );
507 }
508 if ( offsetDifference.y() != 0 )
509 {
510 qint32 iy = *reinterpret_cast< const qint32 * >( ptr + i * recordSize + context.yOffset() );
511 iy -= std::lround( offsetDifference.y() / context.scale().y() );
512 const char *yPtr = reinterpret_cast< const char * >( &iy );
513 allByteArrays.replace( pointCount * recordSize + context.yOffset(), 4, QByteArray( yPtr, 4 ) );
514 }
515 // We need the Z value regardless of the node's offset
516 qint32 iz = *reinterpret_cast< const qint32 * >( ptr + i * recordSize + context.zOffset() );
517 if ( offsetDifference.z() != 0 )
518 {
519 iz -= std::lround( offsetDifference.z() / context.scale().z() );
520 const char *zPtr = reinterpret_cast< const char * >( &iz );
521 allByteArrays.replace( pointCount * recordSize + context.zOffset(), 4, QByteArray( zPtr, 4 ) );
522 }
523 allPairs.append( qMakePair( pointCount, double( iz ) + block->offset().z() ) );
524
525 ++pointCount;
526 }
527 ++blockCount;
528 }
529
530 if ( pointCount == 0 )
531 return 0;
532
533 switch ( order )
534 {
536 std::sort( allPairs.begin(), allPairs.end(), []( QPair<int, double> a, QPair<int, double> b ) { return a.second < b.second; } );
537 break;
539 std::sort( allPairs.begin(), allPairs.end(), []( QPair<int, double> a, QPair<int, double> b ) { return a.second > b.second; } );
540 break;
542 break;
543 }
544
545 // Now we can reconstruct a byte array sorted by Z value
546 QByteArray sortedByteArray;
547 sortedByteArray.reserve( allPairs.size() );
548 for ( QPair<int, double> pair : allPairs )
549 sortedByteArray.append( allByteArrays.mid( pair.first * recordSize, recordSize ) );
550
551 std::unique_ptr<QgsPointCloudBlock> bigBlock { new QgsPointCloudBlock( pointCount,
552 blockAttributes,
553 sortedByteArray,
554 blockScale,
555 blockOffset ) };
556
557 QgsVector3D contextScale = context.scale();
558 QgsVector3D contextOffset = context.offset();
559
560 context.setScale( bigBlock->scale() );
561 context.setOffset( bigBlock->offset() );
562 context.setAttributes( bigBlock->attributes() );
563
564 mRenderer->renderBlock( bigBlock.get(), context );
565
566 context.setScale( contextScale );
567 context.setOffset( contextOffset );
568
569 return blockCount;
570}
571
573{
574 // unless we are using the extent only renderer, point cloud layers should always be rasterized -- we don't want to export points as vectors
575 // to formats like PDF!
576 return mRenderer ? mRenderer->type() != QLatin1String( "extent" ) : false;
577}
578
580{
581 mRenderTimeHint = time;
582}
583
584QVector<IndexedPointCloudNode> QgsPointCloudLayerRenderer::traverseTree( const QgsPointCloudIndex *pc,
585 const QgsRenderContext &context,
587 double maxErrorPixels,
588 double nodeErrorPixels )
589{
590 QVector<IndexedPointCloudNode> nodes;
591
592 if ( context.renderingStopped() )
593 {
594 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
595 return nodes;
596 }
597
598 if ( !context.extent().intersects( pc->nodeMapExtent( n ) ) )
599 return nodes;
600
601 const QgsDoubleRange nodeZRange = pc->nodeZRange( n );
602 const QgsDoubleRange adjustedNodeZRange = QgsDoubleRange( nodeZRange.lower() + mZOffset, nodeZRange.upper() + mZOffset );
603 if ( !context.zRange().isInfinite() && !context.zRange().overlaps( adjustedNodeZRange ) )
604 return nodes;
605
606 if ( pc->nodePointCount( n ) > 0 )
607 nodes.append( n );
608
609 double childrenErrorPixels = nodeErrorPixels / 2.0;
610 if ( childrenErrorPixels < maxErrorPixels )
611 return nodes;
612
613 const QList<IndexedPointCloudNode> children = pc->nodeChildren( n );
614 for ( const IndexedPointCloudNode &nn : children )
615 {
616 nodes += traverseTree( pc, context, nn, maxErrorPixels, childrenErrorPixels );
617 }
618
619 return nodes;
620}
621
Represents a indexed point cloud node in octree.
QString toString() const
Encode node to string.
The Qgis class provides global constants for use throughout the application.
Definition qgis.h:54
PointCloudDrawOrder
Pointcloud rendering order for 2d views.
Definition qgis.h:2888
@ 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.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Reverse
Reverse/inverse transform (from destination to source)
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
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
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
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
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:54
void canceled()
Internal routines can connect to this signal if they use event loop.
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,...
QString layerId() const
Gets access to the ID of the layer rendered by this class.
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.
void finished()
Emitted when the request processing has finished.
std::unique_ptr< QgsPointCloudBlock > takeBlock()
Returns the requested block.
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.
@ Remote
Remote means it's loaded through a protocol like HTTP.
@ Local
Local means the source is a local file on the machine.
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.
IndexedPointCloudNode root()
Returns root node of the index.
QgsDoubleRange nodeZRange(const IndexedPointCloudNode &node) const
Returns the z range of a node.
virtual std::unique_ptr< QgsPointCloudBlock > nodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns node data block.
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.
bool intersects(const QgsRectangle &rect) const
Returns true when rectangle intersects with other rectangle.
double width() const
Returns the width of the rectangle.
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.
void record(const QString &name, double time, const QString &group="startup", const QString &id=QString())
Manually adds a profile event with the given name and total time (in seconds).
Scoped object for saving and restoring a QPainter object's state.
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition qgsvector3d.h:32
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