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