QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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 "qgspointcloudindex.h"
23#include "qgseventtracing.h"
24
25
29
30#include <QtConcurrent>
31#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
32#include <Qt3DRender/QAttribute>
33#else
34#include <Qt3DCore/QAttribute>
35#endif
36#include <Qt3DRender/QTechnique>
37#include <Qt3DRender/QShaderProgram>
38#include <Qt3DRender/QGraphicsApiFilter>
39#include <QPointSize>
40
42
43
45
46QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr< QgsPointCloud3DSymbol > symbol,
47 const QgsCoordinateTransform &coordinateTransform, double zValueScale, double zValueOffset )
48 : QgsChunkLoader( node )
49 , mFactory( factory )
50 , mContext( factory->mMap, coordinateTransform, std::move( symbol ), zValueScale, zValueOffset )
51{
52
53 QgsPointCloudIndex *pc = mFactory->mPointCloudIndex;
54 mContext.setAttributes( pc->attributes() );
55
56 const QgsChunkNodeId nodeId = node->tileId();
57 const IndexedPointCloudNode pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
58
59 Q_ASSERT( pc->hasNode( pcNode ) );
60
61 QgsDebugMsgLevel( QStringLiteral( "loading entity %1" ).arg( node->tileId().text() ), 2 );
62
63 if ( mContext.symbol()->symbolType() == QLatin1String( "single-color" ) )
64 mHandler.reset( new QgsSingleColorPointCloud3DSymbolHandler() );
65 else if ( mContext.symbol()->symbolType() == QLatin1String( "color-ramp" ) )
66 mHandler.reset( new QgsColorRampPointCloud3DSymbolHandler() );
67 else if ( mContext.symbol()->symbolType() == QLatin1String( "rgb" ) )
68 mHandler.reset( new QgsRGBPointCloud3DSymbolHandler() );
69 else if ( mContext.symbol()->symbolType() == QLatin1String( "classification" ) )
70 {
71 mHandler.reset( new QgsClassificationPointCloud3DSymbolHandler() );
72 const QgsClassificationPointCloud3DSymbol *classificationSymbol = dynamic_cast<const QgsClassificationPointCloud3DSymbol *>( mContext.symbol() );
73 mContext.setFilteredOutCategories( classificationSymbol->getFilteredOutCategories() );
74 }
75
76 //
77 // this will be run in a background thread
78 //
79 mFutureWatcher = new QFutureWatcher<void>( this );
80 connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
81
82 const QgsAABB bbox = node->bbox();
83 const QFuture<void> future = QtConcurrent::run( [pc, pcNode, bbox, this]
84 {
85 const QgsEventTracing::ScopedEvent e( QStringLiteral( "3D" ), QStringLiteral( "PC chunk load" ) );
86
87 if ( mContext.isCanceled() )
88 {
89 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
90 return;
91 }
92
93 mHandler->processNode( pc, pcNode, mContext );
94 if ( mContext.symbol()->renderAsTriangles() )
95 mHandler->triangulate( pc, pcNode, mContext, bbox );
96 } );
97
98 // emit finished() as soon as the handler is populated with features
99 mFutureWatcher->setFuture( future );
100}
101
102QgsPointCloudLayerChunkLoader::~QgsPointCloudLayerChunkLoader()
103{
104 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
105 {
106 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
107 mContext.cancelRendering();
108 mFutureWatcher->waitForFinished();
109 }
110}
111
112void QgsPointCloudLayerChunkLoader::cancel()
113{
114 mContext.cancelRendering();
115}
116
117Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
118{
119 QgsPointCloudIndex *pc = mFactory->mPointCloudIndex;
120 const QgsChunkNodeId nodeId = mNode->tileId();
121 const IndexedPointCloudNode pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
122 Q_ASSERT( pc->hasNode( pcNode ) );
123
124 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
125 mHandler->finalize( entity, mContext );
126 return entity;
127}
128
130
131
132QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory( const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, QgsPointCloudIndex *pc, QgsPointCloud3DSymbol *symbol,
133 double zValueScale, double zValueOffset, int pointBudget )
134 : mMap( map )
135 , mCoordinateTransform( coordinateTransform )
136 , mPointCloudIndex( pc )
137 , mZValueScale( zValueScale )
138 , mZValueOffset( zValueOffset )
139 , mPointBudget( pointBudget )
140{
141 mSymbol.reset( symbol );
142}
143
144QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
145{
146 const QgsChunkNodeId id = node->tileId();
147
148 Q_ASSERT( mPointCloudIndex->hasNode( IndexedPointCloudNode( id.d, id.x, id.y, id.z ) ) );
149 QgsPointCloud3DSymbol *symbol = static_cast< QgsPointCloud3DSymbol * >( mSymbol->clone() );
150 return new QgsPointCloudLayerChunkLoader( this, node, std::unique_ptr< QgsPointCloud3DSymbol >( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
151}
152
153int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node ) const
154{
155 const QgsChunkNodeId id = node->tileId();
156 const IndexedPointCloudNode n( id.d, id.x, id.y, id.z );
157 Q_ASSERT( mPointCloudIndex->hasNode( n ) );
158 return mPointCloudIndex->nodePointCount( n );
159}
160
161QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset, QgsVector3D scale, const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, double zValueOffset );
162
163QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode() const
164{
165 const QgsAABB bbox = nodeBoundsToAABB( mPointCloudIndex->nodeBounds( IndexedPointCloudNode( 0, 0, 0, 0 ) ), mPointCloudIndex->offset(), mPointCloudIndex->scale(), mMap, mCoordinateTransform, mZValueOffset );
166 const float error = mPointCloudIndex->nodeError( IndexedPointCloudNode( 0, 0, 0, 0 ) );
167 return new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), bbox, error );
168}
169
170QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node ) const
171{
172 QVector<QgsChunkNode *> children;
173 const QgsChunkNodeId nodeId = node->tileId();
174 const QgsAABB bbox = node->bbox();
175 const float childError = node->error() / 2;
176 float xc = bbox.xCenter(), yc = bbox.yCenter(), zc = bbox.zCenter();
177
178 for ( int i = 0; i < 8; ++i )
179 {
180 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
181 const QgsChunkNodeId childId( nodeId.d + 1, nodeId.x * 2 + dx, nodeId.y * 2 + dy, nodeId.z * 2 + dz );
182
183 if ( !mPointCloudIndex->hasNode( IndexedPointCloudNode( childId.d, childId.x, childId.y, childId.z ) ) )
184 continue;
185
186 // the Y and Z coordinates below are intentionally flipped, because
187 // in chunk node IDs the X,Y axes define horizontal plane,
188 // while in our 3D scene the X,Z axes define the horizontal plane
189 const float chXMin = dx ? xc : bbox.xMin;
190 const float chXMax = dx ? bbox.xMax : xc;
191 // Z axis: values are increasing to the south
192 const float chZMin = !dy ? zc : bbox.zMin;
193 const float chZMax = !dy ? bbox.zMax : zc;
194 const float chYMin = dz ? yc : bbox.yMin;
195 const float chYMax = dz ? bbox.yMax : yc;
196 children << new QgsChunkNode( childId, QgsAABB( chXMin, chYMin, chZMin, chXMax, chYMax, chZMax ), childError, node );
197 }
198 return children;
199}
200
202
203
204QgsAABB nodeBoundsToAABB( QgsPointCloudDataBounds nodeBounds, QgsVector3D offset, QgsVector3D scale, const Qgs3DMapSettings &map, const QgsCoordinateTransform &coordinateTransform, double zValueOffset )
205{
206 QgsVector3D extentMin3D( nodeBounds.xMin() * scale.x() + offset.x(), nodeBounds.yMin() * scale.y() + offset.y(), nodeBounds.zMin() * scale.z() + offset.z() + zValueOffset );
207 QgsVector3D extentMax3D( nodeBounds.xMax() * scale.x() + offset.x(), nodeBounds.yMax() * scale.y() + offset.y(), nodeBounds.zMax() * scale.z() + offset.z() + zValueOffset );
208 QgsCoordinateTransform extentTransform = coordinateTransform;
209 extentTransform.setBallparkTransformsAreAppropriate( true );
210 try
211 {
212 extentMin3D = extentTransform.transform( extentMin3D );
213 extentMax3D = extentTransform.transform( extentMax3D );
214 }
215 catch ( QgsCsException & )
216 {
217 QgsDebugMsg( QStringLiteral( "Error transforming node bounds coordinate" ) );
218 }
219 const QgsVector3D worldExtentMin3D = Qgs3DUtils::mapToWorldCoordinates( extentMin3D, map.origin() );
220 const QgsVector3D worldExtentMax3D = Qgs3DUtils::mapToWorldCoordinates( extentMax3D, map.origin() );
221 QgsAABB rootBbox( worldExtentMin3D.x(), worldExtentMin3D.y(), worldExtentMin3D.z(),
222 worldExtentMax3D.x(), worldExtentMax3D.y(), worldExtentMax3D.z() );
223 return rootBbox;
224}
225
226
227QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity( QgsPointCloudIndex *pc, const Qgs3DMapSettings &map,
228 const QgsCoordinateTransform &coordinateTransform, QgsPointCloud3DSymbol *symbol,
229 float maximumScreenSpaceError, bool showBoundingBoxes,
230 double zValueScale, double zValueOffset,
231 int pointBudget )
232 : QgsChunkedEntity( maximumScreenSpaceError,
233 new QgsPointCloudLayerChunkLoaderFactory( map, coordinateTransform, pc, symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
234{
235 setUsingAdditiveStrategy( !symbol->renderAsTriangles() );
236 setShowBoundingBoxes( showBoundingBoxes );
237}
238
239QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
240{
241 // cancel / wait for jobs
242 cancelActiveJobs();
243}
244
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:540
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.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
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
bool renderAsTriangles() const
Returns whether points are triangulated to render solid surface.
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