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