QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 "qgspointcloudrequest.h"
28 #include "qgspointcloudattribute.h"
29 #include "qgspointcloudrenderer.h"
31 #include "qgslogger.h"
33 #include "qgsmessagelog.h"
34 #include "qgscircle.h"
35 #include "qgsmapclippingutils.h"
37 
39  : QgsMapLayerRenderer( layer->id(), &context )
40  , mLayer( layer )
41  , mLayerAttributes( layer->attributes() )
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 
51  if ( mLayer->dataProvider()->index() )
52  {
53  mScale = mLayer->dataProvider()->index()->scale();
54  mOffset = mLayer->dataProvider()->index()->offset();
55  }
56 
57  if ( const QgsPointCloudLayerElevationProperties *elevationProps = qobject_cast< const QgsPointCloudLayerElevationProperties * >( mLayer->elevationProperties() ) )
58  {
59  mZOffset = elevationProps->zOffset();
60  mZScale = elevationProps->zScale();
61  }
62 
63  mCloudExtent = mLayer->dataProvider()->polygonBounds();
64 
66 
67  mReadyToCompose = false;
68 }
69 
71 {
72  QgsPointCloudRenderContext context( *renderContext(), mScale, mOffset, mZScale, mZOffset, mFeedback.get() );
73 
74  // Set up the render configuration options
75  QPainter *painter = context.renderContext().painter();
76 
77  QgsScopedQPainterState painterState( painter );
78  context.renderContext().setPainterFlagsUsingContext( painter );
79 
80  if ( !mClippingRegions.empty() )
81  {
82  bool needsPainterClipPath = false;
83  const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), QgsMapLayerType::VectorTileLayer, needsPainterClipPath );
84  if ( needsPainterClipPath )
85  renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
86  }
87 
88  if ( mRenderer->type() == QLatin1String( "extent" ) )
89  {
90  // special case for extent only renderer!
91  mRenderer->startRender( context );
92  static_cast< QgsPointCloudExtentRenderer * >( mRenderer.get() )->renderExtent( mCloudExtent, context );
93  mRenderer->stopRender( context );
94  mReadyToCompose = true;
95  return true;
96  }
97 
98  // TODO cache!?
99  QgsPointCloudIndex *pc = mLayer->dataProvider()->index();
100  if ( !pc || !pc->isValid() )
101  {
102  mReadyToCompose = true;
103  return false;
104  }
105 
106  // if the previous layer render was relatively quick (e.g. less than 3 seconds), the we show any previously
107  // cached version of the layer during rendering instead of the usual progressive updates
108  if ( mRenderTimeHint > 0 && mRenderTimeHint <= MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
109  {
110  mBlockRenderUpdates = true;
111  mElapsedTimer.start();
112  }
113 
114  mRenderer->startRender( context );
115 
116  mAttributes.push_back( QgsPointCloudAttribute( QStringLiteral( "X" ), QgsPointCloudAttribute::Int32 ) );
117  mAttributes.push_back( QgsPointCloudAttribute( QStringLiteral( "Y" ), QgsPointCloudAttribute::Int32 ) );
118 
119  if ( !context.renderContext().zRange().isInfinite() ||
120  mRenderer->drawOrder2d() == QgsPointCloudRenderer::DrawOrder::BottomToTop ||
121  mRenderer->drawOrder2d() == QgsPointCloudRenderer::DrawOrder::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 
234 int 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 
276 int QgsPointCloudLayerRenderer::renderNodesAsync( const QVector<IndexedPointCloudNode> &nodes, QgsPointCloudIndex *pc, QgsPointCloudRenderContext &context, QgsPointCloudRequest &request, bool &canceled )
277 {
278  int nodesDrawn = 0;
279 
280  QElapsedTimer downloadTimer;
281  downloadTimer.start();
282 
283  // Instead of loading all point blocks in parallel and then rendering the one by one,
284  // we split the processing into groups of size groupSize where we load the blocks of the group
285  // in parallel and then render the group's blocks sequentially.
286  // This way helps QGIS stay responsive if the nodes vector size is big
287  const int groupSize = 4;
288  for ( int groupIndex = 0; groupIndex < nodes.size(); groupIndex += groupSize )
289  {
290  if ( context.feedback() && context.feedback()->isCanceled() )
291  break;
292  // Async loading of nodes
293  const int currentGroupSize = std::min< size_t >( std::max< size_t >( nodes.size() - groupIndex, 0 ), groupSize );
294  QVector<QgsPointCloudBlockRequest *> blockRequests( currentGroupSize, nullptr );
295  QVector<bool> finishedLoadingBlock( currentGroupSize, false );
296  QEventLoop loop;
297  if ( context.feedback() )
298  QObject::connect( context.feedback(), &QgsFeedback::canceled, &loop, &QEventLoop::quit );
299  // Note: All capture by reference warnings here shouldn't be an issue since we have an event loop, so locals won't be deallocated
300  for ( int i = 0; i < blockRequests.size(); ++i )
301  {
302  int nodeIndex = groupIndex + i;
303  const IndexedPointCloudNode &n = nodes[nodeIndex];
304  const QString nStr = n.toString();
305  QgsPointCloudBlockRequest *blockRequest = pc->asyncNodeData( n, request );
306  blockRequests[ i ] = blockRequest;
307  QObject::connect( blockRequest, &QgsPointCloudBlockRequest::finished, &loop, [ &, i, nStr, blockRequest ]()
308  {
309  if ( !blockRequest->block() )
310  {
311  QgsDebugMsg( QStringLiteral( "Unable to load node %1, error: %2" ).arg( nStr, blockRequest->errorStr() ) );
312  }
313  finishedLoadingBlock[ i ] = true;
314  // If all blocks are loaded, exit the event loop
315  if ( !finishedLoadingBlock.contains( false ) ) loop.exit();
316  } );
317  }
318  // Wait for all point cloud nodes to finish loading
319  loop.exec();
320 
321  QgsDebugMsgLevel( QStringLiteral( "Downloaded in : %1ms" ).arg( downloadTimer.elapsed() ), 2 );
322  if ( !context.feedback()->isCanceled() )
323  {
324  // Render all the point cloud blocks sequentially
325  for ( int i = 0; i < blockRequests.size(); ++i )
326  {
327  if ( context.renderContext().renderingStopped() )
328  {
329  QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
330  canceled = true;
331  break;
332  }
333 
334  if ( !blockRequests[ i ]->block() )
335  continue;
336 
337  QgsVector3D contextScale = context.scale();
338  QgsVector3D contextOffset = context.offset();
339 
340  context.setScale( blockRequests[ i ]->block()->scale() );
341  context.setOffset( blockRequests[ i ]->block()->offset() );
342 
343  context.setAttributes( blockRequests[ i ]->block()->attributes() );
344 
345  mRenderer->renderBlock( blockRequests[ i ]->block(), context );
346 
347  context.setScale( contextScale );
348  context.setOffset( contextOffset );
349 
350  ++nodesDrawn;
351 
352  // as soon as first block is rendered, we can start showing layer updates.
353  // but if we are blocking render updates (so that a previously cached image is being shown), we wait
354  // at most e.g. 3 seconds before we start forcing progressive updates.
355  if ( !mBlockRenderUpdates || mElapsedTimer.elapsed() > MAX_TIME_TO_USE_CACHED_PREVIEW_IMAGE )
356  {
357  mReadyToCompose = true;
358  }
359  }
360  }
361 
362  for ( int i = 0; i < blockRequests.size(); ++i )
363  {
364  if ( blockRequests[ i ] )
365  {
366  if ( blockRequests[ i ]->block() )
367  delete blockRequests[ i ]->block();
368  blockRequests[ i ]->deleteLater();
369  }
370  }
371  }
372 
373  return nodesDrawn;
374 }
375 
376 int QgsPointCloudLayerRenderer::renderNodesSorted( const QVector<IndexedPointCloudNode> &nodes, QgsPointCloudIndex *pc, QgsPointCloudRenderContext &context, QgsPointCloudRequest &request, bool &canceled, QgsPointCloudRenderer::DrawOrder order )
377 {
378  int blockCount = 0;
379  int pointCount = 0;
380 
381  QgsVector3D blockScale;
382  QgsVector3D blockOffset;
383  QgsPointCloudAttributeCollection blockAttributes;
384  int recordSize = 0;
385 
386  // We'll collect byte array data from all blocks
387  QByteArray allByteArrays;
388  // And pairs of byte array start positions paired with their Z values for sorting
389  QVector<QPair<int, double>> allPairs;
390 
391  for ( const IndexedPointCloudNode &n : nodes )
392  {
393  if ( context.renderContext().renderingStopped() )
394  {
395  QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
396  canceled = true;
397  break;
398  }
399  std::unique_ptr<QgsPointCloudBlock> block( pc->nodeData( n, request ) );
400 
401  if ( !block )
402  continue;
403 
404  // Individual nodes may have different offset values than the root node
405  // we'll calculate the differences and translate x,y,z values to use the root node's offset
406  QgsVector3D offsetDifference = QgsVector3D( 0, 0, 0 );
407  if ( blockCount == 0 )
408  {
409  blockScale = block->scale();
410  blockOffset = block->offset();
411  blockAttributes = block->attributes();
412  }
413  else
414  {
415  offsetDifference = blockOffset - block->offset();
416  }
417 
418  const char *ptr = block->data();
419 
420  context.setScale( block->scale() );
421  context.setOffset( block->offset() );
422  context.setAttributes( block->attributes() );
423 
424  recordSize = context.pointRecordSize();
425 
426  for ( int i = 0; i < block->pointCount(); ++i )
427  {
428  allByteArrays.append( ptr + i * recordSize, recordSize );
429 
430  // Calculate the translated values only for axes that have a different offset
431  if ( offsetDifference.x() != 0 )
432  {
433  qint32 ix = *reinterpret_cast< const qint32 * >( ptr + i * recordSize + context.xOffset() );
434  ix -= std::lround( offsetDifference.x() / context.scale().x() );
435  const char *xPtr = reinterpret_cast< const char * >( &ix );
436  allByteArrays.replace( pointCount * recordSize + context.xOffset(), 4, QByteArray( xPtr, 4 ) );
437  }
438  if ( offsetDifference.y() != 0 )
439  {
440  qint32 iy = *reinterpret_cast< const qint32 * >( ptr + i * recordSize + context.yOffset() );
441  iy -= std::lround( offsetDifference.y() / context.scale().y() );
442  const char *yPtr = reinterpret_cast< const char * >( &iy );
443  allByteArrays.replace( pointCount * recordSize + context.yOffset(), 4, QByteArray( yPtr, 4 ) );
444  }
445  // We need the Z value regardless of the node's offset
446  qint32 iz = *reinterpret_cast< const qint32 * >( ptr + i * recordSize + context.zOffset() );
447  if ( offsetDifference.z() != 0 )
448  {
449  iz -= std::lround( offsetDifference.z() / context.scale().z() );
450  const char *zPtr = reinterpret_cast< const char * >( &iz );
451  allByteArrays.replace( pointCount * recordSize + context.zOffset(), 4, QByteArray( zPtr, 4 ) );
452  }
453  allPairs.append( qMakePair( pointCount, double( iz ) + block->offset().z() ) );
454 
455  ++pointCount;
456  }
457  ++blockCount;
458  }
459 
460  if ( pointCount == 0 )
461  return 0;
462 
463  switch ( order )
464  {
466  std::sort( allPairs.begin(), allPairs.end(), []( QPair<int, double> a, QPair<int, double> b ) { return a.second < b.second; } );
467  break;
469  std::sort( allPairs.begin(), allPairs.end(), []( QPair<int, double> a, QPair<int, double> b ) { return a.second > b.second; } );
470  break;
472  break;
473  }
474 
475  // Now we can reconstruct a byte array sorted by Z value
476  QByteArray sortedByteArray;
477  sortedByteArray.reserve( allPairs.size() );
478  for ( QPair<int, double> pair : allPairs )
479  sortedByteArray.append( allByteArrays.mid( pair.first * recordSize, recordSize ) );
480 
481  std::unique_ptr<QgsPointCloudBlock> bigBlock { new QgsPointCloudBlock( pointCount,
482  blockAttributes,
483  sortedByteArray,
484  blockScale,
485  blockOffset ) };
486 
487  QgsVector3D contextScale = context.scale();
488  QgsVector3D contextOffset = context.offset();
489 
490  context.setScale( bigBlock->scale() );
491  context.setOffset( bigBlock->offset() );
492  context.setAttributes( bigBlock->attributes() );
493 
494  mRenderer->renderBlock( bigBlock.get(), context );
495 
496  context.setScale( contextScale );
497  context.setOffset( contextOffset );
498 
499  return blockCount;
500 }
501 
503 {
504  // unless we are using the extent only renderer, point cloud layers should always be rasterized -- we don't want to export points as vectors
505  // to formats like PDF!
506  return mRenderer ? mRenderer->type() != QLatin1String( "extent" ) : false;
507 }
508 
510 {
511  mRenderTimeHint = time;
512 }
513 
514 QVector<IndexedPointCloudNode> QgsPointCloudLayerRenderer::traverseTree( const QgsPointCloudIndex *pc,
515  const QgsRenderContext &context,
517  double maxErrorPixels,
518  double nodeErrorPixels )
519 {
520  QVector<IndexedPointCloudNode> nodes;
521 
522  if ( context.renderingStopped() )
523  {
524  QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
525  return nodes;
526  }
527 
528  if ( !context.extent().intersects( pc->nodeMapExtent( n ) ) )
529  return nodes;
530 
531  const QgsDoubleRange nodeZRange = pc->nodeZRange( n );
532  const QgsDoubleRange adjustedNodeZRange = QgsDoubleRange( nodeZRange.lower() + mZOffset, nodeZRange.upper() + mZOffset );
533  if ( !context.zRange().isInfinite() && !context.zRange().overlaps( adjustedNodeZRange ) )
534  return nodes;
535 
536  nodes.append( n );
537 
538  double childrenErrorPixels = nodeErrorPixels / 2.0;
539  if ( childrenErrorPixels < maxErrorPixels )
540  return nodes;
541 
542  const QList<IndexedPointCloudNode> children = pc->nodeChildren( n );
543  for ( const IndexedPointCloudNode &nn : children )
544  {
545  nodes += traverseTree( pc, context, nn, maxErrorPixels, childrenErrorPixels );
546  }
547 
548  return nodes;
549 }
550 
Represents a indexed point cloud node in octree.
QString toString() const
Encode node to string.
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 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.
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.
QgsRectangle nodeMapExtent(const IndexedPointCloudNode &node) const
Returns the extent of a node in map coordinates.
virtual QList< IndexedPointCloudNode > nodeChildren(const IndexedPointCloudNode &n) const
Returns all children of node.
QgsVector3D offset() const
Returns offset.
QgsVector3D scale() const
Returns scale.
virtual QgsPointCloudBlock * nodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns node data block.
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 QgsPointCloudBlockRequest * asyncNodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns a handle responsible for loading a 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.
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.
QgsFeedback * feedback() const
Returns the feedback object used to cancel rendering.
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.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Sets the attributes associated with the rendered block.
DrawOrder
Pointcloud rendering order for 2d views /since QGIS 3.24.
@ 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.
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.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
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).
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.
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
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