QGIS API Documentation 3.41.0-Master (3440c17df1d)
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 && 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
193
194QVariant QgsPointCloudDataProvider::metadataStatistic( const QString &attribute, Qgis::Statistic statistic ) const
195{
197
198 QgsPointCloudIndex *pcIndex = index();
199 if ( pcIndex )
200 {
201 return pcIndex->metadataStatistic( attribute, statistic );
202 }
203 return QVariant();
204}
205
206QVariantList QgsPointCloudDataProvider::metadataClasses( const QString &attribute ) const
207{
209
210 QgsPointCloudIndex *pcIndex = index();
211 if ( pcIndex )
212 {
213 return pcIndex->metadataClasses( attribute );
214 }
215 return QVariantList();
216}
217
218QVariant QgsPointCloudDataProvider::metadataClassStatistic( const QString &attribute, const QVariant &value, Qgis::Statistic statistic ) const
219{
221
222 QgsPointCloudIndex *pcIndex = index();
223 if ( pcIndex )
224 {
225 return pcIndex->metadataClassStatistic( attribute, value, statistic );
226 }
227 return QVariant();
228}
229
231{
233
234 QgsPointCloudIndex *pcIndex = index();
235 if ( pcIndex )
236 {
237 return pcIndex->metadataStatistics();
238 }
240}
241
243{
244 return true;
245}
246
248{
249 return tr( "QGIS expression" );
250}
251
253{
254 // unfortunately we can't access QgsHelp here, that's a GUI class!
255 return QString();
256}
257
259{
260 typedef QVector<QMap<QString, QVariant>> result_type;
261
262 MapIndexedPointCloudNode( QgsPointCloudRequest &request, const QgsVector3D &indexScale, const QgsVector3D &indexOffset,
263 const QgsGeometry &extentGeometry, const QgsDoubleRange &zRange, QgsPointCloudIndex *index, int pointsLimit )
264 : mRequest( request ), mIndexScale( indexScale ), mIndexOffset( indexOffset ), mExtentGeometry( extentGeometry ), mZRange( zRange ), mIndex( index ), mPointsLimit( pointsLimit )
265 { }
266
267 QVector<QVariantMap> operator()( IndexedPointCloudNode n )
268 {
269 QVector<QVariantMap> acceptedPoints;
270 std::unique_ptr<QgsPointCloudBlock> block( mIndex->nodeData( n, mRequest ) );
271
272 if ( !block || pointsCount == mPointsLimit )
273 return acceptedPoints;
274
275 const char *ptr = block->data();
276 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
277 const std::size_t recordSize = blockAttributes.pointRecordSize();
278 int xOffset = 0, yOffset = 0, zOffset = 0;
279 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( QStringLiteral( "X" ), xOffset )->type();
280 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( QStringLiteral( "Y" ), yOffset )->type();
281 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( QStringLiteral( "Z" ), zOffset )->type();
282 std::unique_ptr< QgsGeos > extentEngine = std::make_unique< QgsGeos >( mExtentGeometry.constGet() );
283 extentEngine->prepareGeometry();
284 for ( int i = 0; i < block->pointCount() && pointsCount < mPointsLimit; ++i )
285 {
286 double x, y, z;
287 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, block->scale(), block->offset(), x, y, z );
288
289 if ( mZRange.contains( z ) && extentEngine->contains( x, y ) )
290 {
291 QVariantMap pointAttr = QgsPointCloudAttribute::getAttributeMap( ptr, i * recordSize, blockAttributes );
292 pointAttr[ QStringLiteral( "X" ) ] = x;
293 pointAttr[ QStringLiteral( "Y" ) ] = y;
294 pointAttr[ QStringLiteral( "Z" ) ] = z;
295
296
297 if ( const QgsCopcPointCloudIndex *copcIndex = dynamic_cast<QgsCopcPointCloudIndex *>( mIndex ) )
298 {
299 const QDateTime gpsBaseTime = QDateTime::fromSecsSinceEpoch( 315964809, Qt::UTC );
300 constexpr int numberOfSecsInWeek = 3600 * 24 * 7;
301 // here we check the flag set in header to determine if we need to
302 // parse the time as GPS week time or GPS adjusted standard time
303 // 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
304 if ( copcIndex->gpsTimeFlag() || pointAttr[QStringLiteral( "GpsTime" )].toDouble() > numberOfSecsInWeek )
305 {
306 const QString utcTime = gpsBaseTime.addSecs( static_cast<qint64>( pointAttr[QStringLiteral( "GpsTime" )].toDouble() + 1e9 ) ).toString( Qt::ISODate );
307 pointAttr[ QStringLiteral( "GpsTime (raw)" )] = pointAttr[QStringLiteral( "GpsTime" )];
308 pointAttr[ QStringLiteral( "GpsTime" )] = utcTime;
309 }
310 else
311 {
312 const QString weekTime = gpsBaseTime.addSecs( pointAttr[QStringLiteral( "GpsTime" )].toLongLong() ).toString( "ddd hh:mm:ss" );
313 pointAttr[ QStringLiteral( "GpsTime (raw)" )] = pointAttr[QStringLiteral( "GpsTime" )];
314 pointAttr[ QStringLiteral( "GpsTime" )] = weekTime;
315 }
316 }
317 pointsCount++;
318 acceptedPoints.push_back( pointAttr );
319 }
320 }
321 return acceptedPoints;
322 }
323
331 int pointsCount = 0;
332};
333
335 double maxError,
336 const QgsGeometry &extentGeometry,
337 const QgsDoubleRange &extentZRange, int pointsLimit )
338{
339 QVector<QVariantMap> acceptedPoints;
340
341 // Try sub-indexes first
342 for ( QgsPointCloudSubIndex &subidx : subIndexes() )
343 {
344 // Check if the sub-index is relevant and if it is loaded. We shouldn't
345 // need to identify points in unloaded indices.
346 if ( !subidx.index()
347 || ( !subidx.zRange().overlaps( extentZRange ) )
348 || !subidx.polygonBounds().intersects( extentGeometry ) )
349 continue;
350 acceptedPoints.append( identify( subidx.index(), maxError, extentGeometry, extentZRange, pointsLimit ) );
351 }
352
353 // Then look at main index
354 acceptedPoints.append( identify( index(), maxError, extentGeometry, extentZRange, pointsLimit ) );
355
356 return acceptedPoints;
357}
358
360 QgsPointCloudIndex *index, double maxError,
361 const QgsGeometry &extentGeometry,
362 const QgsDoubleRange &extentZRange, int pointsLimit )
363{
365
366 QVector<QVariantMap> acceptedPoints;
367
368 if ( !index || !index->isValid() )
369 return acceptedPoints;
370
371 const IndexedPointCloudNode root = index->root();
372
373 const QgsRectangle rootNodeExtent = index->nodeMapExtent( root );
374 const double rootError = rootNodeExtent.width() / index->span();
375
376 const QVector<IndexedPointCloudNode> nodes = traverseTree( index, root, maxError, rootError, extentGeometry, extentZRange );
377
378 const QgsPointCloudAttributeCollection attributeCollection = index->attributes();
379 QgsPointCloudRequest request;
380 request.setAttributes( attributeCollection );
381
382 acceptedPoints = QtConcurrent::blockingMappedReduced( nodes,
383 MapIndexedPointCloudNode( request, index->scale(), index->offset(), extentGeometry, extentZRange, index, pointsLimit ),
384 qOverload<const QVector<QMap<QString, QVariant>>&>( &QVector<QMap<QString, QVariant>>::append ),
385 QtConcurrent::UnorderedReduce );
386
387 return acceptedPoints;
388}
389
390QVector<IndexedPointCloudNode> QgsPointCloudDataProvider::traverseTree(
391 const QgsPointCloudIndex *pc,
393 double maxError,
394 double nodeError,
395 const QgsGeometry &extentGeometry,
396 const QgsDoubleRange &extentZRange )
397{
399
400 QVector<IndexedPointCloudNode> nodes;
401
402 const QgsDoubleRange nodeZRange = pc->nodeZRange( n );
403 if ( !extentZRange.overlaps( nodeZRange ) )
404 return nodes;
405
406 if ( !extentGeometry.intersects( pc->nodeMapExtent( n ) ) )
407 return nodes;
408
409 nodes.append( n );
410
411 const double childrenError = nodeError / 2.0;
412 if ( childrenError < maxError )
413 return nodes;
414
415 const QList<IndexedPointCloudNode> children = pc->nodeChildren( n );
416 for ( const IndexedPointCloudNode &nn : children )
417 {
418 if ( extentGeometry.intersects( pc->nodeMapExtent( nn ) ) )
419 nodes += traverseTree( pc, nn, maxError, childrenError, extentGeometry, extentZRange );
420 }
421
422 return nodes;
423}
424
425bool QgsPointCloudDataProvider::setSubsetString( const QString &subset, bool updateFeatureCount )
426{
428
429 Q_UNUSED( updateFeatureCount )
430 const auto i = index();
431 if ( !i )
432 return false;
433
434 if ( !i->setSubsetString( subset ) )
435 return false;
436 mSubsetString = subset;
437 emit dataChanged();
438 return true;
439}
440
447
Represents a indexed point cloud node in octree.
Statistic
Available generic statistics.
Definition qgis.h:5446
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:450
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:231
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.
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 bool hasStatisticsMetadata() const
Returns whether the dataset contains statistics metadata.
virtual QgsPointCloudIndex * index() const
Returns the point cloud index associated with the provider.
virtual QVariant metadataStatistic(const QString &attribute, Qgis::Statistic statistic) const
Returns a statistic for the specified attribute, taken only from the metadata of the point cloud data...
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.
virtual QVariantList metadataClasses(const QString &attribute) const
Returns a list of existing classes which are present for the specified attribute, taken only from the...
virtual QVariant metadataClassStatistic(const QString &attribute, const QVariant &value, Qgis::Statistic statistic) const
Returns a statistic for one class value from the specified attribute, taken only from the metadata of...
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 bool isValid() const =0
Returns whether index is loaded and valid.
IndexedPointCloudNode root()
Returns root node of the index.
virtual bool hasStatisticsMetadata() const =0
Returns whether the dataset contains metadata of statistics.
QgsDoubleRange nodeZRange(const IndexedPointCloudNode &node) const
Returns the z range of a node.
virtual std::unique_ptr< QgsPointCloudBlock > nodeData(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request)=0
Returns node data block.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
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
A rectangle specified with double values.
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< QVariantMap > operator()(IndexedPointCloudNode n)
QVector< QMap< QString, QVariant > > result_type
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.