QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
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"
27
28#include <mutex>
29#include <QDebug>
30#include <QtMath>
31
32#include <QtConcurrent/QtConcurrentMap>
33
35 const QString &uri,
38 : QgsDataProvider( uri, options, flags )
39{
40}
41
43
50
52{
54
55 QgsPointCloudIndex *lIndex = index();
56 return lIndex && lIndex->isValid();
57}
58
65
67{
69
70 return QVariantMap();
71}
72
74{
76
77 return nullptr;
78}
79
81{
82 static QMap< int, QString > sCodes
83 {
84 {0, QStringLiteral( "Created, Never Classified" )},
85 {1, QStringLiteral( "Unclassified" )},
86 {2, QStringLiteral( "Ground" )},
87 {3, QStringLiteral( "Low Vegetation" )},
88 {4, QStringLiteral( "Medium Vegetation" )},
89 {5, QStringLiteral( "High Vegetation" )},
90 {6, QStringLiteral( "Building" )},
91 {7, QStringLiteral( "Low Point (Low Noise)" )},
92 {8, QStringLiteral( "Reserved" )},
93 {9, QStringLiteral( "Water" )},
94 {10, QStringLiteral( "Rail" )},
95 {11, QStringLiteral( "Road Surface" )},
96 {12, QStringLiteral( "Reserved" )},
97 {13, QStringLiteral( "Wire - Guard (Shield)" )},
98 {14, QStringLiteral( "Wire - Conductor (Phase)" )},
99 {15, QStringLiteral( "Transmission Tower" )},
100 {16, QStringLiteral( "Wire-Structure Connector (Insulator)" )},
101 {17, QStringLiteral( "Bridge Deck" )},
102 {18, QStringLiteral( "High Noise" )},
103 };
104
105 static std::once_flag initialized;
106 std::call_once( initialized, [ = ]( )
107 {
108 for ( int i = 19; i <= 63; ++i )
109 sCodes.insert( i, QStringLiteral( "Reserved" ) );
110 for ( int i = 64; i <= 255; ++i )
111 sCodes.insert( i, QStringLiteral( "User Definable" ) );
112 } );
113
114 return sCodes;
115}
116
118{
119 static QMap< int, QString > sCodes
120 {
121 {0, QObject::tr( "Created, Never Classified" )},
122 {1, QObject::tr( "Unclassified" )},
123 {2, QObject::tr( "Ground" )},
124 {3, QObject::tr( "Low Vegetation" )},
125 {4, QObject::tr( "Medium Vegetation" )},
126 {5, QObject::tr( "High Vegetation" )},
127 {6, QObject::tr( "Building" )},
128 {7, QObject::tr( "Low Point (Noise)" )},
129 {8, QObject::tr( "Reserved" )},
130 {9, QObject::tr( "Water" )},
131 {10, QObject::tr( "Rail" )},
132 {11, QObject::tr( "Road Surface" )},
133 {12, QObject::tr( "Reserved" )},
134 {13, QObject::tr( "Wire - Guard (Shield)" )},
135 {14, QObject::tr( "Wire - Conductor (Phase)" )},
136 {15, QObject::tr( "Transmission Tower" )},
137 {16, QObject::tr( "Wire-Structure Connector (Insulator)" )},
138 {17, QObject::tr( "Bridge Deck" )},
139 {18, QObject::tr( "High Noise" )},
140 };
141
142 static std::once_flag initialized;
143 std::call_once( initialized, [ = ]( )
144 {
145 for ( int i = 19; i <= 63; ++i )
146 sCodes.insert( i, QObject::tr( "Reserved" ) );
147 for ( int i = 64; i <= 255; ++i )
148 sCodes.insert( i, QObject::tr( "User Definable" ) );
149 } );
150
151 return sCodes;
152}
153
155{
156 static const QMap< int, QString > sCodes
157 {
158 {0, QStringLiteral( "No color or time stored" )},
159 {1, QStringLiteral( "Time is stored" )},
160 {2, QStringLiteral( "Color is stored" )},
161 {3, QStringLiteral( "Color and time are stored" )},
162 {6, QStringLiteral( "Time is stored" )},
163 {7, QStringLiteral( "Time and color are stored)" )},
164 {8, QStringLiteral( "Time, color and near infrared are stored" )},
165 };
166
167 return sCodes;
168}
169
171{
172 static const QMap< int, QString > sCodes
173 {
174 {0, QObject::tr( "No color or time stored" )},
175 {1, QObject::tr( "Time is stored" )},
176 {2, QObject::tr( "Color is stored" )},
177 {3, QObject::tr( "Color and time are stored" )},
178 {6, QObject::tr( "Time is stored" )},
179 {7, QObject::tr( "Time and color are stored)" )},
180 {8, QObject::tr( "Time, color and near infrared are stored" )},
181 };
182
183 return sCodes;
184}
185
192
193QVariant QgsPointCloudDataProvider::metadataStatistic( const QString &attribute, Qgis::Statistic statistic ) const
194{
196
197 QgsPointCloudIndex *pcIndex = index();
198 if ( pcIndex )
199 {
200 return pcIndex->metadataStatistic( attribute, statistic );
201 }
202 return QVariant();
203}
204
205QVariantList QgsPointCloudDataProvider::metadataClasses( const QString &attribute ) const
206{
208
209 QgsPointCloudIndex *pcIndex = index();
210 if ( pcIndex )
211 {
212 return pcIndex->metadataClasses( attribute );
213 }
214 return QVariantList();
215}
216
217QVariant QgsPointCloudDataProvider::metadataClassStatistic( const QString &attribute, const QVariant &value, Qgis::Statistic statistic ) const
218{
220
221 QgsPointCloudIndex *pcIndex = index();
222 if ( pcIndex )
223 {
224 return pcIndex->metadataClassStatistic( attribute, value, statistic );
225 }
226 return QVariant();
227}
228
230{
232
233 QgsPointCloudIndex *pcIndex = index();
234 if ( pcIndex )
235 {
236 return pcIndex->metadataStatistics();
237 }
239}
240
242{
243 return true;
244}
245
247{
248 return tr( "QGIS expression" );
249}
250
252{
253 // unfortunately we can't access QgsHelp here, that's a GUI class!
254 return QString();
255}
256
258{
259 typedef QVector<QMap<QString, QVariant>> result_type;
260
261 MapIndexedPointCloudNode( QgsPointCloudRequest &request, const QgsVector3D &indexScale, const QgsVector3D &indexOffset,
262 const QgsGeometry &extentGeometry, const QgsDoubleRange &zRange, QgsPointCloudIndex *index, int pointsLimit )
263 : mRequest( request ), mIndexScale( indexScale ), mIndexOffset( indexOffset ), mExtentGeometry( extentGeometry ), mZRange( zRange ), mIndex( index ), mPointsLimit( pointsLimit )
264 { }
265
266 QVector<QVariantMap> operator()( IndexedPointCloudNode n )
267 {
268 QVector<QVariantMap> acceptedPoints;
269 std::unique_ptr<QgsPointCloudBlock> block( mIndex->nodeData( n, mRequest ) );
270
271 if ( !block || pointsCount == mPointsLimit )
272 return acceptedPoints;
273
274 const char *ptr = block->data();
275 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
276 const std::size_t recordSize = blockAttributes.pointRecordSize();
277 int xOffset = 0, yOffset = 0, zOffset = 0;
278 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( QStringLiteral( "X" ), xOffset )->type();
279 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( QStringLiteral( "Y" ), yOffset )->type();
280 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( QStringLiteral( "Z" ), zOffset )->type();
281 std::unique_ptr< QgsGeos > extentEngine = std::make_unique< QgsGeos >( mExtentGeometry.constGet() );
282 extentEngine->prepareGeometry();
283 for ( int i = 0; i < block->pointCount() && pointsCount < mPointsLimit; ++i )
284 {
285 double x, y, z;
286 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, block->scale(), block->offset(), x, y, z );
287
288 if ( mZRange.contains( z ) && extentEngine->contains( x, y ) )
289 {
290 QVariantMap pointAttr = QgsPointCloudAttribute::getAttributeMap( ptr, i * recordSize, blockAttributes );
291 pointAttr[ QStringLiteral( "X" ) ] = x;
292 pointAttr[ QStringLiteral( "Y" ) ] = y;
293 pointAttr[ QStringLiteral( "Z" ) ] = z;
294 pointsCount++;
295 acceptedPoints.push_back( pointAttr );
296 }
297 }
298 return acceptedPoints;
299 }
300
308 int pointsCount = 0;
309};
310
312 double maxError,
313 const QgsGeometry &extentGeometry,
314 const QgsDoubleRange &extentZRange, int pointsLimit )
315{
316 QVector<QVariantMap> acceptedPoints;
317
318 // Try sub-indexes first
319 for ( QgsPointCloudSubIndex &subidx : subIndexes() )
320 {
321 // Check if the sub-index is relevant and if it is loaded. We shouldn't
322 // need to identify points in unloaded indices.
323 if ( !subidx.index()
324 || ( !subidx.zRange().overlaps( extentZRange ) )
325 || !subidx.polygonBounds().intersects( extentGeometry ) )
326 continue;
327 acceptedPoints.append( identify( subidx.index(), maxError, extentGeometry, extentZRange, pointsLimit ) );
328 }
329
330 // Then look at main index
331 acceptedPoints.append( identify( index(), maxError, extentGeometry, extentZRange, pointsLimit ) );
332
333 return acceptedPoints;
334}
335
337 QgsPointCloudIndex *index, double maxError,
338 const QgsGeometry &extentGeometry,
339 const QgsDoubleRange &extentZRange, int pointsLimit )
340{
342
343 QVector<QVariantMap> acceptedPoints;
344
345 if ( !index || !index->isValid() )
346 return acceptedPoints;
347
348 const IndexedPointCloudNode root = index->root();
349
350 const QgsRectangle rootNodeExtent = index->nodeMapExtent( root );
351 const double rootError = rootNodeExtent.width() / index->span();
352
353 const QVector<IndexedPointCloudNode> nodes = traverseTree( index, root, maxError, rootError, extentGeometry, extentZRange );
354
355 const QgsPointCloudAttributeCollection attributeCollection = index->attributes();
356 QgsPointCloudRequest request;
357 request.setAttributes( attributeCollection );
358
359 acceptedPoints = QtConcurrent::blockingMappedReduced( nodes,
360 MapIndexedPointCloudNode( request, index->scale(), index->offset(), extentGeometry, extentZRange, index, pointsLimit ),
361 qOverload<const QVector<QMap<QString, QVariant>>&>( &QVector<QMap<QString, QVariant>>::append ),
362 QtConcurrent::UnorderedReduce );
363
364 return acceptedPoints;
365}
366
367QVector<IndexedPointCloudNode> QgsPointCloudDataProvider::traverseTree(
368 const QgsPointCloudIndex *pc,
370 double maxError,
371 double nodeError,
372 const QgsGeometry &extentGeometry,
373 const QgsDoubleRange &extentZRange )
374{
376
377 QVector<IndexedPointCloudNode> nodes;
378
379 const QgsDoubleRange nodeZRange = pc->nodeZRange( n );
380 if ( !extentZRange.overlaps( nodeZRange ) )
381 return nodes;
382
383 if ( !extentGeometry.intersects( pc->nodeMapExtent( n ) ) )
384 return nodes;
385
386 nodes.append( n );
387
388 const double childrenError = nodeError / 2.0;
389 if ( childrenError < maxError )
390 return nodes;
391
392 const QList<IndexedPointCloudNode> children = pc->nodeChildren( n );
393 for ( const IndexedPointCloudNode &nn : children )
394 {
395 if ( extentGeometry.intersects( pc->nodeMapExtent( nn ) ) )
396 nodes += traverseTree( pc, nn, maxError, childrenError, extentGeometry, extentZRange );
397 }
398
399 return nodes;
400}
401
402bool QgsPointCloudDataProvider::setSubsetString( const QString &subset, bool updateFeatureCount )
403{
405
406 Q_UNUSED( updateFeatureCount )
407 const auto i = index();
408 if ( !i )
409 return false;
410
411 if ( !i->setSubsetString( subset ) )
412 return false;
413 mSubsetString = subset;
414 emit dataChanged();
415 return true;
416}
417
424
Represents a indexed point cloud node in octree.
Statistic
Available generic statistics.
Definition qgis.h:5432
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.
double width() const
Returns the width of the rectangle.
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.