QGIS API Documentation  3.23.0-Master (eb871beae0)
qgspointcloudlayerchunkloader_p.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspointcloudlayerchunkloader_p.cpp
3  --------------------------------------
4  Date : October 2020
5  Copyright : (C) 2020 by Peter Petrik
6  Email : zilolv dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
18 #include "qgs3dutils.h"
20 #include "qgschunknode_p.h"
21 #include "qgslogger.h"
22 #include "qgspointcloudlayer.h"
23 #include "qgspointcloudindex.h"
24 #include "qgseventtracing.h"
25 
26 #include "qgspoint3dsymbol.h"
28 
29 #include "qgspointcloud3dsymbol.h"
30 #include "qgsapplication.h"
31 #include "qgs3dsymbolregistry.h"
32 #include "qgspointcloudattribute.h"
33 #include "qgspointcloudrequest.h"
34 #include "qgscolorramptexture.h"
36 
37 #include <QtConcurrent>
38 #include <Qt3DRender/QAttribute>
39 #include <Qt3DRender/QTechnique>
40 #include <Qt3DRender/QShaderProgram>
41 #include <Qt3DRender/QGraphicsApiFilter>
42 #include <QPointSize>
43 
45 
46 
48 
49 QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol,
50  const QgsCoordinateTransform &coordinateTransform, double zValueScale, double zValueOffset )
51  : QgsChunkLoader( node )
52  , mFactory( factory )
53  , mContext( factory->mMap, coordinateTransform, std::move( symbol ), zValueScale, zValueOffset )
54 {
55 
56  QgsPointCloudIndex *pc = mFactory->mPointCloudIndex;
57  mContext.setAttributes( pc->attributes() );
58 
59  const QgsChunkNodeId nodeId = node->tileId();
60  const IndexedPointCloudNode pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
61 
62  Q_ASSERT( pc->hasNode( pcNode ) );
63 
64  QgsDebugMsgLevel( QStringLiteral( "loading entity %1" ).arg( node->tileId().text() ), 2 );
65 
66  if ( mContext.symbol()->symbolType() == QLatin1String( "single-color" ) )
67  mHandler.reset( new QgsSingleColorPointCloud3DSymbolHandler() );
68  else if ( mContext.symbol()->symbolType() == QLatin1String( "color-ramp" ) )
69  mHandler.reset( new QgsColorRampPointCloud3DSymbolHandler() );
70  else if ( mContext.symbol()->symbolType() == QLatin1String( "rgb" ) )
71  mHandler.reset( new QgsRGBPointCloud3DSymbolHandler() );
72  else if ( mContext.symbol()->symbolType() == QLatin1String( "classification" ) )
73  {
74  mHandler.reset( new QgsClassificationPointCloud3DSymbolHandler() );
75  const QgsClassificationPointCloud3DSymbol *classificationSymbol = dynamic_cast<const QgsClassificationPointCloud3DSymbol *>( mContext.symbol() );
76  mContext.setFilteredOutCategories( classificationSymbol->getFilteredOutCategories() );
77  }
78 
79  //
80  // this will be run in a background thread
81  //
82  mFutureWatcher = new QFutureWatcher<void>( this );
83  connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
84 
85  const QFuture<void> future = QtConcurrent::run( [pc, pcNode, this]
86  {
87  const QgsEventTracing::ScopedEvent e( QStringLiteral( "3D" ), QStringLiteral( "PC chunk load" ) );
88 
89  if ( mContext.isCanceled() )
90  {
91  QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
92  return;
93  }
94  mHandler->processNode( pc, pcNode, mContext );
95  } );
96 
97  // emit finished() as soon as the handler is populated with features
98  mFutureWatcher->setFuture( future );
99 }
100 
101 QgsPointCloudLayerChunkLoader::~QgsPointCloudLayerChunkLoader()
102 {
103  if ( mFutureWatcher && !mFutureWatcher->isFinished() )
104  {
105  disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
106  mContext.cancelRendering();
107  mFutureWatcher->waitForFinished();
108  }
109 }
110 
111 void QgsPointCloudLayerChunkLoader::cancel()
112 {
113  mContext.cancelRendering();
114 }
115 
116 Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
117 {
118  QgsPointCloudIndex *pc = mFactory->mPointCloudIndex;
119  const QgsChunkNodeId nodeId = mNode->tileId();
120  const IndexedPointCloudNode pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
121  Q_ASSERT( pc->hasNode( pcNode ) );
122 
123  Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
124  mHandler->finalize( entity, mContext );
125  return entity;
126 }
127 
129 
130 
131 QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, QgsPointCloudIndex *pc, QgsPointCloud3DSymbol *symbol,
132  double zValueScale, double zValueOffset, int pointBudget )
133  : mMap( map )
134  , mCoordinateTransform( coordinateTransform )
135  , mPointCloudIndex( pc )
136  , mZValueScale( zValueScale )
137  , mZValueOffset( zValueOffset )
138  , mPointBudget( pointBudget )
139 {
140  mSymbol.reset( symbol );
141 }
142 
143 QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
144 {
145  const QgsChunkNodeId id = node->tileId();
146 
147  Q_ASSERT( mPointCloudIndex->hasNode( IndexedPointCloudNode( id.d, id.x, id.y, id.z ) ) );
148  QgsPointCloud3DSymbol *symbol = static_cast< QgsPointCloud3DSymbol * >( mSymbol->clone() );
149  return new QgsPointCloudLayerChunkLoader( this, node, std::unique_ptr< QgsPointCloud3DSymbol >( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
150 }
151 
152 int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node ) const
153 {
154  const QgsChunkNodeId id = node->tileId();
155  const IndexedPointCloudNode n( id.d, id.x, id.y, id.z );
156  Q_ASSERT( mPointCloudIndex->hasNode( n ) );
157  return mPointCloudIndex->nodePointCount( n );
158 }
159 
160 QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset, QgsVector3D scale, const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, double zValueOffset );
161 
162 QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode() const
163 {
164  const QgsAABB bbox = nodeBoundsToAABB( mPointCloudIndex->nodeBounds( IndexedPointCloudNode( 0, 0, 0, 0 ) ), mPointCloudIndex->offset(), mPointCloudIndex->scale(), mMap, mCoordinateTransform, mZValueOffset );
165  const float error = mPointCloudIndex->nodeError( IndexedPointCloudNode( 0, 0, 0, 0 ) );
166  return new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), bbox, error );
167 }
168 
169 QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node ) const
170 {
171  QVector<QgsChunkNode *> children;
172  const QgsChunkNodeId nodeId = node->tileId();
173  const QgsAABB bbox = node->bbox();
174  const float childError = node->error() / 2;
175  float xc = bbox.xCenter(), yc = bbox.yCenter(), zc = bbox.zCenter();
176 
177  for ( int i = 0; i < 8; ++i )
178  {
179  int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
180  const QgsChunkNodeId childId( nodeId.d + 1, nodeId.x * 2 + dx, nodeId.y * 2 + dy, nodeId.z * 2 + dz );
181 
182  if ( !mPointCloudIndex->hasNode( IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ) )
183  continue;
184 
185  // the Y and Z coordinates below are intentionally flipped, because
186  // in chunk node IDs the X,Y axes define horizontal plane,
187  // while in our 3D scene the X,Z axes define the horizontal plane
188  const float chXMin = dx ? xc : bbox.xMin;
189  const float chXMax = dx ? bbox.xMax : xc;
190  // Z axis: values are increasing to the south
191  const float chZMin = !dy ? zc : bbox.zMin;
192  const float chZMax = !dy ? bbox.zMax : zc;
193  const float chYMin = dz ? yc : bbox.yMin;
194  const float chYMax = dz ? bbox.yMax : yc;
195  children << new QgsChunkNode( childId, QgsAABB( chXMin, chYMin, chZMin, chXMax, chYMax, chZMax ), childError, node );
196  }
197  return children;
198 }
199 
201 
202 
203 QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset, QgsVector3D scale, const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, double zValueOffset )
204 {
205  QgsVector3D extentMin3D( nodeBounds.xMin() * scale.x() + offset.x(), nodeBounds.yMin() * scale.y() + offset.y(), nodeBounds.zMin() * scale.z() + offset.z() + zValueOffset );
206  QgsVector3D extentMax3D( nodeBounds.xMax() * scale.x() + offset.x(), nodeBounds.yMax() * scale.y() + offset.y(), nodeBounds.zMax() * scale.z() + offset.z() + zValueOffset );
207  try
208  {
209  extentMin3D = coordinateTransform.transform( extentMin3D );
210  extentMax3D = coordinateTransform.transform( extentMax3D );
211  }
212  catch ( QgsCsException & )
213  {
214  QgsDebugMsg( QStringLiteral( "Error transforming node bounds coordinate" ) );
215  }
216  const QgsVector3D worldExtentMin3D = Qgs3DUtils::mapToWorldCoordinates( extentMin3D, map.origin() );
217  const QgsVector3D worldExtentMax3D = Qgs3DUtils::mapToWorldCoordinates( extentMax3D, map.origin() );
218  QgsAABB rootBbox( worldExtentMin3D.x(), worldExtentMin3D.y(), worldExtentMin3D.z(),
219  worldExtentMax3D.x(), worldExtentMax3D.y(), worldExtentMax3D.z() );
220  return rootBbox;
221 }
222 
223 
224 QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity( QgsPointCloudIndex *pc, const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, QgsPointCloud3DSymbol *symbol, float maximumScreenSpaceError, bool showBoundingBoxes, double zValueScale, double zValueOffset, int pointBudget )
225  : QgsChunkedEntity( maximumScreenSpaceError,
226  new QgsPointCloudLayerChunkLoaderFactory( map, coordinateTransform, pc, symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
227 {
228  setUsingAdditiveStrategy( true );
229  setShowBoundingBoxes( showBoundingBoxes );
230 }
231 
232 QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
233 {
234  // cancel / wait for jobs
235  cancelActiveJobs();
236 }
237 
Represents a indexed point cloud node in octree.
QgsVector3D origin() const
Returns coordinates in map CRS at which 3D scene has origin (0,0,0)
static QgsVector3D mapToWorldCoordinates(const QgsVector3D &mapCoords, const QgsVector3D &origin)
Converts map coordinates to 3D world coordinates (applies offset and turns (x,y,z) into (x,...
Definition: qgs3dutils.cpp:434
3
Definition: qgsaabb.h:34
float yMax
Definition: qgsaabb.h:85
float xMax
Definition: qgsaabb.h:84
float xCenter() const
Returns center in X axis.
Definition: qgsaabb.h:50
float xMin
Definition: qgsaabb.h:81
float zMax
Definition: qgsaabb.h:86
float yMin
Definition: qgsaabb.h:82
float yCenter() const
Returns center in Y axis.
Definition: qgsaabb.h:52
float zMin
Definition: qgsaabb.h:83
float zCenter() const
Returns center in Z axis.
Definition: qgsaabb.h:54
QgsPointCloudCategoryList getFilteredOutCategories() const
Gets the list of categories of the classification that should not be rendered.
Class for doing transforms between two map coordinate systems.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Represents packaged data bounds.
qint32 xMax() const
Returns x max.
qint32 xMin() const
Returns x min.
qint32 yMax() const
Returns y max.
qint32 zMax() const
Returns z max.
qint32 yMin() const
Returns y min.
qint32 zMin() const
Returns z min.
Represents a indexed point clouds data in octree.
virtual bool hasNode(const IndexedPointCloudNode &n) const
Returns whether the octree contain given node.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Sets native attributes of the data.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
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