QGIS API Documentation 3.99.0-Master (26c88405ac0)
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
19
20#include <mutex>
21
22#include "qgis.h"
24#include "qgsgeometry.h"
25#include "qgsgeos.h"
26#include "qgspointcloudindex.h"
29#include "qgsthreadingutils.h"
30
31#include <QDebug>
32#include <QtConcurrent/QtConcurrentMap>
33#include <QtMath>
34
35#include "moc_qgspointclouddataprovider.cpp"
36
44
46
53
55{
57
58 QgsPointCloudIndex lIndex = index();
59 return lIndex.isValid();
60}
61
68
70{
72
73 return QVariantMap();
74}
75
77{
79
80 return nullptr;
81}
82
84{
85 static QMap< int, QString > sCodes
86 {
87 {0, QStringLiteral( "Created, Never Classified" )},
88 {1, QStringLiteral( "Unclassified" )},
89 {2, QStringLiteral( "Ground" )},
90 {3, QStringLiteral( "Low Vegetation" )},
91 {4, QStringLiteral( "Medium Vegetation" )},
92 {5, QStringLiteral( "High Vegetation" )},
93 {6, QStringLiteral( "Building" )},
94 {7, QStringLiteral( "Low Point (Low Noise)" )},
95 {8, QStringLiteral( "Reserved" )},
96 {9, QStringLiteral( "Water" )},
97 {10, QStringLiteral( "Rail" )},
98 {11, QStringLiteral( "Road Surface" )},
99 {12, QStringLiteral( "Reserved" )},
100 {13, QStringLiteral( "Wire - Guard (Shield)" )},
101 {14, QStringLiteral( "Wire - Conductor (Phase)" )},
102 {15, QStringLiteral( "Transmission Tower" )},
103 {16, QStringLiteral( "Wire-Structure Connector (Insulator)" )},
104 {17, QStringLiteral( "Bridge Deck" )},
105 {18, QStringLiteral( "High Noise" )},
106 };
107
108 static std::once_flag initialized;
109 std::call_once( initialized, []( )
110 {
111 for ( int i = 19; i <= 63; ++i )
112 sCodes.insert( i, QStringLiteral( "Reserved" ) );
113 for ( int i = 64; i <= 255; ++i )
114 sCodes.insert( i, QStringLiteral( "User Definable" ) );
115 } );
116
117 return sCodes;
118}
119
121{
122 static QMap< int, QString > sCodes
123 {
124 {0, QObject::tr( "Created, Never Classified" )},
125 {1, QObject::tr( "Unclassified" )},
126 {2, QObject::tr( "Ground" )},
127 {3, QObject::tr( "Low Vegetation" )},
128 {4, QObject::tr( "Medium Vegetation" )},
129 {5, QObject::tr( "High Vegetation" )},
130 {6, QObject::tr( "Building" )},
131 {7, QObject::tr( "Low Point (Noise)" )},
132 {8, QObject::tr( "Reserved" )},
133 {9, QObject::tr( "Water" )},
134 {10, QObject::tr( "Rail" )},
135 {11, QObject::tr( "Road Surface" )},
136 {12, QObject::tr( "Reserved" )},
137 {13, QObject::tr( "Wire - Guard (Shield)" )},
138 {14, QObject::tr( "Wire - Conductor (Phase)" )},
139 {15, QObject::tr( "Transmission Tower" )},
140 {16, QObject::tr( "Wire-Structure Connector (Insulator)" )},
141 {17, QObject::tr( "Bridge Deck" )},
142 {18, QObject::tr( "High Noise" )},
143 };
144
145 static std::once_flag initialized;
146 std::call_once( initialized, []( )
147 {
148 for ( int i = 19; i <= 63; ++i )
149 sCodes.insert( i, QObject::tr( "Reserved" ) );
150 for ( int i = 64; i <= 255; ++i )
151 sCodes.insert( i, QObject::tr( "User Definable" ) );
152 } );
153
154 return sCodes;
155}
156
158{
159 static const QMap< int, QString > sCodes
160 {
161 {0, QStringLiteral( "No color or time stored" )},
162 {1, QStringLiteral( "Time is stored" )},
163 {2, QStringLiteral( "Color is stored" )},
164 {3, QStringLiteral( "Color and time are stored" )},
165 {6, QStringLiteral( "Time is stored" )},
166 {7, QStringLiteral( "Time and color are stored)" )},
167 {8, QStringLiteral( "Time, color and near infrared are stored" )},
168 };
169
170 return sCodes;
171}
172
174{
175 static const QMap< int, QString > sCodes
176 {
177 {0, QObject::tr( "No color or time stored" )},
178 {1, QObject::tr( "Time is stored" )},
179 {2, QObject::tr( "Color is stored" )},
180 {3, QObject::tr( "Color and time are stored" )},
181 {6, QObject::tr( "Time is stored" )},
182 {7, QObject::tr( "Time and color are stored)" )},
183 {8, QObject::tr( "Time, color and near infrared are stored" )},
184 };
185
186 return sCodes;
187}
188
190{
192
193 QgsPointCloudIndex pcIndex = index();
194 if ( pcIndex )
195 {
196 return pcIndex.metadataStatistics();
197 }
199}
200
202{
203 return true;
204}
205
207{
208 return tr( "QGIS expression" );
209}
210
212{
213 // unfortunately we can't access QgsHelp here, that's a GUI class!
214 return QString();
215}
216
218{
219 typedef QVector<QMap<QString, QVariant>> result_type;
220
221 MapIndexedPointCloudNode( QgsPointCloudRequest &request, const QgsVector3D &indexScale, const QgsVector3D &indexOffset,
222 const QgsGeometry &extentGeometry, const QgsDoubleRange &zRange, QgsPointCloudIndex index, int pointsLimit )
223 : mRequest( request ), mIndexScale( indexScale ), mIndexOffset( indexOffset ), mExtentGeometry( extentGeometry ), mZRange( zRange ), mIndex( std::move( index ) ), mPointsLimit( pointsLimit )
224 { }
225
226 QVector<QVariantMap> operator()( QgsPointCloudNodeId n )
227 {
228 QVector<QVariantMap> acceptedPoints;
229 std::unique_ptr<QgsPointCloudBlock> block( mIndex.nodeData( n, mRequest ) );
230
231 if ( !block || pointsCount == mPointsLimit )
232 return acceptedPoints;
233
234 const char *ptr = block->data();
235 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
236 const std::size_t recordSize = blockAttributes.pointRecordSize();
237 int xOffset = 0, yOffset = 0, zOffset = 0;
238 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( QStringLiteral( "X" ), xOffset )->type();
239 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( QStringLiteral( "Y" ), yOffset )->type();
240 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( QStringLiteral( "Z" ), zOffset )->type();
241 auto extentEngine = std::make_unique< QgsGeos >( mExtentGeometry.constGet() );
242 extentEngine->prepareGeometry();
243
244 std::optional<bool> copcTimeFlag = std::nullopt;
245 QVariantMap extraMetadata = mIndex.extraMetadata();
246 if ( extraMetadata.contains( QStringLiteral( "CopcGpsTimeFlag" ) ) )
247 copcTimeFlag = extraMetadata[ QStringLiteral( "CopcGpsTimeFlag" ) ].toBool();
248
249 for ( int i = 0; i < block->pointCount() && pointsCount < mPointsLimit; ++i )
250 {
251 double x, y, z;
252 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, block->scale(), block->offset(), x, y, z );
253
254 if ( mZRange.contains( z ) && extentEngine->contains( x, y ) )
255 {
256 QVariantMap pointAttr = QgsPointCloudAttribute::getAttributeMap( ptr, i * recordSize, blockAttributes );
257 pointAttr[ QStringLiteral( "X" ) ] = x;
258 pointAttr[ QStringLiteral( "Y" ) ] = y;
259 pointAttr[ QStringLiteral( "Z" ) ] = z;
260
261
262 if ( copcTimeFlag.has_value() )
263 {
264 const QDateTime gpsBaseTime = QDateTime::fromSecsSinceEpoch( 315964809, Qt::UTC );
265 constexpr int numberOfSecsInWeek = 3600 * 24 * 7;
266 // here we check the flag set in header to determine if we need to
267 // parse the time as GPS week time or GPS adjusted standard time
268 // 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
269 if ( *copcTimeFlag || pointAttr[QStringLiteral( "GpsTime" )].toDouble() > numberOfSecsInWeek )
270 {
271 const QString utcTime = gpsBaseTime.addSecs( static_cast<qint64>( pointAttr[QStringLiteral( "GpsTime" )].toDouble() + 1e9 ) ).toString( Qt::ISODate );
272 pointAttr[ QStringLiteral( "GpsTime (raw)" )] = pointAttr[QStringLiteral( "GpsTime" )];
273 pointAttr[ QStringLiteral( "GpsTime" )] = utcTime;
274 }
275 else
276 {
277 const QString weekTime = gpsBaseTime.addSecs( pointAttr[QStringLiteral( "GpsTime" )].toLongLong() ).toString( "ddd hh:mm:ss" );
278 pointAttr[ QStringLiteral( "GpsTime (raw)" )] = pointAttr[QStringLiteral( "GpsTime" )];
279 pointAttr[ QStringLiteral( "GpsTime" )] = weekTime;
280 }
281 }
282 pointsCount++;
283 acceptedPoints.push_back( pointAttr );
284 }
285 }
286 return acceptedPoints;
287 }
288
296 int pointsCount = 0;
297};
298
300 double maxError,
301 const QgsGeometry &extentGeometry,
302 const QgsDoubleRange &extentZRange, int pointsLimit )
303{
304 QVector<QVariantMap> acceptedPoints;
305
306 // Try sub-indexes first
307 for ( QgsPointCloudSubIndex &subidx : subIndexes() )
308 {
309 // Check if the sub-index is relevant and if it is loaded. We shouldn't
310 // need to identify points in unloaded indices.
311 QgsPointCloudIndex index = subidx.index();
312 if ( !index
313 || ( !subidx.zRange().overlaps( extentZRange ) )
314 || !subidx.polygonBounds().intersects( extentGeometry ) )
315 continue;
316 acceptedPoints.append( identify( index, maxError, extentGeometry, extentZRange, pointsLimit ) );
317 }
318
319 // Then look at main index
320 QgsPointCloudIndex mainIndex = index();
321 acceptedPoints.append( identify( mainIndex, maxError, extentGeometry, extentZRange, pointsLimit ) );
322
323 return acceptedPoints;
324}
325
327 QgsPointCloudIndex &index, double maxError,
328 const QgsGeometry &extentGeometry,
329 const QgsDoubleRange &extentZRange, int pointsLimit )
330{
332
333 QVector<QVariantMap> acceptedPoints;
334
335 if ( !index.isValid() )
336 return acceptedPoints;
337
338 const QgsPointCloudNode root = index.getNode( index.root() );
339 const QVector<QgsPointCloudNodeId> nodes = traverseTree( index, root, maxError, root.error(), extentGeometry, extentZRange );
340
341 const QgsPointCloudAttributeCollection attributeCollection = index.attributes();
342 QgsPointCloudRequest request;
343 request.setAttributes( attributeCollection );
344
345 acceptedPoints = QtConcurrent::blockingMappedReduced( nodes,
346 MapIndexedPointCloudNode( request, index.scale(), index.offset(), extentGeometry, extentZRange, index, pointsLimit ),
347 qOverload<const QVector<QMap<QString, QVariant>>&>( &QVector<QMap<QString, QVariant>>::append ),
348 QtConcurrent::UnorderedReduce );
349
350 return acceptedPoints;
351}
352
353QVector<QgsPointCloudNodeId> QgsPointCloudDataProvider::traverseTree(
354 const QgsPointCloudIndex &pc,
356 double maxError,
357 double nodeError,
358 const QgsGeometry &extentGeometry,
359 const QgsDoubleRange &extentZRange )
360{
362
363 QVector<QgsPointCloudNodeId> nodes;
364
365 const QgsBox3D nodeBounds = node.bounds();
366 const QgsDoubleRange nodeZRange( nodeBounds.zMinimum(), nodeBounds.zMaximum() );
367 if ( !extentZRange.overlaps( nodeZRange ) )
368 return nodes;
369
370 if ( !extentGeometry.intersects( nodeBounds.toRectangle() ) )
371 return nodes;
372
373 nodes.append( node.id() );
374
375 const double childrenError = nodeError / 2.0;
376 if ( childrenError < maxError )
377 return nodes;
378
379 for ( const QgsPointCloudNodeId &nn : node.children() )
380 {
381 const QgsPointCloudNode childNode = pc.getNode( nn );
382 if ( extentGeometry.intersects( childNode.bounds().toRectangle() ) )
383 nodes += traverseTree( pc, childNode, maxError, childrenError, extentGeometry, extentZRange );
384 }
385
386 return nodes;
387}
388
389bool QgsPointCloudDataProvider::setSubsetString( const QString &subset, bool updateFeatureCount )
390{
392
393 Q_UNUSED( updateFeatureCount )
395 if ( !i )
396 return false;
397
398 if ( !i.setSubsetString( subset ) )
399 return false;
400 mSubsetString = subset;
401 emit dataChanged();
402 return true;
403}
404
411
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:486
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:42
double zMaximum() const
Returns the maximum z value.
Definition qgsbox3d.h:258
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:378
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:251
virtual Qgis::DataProviderFlags flags() const
Returns the generic data provider flags.
void dataChanged()
Emitted whenever a change is made to the data provider which may have caused changes in the provider'...
QgsDataProvider(const QString &uri=QString(), const QgsDataProvider::ProviderOptions &providerOptions=QgsDataProvider::ProviderOptions(), Qgis::DataProviderReadFlags flags=Qgis::DataProviderReadFlags())
Create a new dataprovider with the specified in the uri.
QgsDataSourceUri uri() const
Gets the data source specification.
virtual QgsRectangle extent() const =0
Returns the extent of the layer.
QgsRange which stores a range of double values.
Definition qgsrange.h:233
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
A 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.
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.
QgsPointCloudNode getNode(const QgsPointCloudNodeId &id) const
Returns object for a given node.
Represents an indexed point cloud node's position in octree.
Keeps metadata for an indexed point cloud node.
QList< QgsPointCloudNodeId > children() const
Returns IDs of child nodes.
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.
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
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:30
#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.