QGIS API Documentation 3.41.0-Master (d2aaa9c6e02)
Loading...
Searching...
No Matches
qgspointclouddataprovider.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointclouddataprovider.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 "qgis.h"
20#include "moc_qgspointclouddataprovider.cpp"
21#include "qgspointcloudindex.h"
22#include "qgsgeometry.h"
24#include "qgsgeos.h"
26#include "qgsthreadingutils.h"
28
29#include <mutex>
30#include <QDebug>
31#include <QtMath>
32
33#include <QtConcurrent/QtConcurrentMap>
34
36 const QString &uri,
39 : QgsDataProvider( uri, options, flags )
40{
41}
42
44
51
53{
55
56 QgsPointCloudIndex lIndex = index();
57 return lIndex.isValid();
58}
59
66
68{
70
71 return QVariantMap();
72}
73
75{
77
78 return nullptr;
79}
80
82{
83 static QMap< int, QString > sCodes
84 {
85 {0, QStringLiteral( "Created, Never Classified" )},
86 {1, QStringLiteral( "Unclassified" )},
87 {2, QStringLiteral( "Ground" )},
88 {3, QStringLiteral( "Low Vegetation" )},
89 {4, QStringLiteral( "Medium Vegetation" )},
90 {5, QStringLiteral( "High Vegetation" )},
91 {6, QStringLiteral( "Building" )},
92 {7, QStringLiteral( "Low Point (Low Noise)" )},
93 {8, QStringLiteral( "Reserved" )},
94 {9, QStringLiteral( "Water" )},
95 {10, QStringLiteral( "Rail" )},
96 {11, QStringLiteral( "Road Surface" )},
97 {12, QStringLiteral( "Reserved" )},
98 {13, QStringLiteral( "Wire - Guard (Shield)" )},
99 {14, QStringLiteral( "Wire - Conductor (Phase)" )},
100 {15, QStringLiteral( "Transmission Tower" )},
101 {16, QStringLiteral( "Wire-Structure Connector (Insulator)" )},
102 {17, QStringLiteral( "Bridge Deck" )},
103 {18, QStringLiteral( "High Noise" )},
104 };
105
106 static std::once_flag initialized;
107 std::call_once( initialized, [ = ]( )
108 {
109 for ( int i = 19; i <= 63; ++i )
110 sCodes.insert( i, QStringLiteral( "Reserved" ) );
111 for ( int i = 64; i <= 255; ++i )
112 sCodes.insert( i, QStringLiteral( "User Definable" ) );
113 } );
114
115 return sCodes;
116}
117
119{
120 static QMap< int, QString > sCodes
121 {
122 {0, QObject::tr( "Created, Never Classified" )},
123 {1, QObject::tr( "Unclassified" )},
124 {2, QObject::tr( "Ground" )},
125 {3, QObject::tr( "Low Vegetation" )},
126 {4, QObject::tr( "Medium Vegetation" )},
127 {5, QObject::tr( "High Vegetation" )},
128 {6, QObject::tr( "Building" )},
129 {7, QObject::tr( "Low Point (Noise)" )},
130 {8, QObject::tr( "Reserved" )},
131 {9, QObject::tr( "Water" )},
132 {10, QObject::tr( "Rail" )},
133 {11, QObject::tr( "Road Surface" )},
134 {12, QObject::tr( "Reserved" )},
135 {13, QObject::tr( "Wire - Guard (Shield)" )},
136 {14, QObject::tr( "Wire - Conductor (Phase)" )},
137 {15, QObject::tr( "Transmission Tower" )},
138 {16, QObject::tr( "Wire-Structure Connector (Insulator)" )},
139 {17, QObject::tr( "Bridge Deck" )},
140 {18, QObject::tr( "High Noise" )},
141 };
142
143 static std::once_flag initialized;
144 std::call_once( initialized, [ = ]( )
145 {
146 for ( int i = 19; i <= 63; ++i )
147 sCodes.insert( i, QObject::tr( "Reserved" ) );
148 for ( int i = 64; i <= 255; ++i )
149 sCodes.insert( i, QObject::tr( "User Definable" ) );
150 } );
151
152 return sCodes;
153}
154
156{
157 static const QMap< int, QString > sCodes
158 {
159 {0, QStringLiteral( "No color or time stored" )},
160 {1, QStringLiteral( "Time is stored" )},
161 {2, QStringLiteral( "Color is stored" )},
162 {3, QStringLiteral( "Color and time are stored" )},
163 {6, QStringLiteral( "Time is stored" )},
164 {7, QStringLiteral( "Time and color are stored)" )},
165 {8, QStringLiteral( "Time, color and near infrared are stored" )},
166 };
167
168 return sCodes;
169}
170
172{
173 static const QMap< int, QString > sCodes
174 {
175 {0, QObject::tr( "No color or time stored" )},
176 {1, QObject::tr( "Time is stored" )},
177 {2, QObject::tr( "Color is stored" )},
178 {3, QObject::tr( "Color and time are stored" )},
179 {6, QObject::tr( "Time is stored" )},
180 {7, QObject::tr( "Time and color are stored)" )},
181 {8, QObject::tr( "Time, color and near infrared are stored" )},
182 };
183
184 return sCodes;
185}
186
188{
190
191 QgsPointCloudIndex pcIndex = index();
192 if ( pcIndex )
193 {
194 return pcIndex.metadataStatistics();
195 }
197}
198
200{
201 return true;
202}
203
205{
206 return tr( "QGIS expression" );
207}
208
210{
211 // unfortunately we can't access QgsHelp here, that's a GUI class!
212 return QString();
213}
214
216{
217 typedef QVector<QMap<QString, QVariant>> result_type;
218
219 MapIndexedPointCloudNode( QgsPointCloudRequest &request, const QgsVector3D &indexScale, const QgsVector3D &indexOffset,
220 const QgsGeometry &extentGeometry, const QgsDoubleRange &zRange, QgsPointCloudIndex index, int pointsLimit )
221 : mRequest( request ), mIndexScale( indexScale ), mIndexOffset( indexOffset ), mExtentGeometry( extentGeometry ), mZRange( zRange ), mIndex( index ), mPointsLimit( pointsLimit )
222 { }
223
224 QVector<QVariantMap> operator()( QgsPointCloudNodeId n )
225 {
226 QVector<QVariantMap> acceptedPoints;
227 std::unique_ptr<QgsPointCloudBlock> block( mIndex.nodeData( n, mRequest ) );
228
229 if ( !block || pointsCount == mPointsLimit )
230 return acceptedPoints;
231
232 const char *ptr = block->data();
233 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
234 const std::size_t recordSize = blockAttributes.pointRecordSize();
235 int xOffset = 0, yOffset = 0, zOffset = 0;
236 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( QStringLiteral( "X" ), xOffset )->type();
237 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( QStringLiteral( "Y" ), yOffset )->type();
238 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( QStringLiteral( "Z" ), zOffset )->type();
239 std::unique_ptr< QgsGeos > extentEngine = std::make_unique< QgsGeos >( mExtentGeometry.constGet() );
240 extentEngine->prepareGeometry();
241
242 std::optional<bool> copcTimeFlag = std::nullopt;
243 QVariantMap extraMetadata = mIndex.extraMetadata();
244 if ( extraMetadata.contains( QStringLiteral( "CopcGpsTimeFlag" ) ) )
245 copcTimeFlag = extraMetadata[ QStringLiteral( "CopcGpsTimeFlag" ) ].toBool();
246
247 for ( int i = 0; i < block->pointCount() && pointsCount < mPointsLimit; ++i )
248 {
249 double x, y, z;
250 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, block->scale(), block->offset(), x, y, z );
251
252 if ( mZRange.contains( z ) && extentEngine->contains( x, y ) )
253 {
254 QVariantMap pointAttr = QgsPointCloudAttribute::getAttributeMap( ptr, i * recordSize, blockAttributes );
255 pointAttr[ QStringLiteral( "X" ) ] = x;
256 pointAttr[ QStringLiteral( "Y" ) ] = y;
257 pointAttr[ QStringLiteral( "Z" ) ] = z;
258
259
260 if ( copcTimeFlag.has_value() )
261 {
262 const QDateTime gpsBaseTime = QDateTime::fromSecsSinceEpoch( 315964809, Qt::UTC );
263 constexpr int numberOfSecsInWeek = 3600 * 24 * 7;
264 // here we check the flag set in header to determine if we need to
265 // parse the time as GPS week time or GPS adjusted standard time
266 // however often times the flag is set wrong, so we determine if the value is bigger than the maximum amount of seconds in week then it has to be adjusted standard time
267 if ( copcTimeFlag.value() || pointAttr[QStringLiteral( "GpsTime" )].toDouble() > numberOfSecsInWeek )
268 {
269 const QString utcTime = gpsBaseTime.addSecs( static_cast<qint64>( pointAttr[QStringLiteral( "GpsTime" )].toDouble() + 1e9 ) ).toString( Qt::ISODate );
270 pointAttr[ QStringLiteral( "GpsTime (raw)" )] = pointAttr[QStringLiteral( "GpsTime" )];
271 pointAttr[ QStringLiteral( "GpsTime" )] = utcTime;
272 }
273 else
274 {
275 const QString weekTime = gpsBaseTime.addSecs( pointAttr[QStringLiteral( "GpsTime" )].toLongLong() ).toString( "ddd hh:mm:ss" );
276 pointAttr[ QStringLiteral( "GpsTime (raw)" )] = pointAttr[QStringLiteral( "GpsTime" )];
277 pointAttr[ QStringLiteral( "GpsTime" )] = weekTime;
278 }
279 }
280 pointsCount++;
281 acceptedPoints.push_back( pointAttr );
282 }
283 }
284 return acceptedPoints;
285 }
286
294 int pointsCount = 0;
295};
296
298 double maxError,
299 const QgsGeometry &extentGeometry,
300 const QgsDoubleRange &extentZRange, int pointsLimit )
301{
302 QVector<QVariantMap> acceptedPoints;
303
304 // Try sub-indexes first
305 for ( QgsPointCloudSubIndex &subidx : subIndexes() )
306 {
307 // Check if the sub-index is relevant and if it is loaded. We shouldn't
308 // need to identify points in unloaded indices.
309 QgsPointCloudIndex index = subidx.index();
310 if ( !index
311 || ( !subidx.zRange().overlaps( extentZRange ) )
312 || !subidx.polygonBounds().intersects( extentGeometry ) )
313 continue;
314 acceptedPoints.append( identify( index, maxError, extentGeometry, extentZRange, pointsLimit ) );
315 }
316
317 // Then look at main index
318 QgsPointCloudIndex mainIndex = index();
319 acceptedPoints.append( identify( mainIndex, maxError, extentGeometry, extentZRange, pointsLimit ) );
320
321 return acceptedPoints;
322}
323
325 QgsPointCloudIndex &index, double maxError,
326 const QgsGeometry &extentGeometry,
327 const QgsDoubleRange &extentZRange, int pointsLimit )
328{
330
331 QVector<QVariantMap> acceptedPoints;
332
333 if ( !index.isValid() )
334 return acceptedPoints;
335
336 const QgsPointCloudNode root = index.getNode( index.root() );
337 const QVector<QgsPointCloudNodeId> nodes = traverseTree( index, root, maxError, root.error(), extentGeometry, extentZRange );
338
339 const QgsPointCloudAttributeCollection attributeCollection = index.attributes();
340 QgsPointCloudRequest request;
341 request.setAttributes( attributeCollection );
342
343 acceptedPoints = QtConcurrent::blockingMappedReduced( nodes,
344 MapIndexedPointCloudNode( request, index.scale(), index.offset(), extentGeometry, extentZRange, index, pointsLimit ),
345 qOverload<const QVector<QMap<QString, QVariant>>&>( &QVector<QMap<QString, QVariant>>::append ),
346 QtConcurrent::UnorderedReduce );
347
348 return acceptedPoints;
349}
350
351QVector<QgsPointCloudNodeId> QgsPointCloudDataProvider::traverseTree(
352 const QgsPointCloudIndex &pc,
354 double maxError,
355 double nodeError,
356 const QgsGeometry &extentGeometry,
357 const QgsDoubleRange &extentZRange )
358{
360
361 QVector<QgsPointCloudNodeId> nodes;
362
363 const QgsBox3D nodeBounds = node.bounds();
364 const QgsDoubleRange nodeZRange( nodeBounds.zMinimum(), nodeBounds.zMaximum() );
365 if ( !extentZRange.overlaps( nodeZRange ) )
366 return nodes;
367
368 if ( !extentGeometry.intersects( nodeBounds.toRectangle() ) )
369 return nodes;
370
371 nodes.append( node.id() );
372
373 const double childrenError = nodeError / 2.0;
374 if ( childrenError < maxError )
375 return nodes;
376
377 for ( const QgsPointCloudNodeId &nn : node.children() )
378 {
379 const QgsPointCloudNode childNode = pc.getNode( nn );
380 if ( extentGeometry.intersects( childNode.bounds().toRectangle() ) )
381 nodes += traverseTree( pc, childNode, maxError, childrenError, extentGeometry, extentZRange );
382 }
383
384 return nodes;
385}
386
387bool QgsPointCloudDataProvider::setSubsetString( const QString &subset, bool updateFeatureCount )
388{
390
391 Q_UNUSED( updateFeatureCount )
393 if ( !i )
394 return false;
395
396 if ( !i.setSubsetString( subset ) )
397 return false;
398 mSubsetString = subset;
399 emit dataChanged();
400 return true;
401}
402
409
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:450
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:43
double zMaximum() const
Returns the maximum z value.
Definition qgsbox3d.h:274
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:394
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:267
Abstract base class for spatial data provider implementations.
void dataChanged()
Emitted whenever a change is made to the data provider which may have caused changes in the provider'...
virtual QgsRectangle extent() const =0
Returns the extent of the layer.
QgsRange which stores a range of double values.
Definition qgsrange.h:237
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
Collection of point cloud attributes.
int pointRecordSize() const
Returns total size of record.
const QgsPointCloudAttribute * find(const QString &attributeName, int &offset) const
Finds the attribute with the name.
QVector< QgsPointCloudAttribute > attributes() const
Returns all attributes.
DataType
Systems of unit measurement.
static void getPointXYZ(const char *ptr, int i, std::size_t pointRecordSize, int xOffset, QgsPointCloudAttribute::DataType xType, int yOffset, QgsPointCloudAttribute::DataType yType, int zOffset, QgsPointCloudAttribute::DataType zType, const QgsVector3D &indexScale, const QgsVector3D &indexOffset, double &x, double &y, double &z)
Retrieves the x, y, z values for the point at index i.
static QVariantMap getAttributeMap(const char *data, std::size_t recordOffset, const QgsPointCloudAttributeCollection &attributeCollection)
Retrieves all the attributes of a point.
DataType type() const
Returns the data type.
QString subsetStringDialect() const override
Returns a user-friendly string describing the dialect which is supported for subset strings by the pr...
@ NoCapabilities
Provider has no capabilities.
bool setSubsetString(const QString &subset, bool updateFeatureCount=false) override
Set the subset string used to create a subset of features in the layer.
~QgsPointCloudDataProvider() override
QgsPointCloudDataProvider(const QString &uri, const QgsDataProvider::ProviderOptions &providerOptions, Qgis::DataProviderReadFlags flags=Qgis::DataProviderReadFlags())
Ctor.
static QMap< int, QString > dataFormatIds()
Returns the map of LAS data format ID to untranslated string value.
bool supportsSubsetString() const override
Returns true if the provider supports setting of subset strings.
virtual QVector< QgsPointCloudSubIndex > subIndexes()
Returns a list of sub indexes available if the provider supports multiple indexes,...
QVector< QVariantMap > identify(double maxError, const QgsGeometry &extentGeometry, const QgsDoubleRange &extentZRange=QgsDoubleRange(), int pointsLimit=1000)
Returns the list of points of the point cloud according to a zoom level defined by maxError (in layer...
QgsPointCloudStatistics metadataStatistics()
Returns the object containing the statistics metadata extracted from the dataset.
virtual QgsPointCloudIndex index() const
Returns the point cloud index associated with the provider.
QString subsetStringHelpUrl() const override
Returns a URL pointing to documentation describing the dialect which is supported for subset strings ...
QString subsetString() const override
Returns the subset definition string currently in use by the layer and used by the provider to limit ...
static QMap< int, QString > translatedDataFormatIds()
Returns the map of LAS data format ID to translated string value.
virtual QgsPointCloudDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities for the data provider.
static QMap< int, QString > translatedLasClassificationCodes()
Returns the map of LAS classification code to translated string value, corresponding to the ASPRS Sta...
QString mSubsetString
String used to define a subset of the layer.
static QMap< int, QString > lasClassificationCodes()
Returns the map of LAS classification code to untranslated string value, corresponding to the ASPRS S...
virtual QgsPointCloudRenderer * createRenderer(const QVariantMap &configuration=QVariantMap()) const
Creates a new 2D point cloud renderer, using provider backend specific information.
bool hasValidIndex() const
Returns whether provider has index which is valid.
virtual QVariantMap originalMetadata() const
Returns a representation of the original metadata included in a point cloud dataset.
virtual QgsGeometry polygonBounds() const
Returns the polygon bounds of the layer.
Smart pointer for QgsAbstractPointCloudIndex.
QgsVector3D offset() const
Returns offset of data from CRS.
QgsVector3D scale() const
Returns scale of data relative to CRS.
bool isValid() const
Returns whether index is loaded and valid.
std::unique_ptr< QgsPointCloudBlock > nodeData(const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request)
Returns node data block.
QVariantMap extraMetadata() const
Returns extra metadata that's not accessible through the other methods in an implementation-specific ...
QgsPointCloudNodeId root() const
Returns root node of the index.
QgsPointCloudNode getNode(const QgsPointCloudNodeId &id) const
Returns object for a given node.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
Represents a indexed point cloud node's position in octree.
Keeps metadata for indexed point cloud node.
QgsPointCloudNodeId id() const
Returns node's ID (unique in index)
float error() const
Returns node's error in map units (used to determine in whether the node has enough detail for the cu...
QgsBox3D bounds() const
Returns node's bounding cube in CRS coords.
Abstract base class for 2d point cloud renderers.
Point cloud data request.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.
Class used to store statistics of a point cloud dataset.
bool overlaps(const QgsRange< T > &other) const
Returns true if this range overlaps another range.
Definition qgsrange.h:176
bool contains(const QgsRange< T > &other) const
Returns true if this range contains another range.
Definition qgsrange.h:137
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition qgsvector3d.h:31
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
QVector< QMap< QString, QVariant > > result_type
QVector< QVariantMap > operator()(QgsPointCloudNodeId n)
MapIndexedPointCloudNode(QgsPointCloudRequest &request, const QgsVector3D &indexScale, const QgsVector3D &indexOffset, const QgsGeometry &extentGeometry, const QgsDoubleRange &zRange, QgsPointCloudIndex index, int pointsLimit)
Setting options for creating vector data providers.