1 /*************************************************************************** 2 qgspointclouddataprovider.h 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 #ifndef QGSPOINTCLOUDDATAPROVIDER_H 19 #define QGSPOINTCLOUDDATAPROVIDER_H 20 21 #include "qgis_core.h" 22 #include "qgsdataprovider.h" 23 #include "qgspointcloudattribute.h" 24 #include "qgsstatisticalsummary.h" 25 #include "qgspointcloudindex.h" 26 #include "qgspoint.h" 27 #include "qgsray3d.h" 28 #include <memory> 29 30 class IndexedPointCloudNode; 31 class QgsPointCloudIndex; 32 class QgsPointCloudRenderer; 33 class QgsGeometry; 34 35 /** 36 * \ingroup core 37 * \brief Base class for providing data for QgsPointCloudLayer 38 * 39 * Responsible for reading native point cloud data and returning the indexed data. 40 * 41 * \note The API is considered EXPERIMENTAL and can be changed without a notice 42 * 43 * \since QGIS 3.18 44 */ 45 class CORE_EXPORT QgsPointCloudDataProvider: public QgsDataProvider 46 { 47 Q_OBJECT 48 public: 49 50 /** 51 * Capabilities that providers may implement. 52 */ 53 enum Capability 54 { 55 NoCapabilities = 0, //!< Provider has no capabilities 56 ReadLayerMetadata = 1 << 0, //!< Provider can read layer metadata from data store. 57 WriteLayerMetadata = 1 << 1, //!< Provider can write layer metadata to the data store. See QgsDataProvider::writeLayerMetadata() 58 CreateRenderer = 1 << 2, //!< Provider can create 2D renderers using backend-specific formatting information. See QgsPointCloudDataProvider::createRenderer(). 59 }; 60 61 Q_DECLARE_FLAGS( Capabilities, Capability ) 62 63 /** 64 * Point cloud index state 65 */ 66 enum PointCloudIndexGenerationState 67 { 68 NotIndexed = 0, //!< Provider has no index available 69 Indexing = 1 << 0, //!< Provider try to index the source data 70 Indexed = 1 << 1 //!< The index is ready to be used 71 }; 72 73 //! Ctor 74 QgsPointCloudDataProvider( const QString &uri, 75 const QgsDataProvider::ProviderOptions &providerOptions, 76 QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags() ); 77 78 ~QgsPointCloudDataProvider() override; 79 80 #ifndef SIP_RUN 81 82 /** 83 * Returns the list of points of the point cloud according to a zoom level 84 * defined by \a maxError (in layer coordinates), an extent \a geometry in the 2D plane 85 * and a range \a extentZRange for z values. The function will try to limit 86 * the number of points returned to \a pointsLimit points 87 * 88 * \a maxErrorPixels : maximum accepted error factor in pixels 89 * 90 * \note this function does not handle elevation properties and you need to 91 * change elevation coordinates yourself after returning from the function 92 */ 93 QVector<QVariantMap> identify( double maxError, const QgsGeometry &extentGeometry, const QgsDoubleRange &extentZRange = QgsDoubleRange(), int pointsLimit = 1000 ); 94 #else 95 96 /** 97 * Returns the list of points of the point cloud according to a zoom level 98 * defined by \a maxError (in layer coordinates), an extent \a geometry in the 2D plane 99 * and a range \a extentZRange for z values. The function will try to limit 100 * the number of points returned to \a pointsLimit points 101 * 102 * \a maxErrorPixels : maximum accepted error factor in pixels 103 * 104 * \note this function does not handle elevation properties and you need to 105 * change elevation coordinates yourself after returning from the function 106 */ 107 SIP_PYLIST identify( float maxErrorInMapCoords, QgsGeometry extentGeometry, const QgsDoubleRange extentZRange = QgsDoubleRange(), int pointsLimit = 1000 ); 108 % MethodCode 109 { 110 QVector<QMap<QString, QVariant>> res = sipCpp->identify( a0, *a1, *a2, a3 ); 111 sipRes = PyList_New( res.size() ); 112 for ( int i = 0; i < res.size(); ++i ) 113 { 114 PyObject *dict = PyDict_New(); 115 for ( QString key : res[i].keys() ) 116 { 117 PyObject *keyObj = sipConvertFromNewType( new QString( key ), sipType_QString, Py_None ); 118 PyObject *valObj = sipConvertFromNewType( new QVariant( res[i][key] ), sipType_QVariant, Py_None ); 119 PyDict_SetItem( dict, keyObj, valObj ); 120 } 121 PyList_SET_ITEM( sipRes, i, dict ); 122 } 123 } 124 % End 125 #endif 126 127 /** 128 * Returns flags containing the supported capabilities for the data provider. 129 */ 130 virtual QgsPointCloudDataProvider::Capabilities capabilities() const; 131 132 /** 133 * Returns the attributes available from this data provider. 134 * May return empty collection until pointCloudIndexLoaded() is emitted 135 */ 136 virtual QgsPointCloudAttributeCollection attributes() const = 0; 137 138 /** 139 * Triggers loading of the point cloud index 140 * 141 * \sa index() 142 */ 143 virtual void loadIndex( ) = 0; 144 145 /** 146 * Triggers generation of the point cloud index 147 * 148 * emits indexGenerationStateChanged() 149 * 150 * \sa index() 151 */ 152 virtual void generateIndex( ) = 0; 153 154 155 /** 156 * Gets the current index generation state 157 */ 158 virtual PointCloudIndexGenerationState indexingState( ) = 0; 159 160 /** 161 * Returns the point cloud index associated with the provider. 162 * 163 * Can be nullptr (e.g. the index is being created) 164 * 165 * \note Not available in Python bindings 166 */ index()167 virtual QgsPointCloudIndex *index() const SIP_SKIP {return nullptr;} 168 169 /** 170 * Returns whether provider has index which is valid 171 */ 172 bool hasValidIndex() const; 173 174 /** 175 * Returns the total number of points available in the dataset. 176 */ 177 virtual qint64 pointCount() const = 0; 178 179 /** 180 * Returns the polygon bounds of the layer. The CRS of the returned geometry will match the provider's crs(). 181 * 182 * This method will return the best approximation for the actual bounds of points contained in the 183 * dataset available from the provider's metadata. This may match the bounding box rectangle returned 184 * by extent(), or for some datasets a "convex hull" style polygon representing a more precise bounds 185 * will be returned. 186 * 187 * This method will not attempt to calculate the data bounds, rather it will return only whatever precomputed bounds 188 * are included in the data source's metadata. 189 */ 190 virtual QgsGeometry polygonBounds() const; 191 192 /** 193 * Returns a representation of the original metadata included in a point cloud dataset. 194 * 195 * This is a free-form dictionary of values, the contents and structure of which will vary by provider and 196 * dataset. 197 */ 198 virtual QVariantMap originalMetadata() const; 199 200 /** 201 * Creates a new 2D point cloud renderer, using provider backend specific information. 202 * 203 * The \a configuration map can be used to pass provider-specific configuration maps to the provider to 204 * allow customization of the returned renderer. Support and format of \a configuration varies by provider. 205 * 206 * When called with an empty \a configuration map the provider's default renderer will be returned. 207 * 208 * This method returns a new renderer and the caller takes ownership of the returned object. 209 * 210 * Only providers which report the CreateRenderer capability will return a 2D renderer. Other 211 * providers will return NULLPTR. 212 */ 213 virtual QgsPointCloudRenderer *createRenderer( const QVariantMap &configuration = QVariantMap() ) const SIP_FACTORY; 214 #ifndef SIP_RUN 215 216 /** 217 * Returns a statistic for the specified \a attribute, taken only from the metadata of the point cloud 218 * data source. 219 * 220 * This method will not perform any statistical calculations, rather it will return only precomputed attribute 221 * statistics which are included in the data source's metadata. Not all data sources include this information 222 * in the metadata, and even for sources with statistical metadata only some \a statistic values may be available. 223 * 224 * If no matching precalculated statistic is available then an invalid variant will be returned. 225 */ 226 virtual QVariant metadataStatistic( const QString &attribute, QgsStatisticalSummary::Statistic statistic ) const; 227 #else 228 229 /** 230 * Returns a statistic for the specified \a attribute, taken only from the metadata of the point cloud 231 * data source. 232 * 233 * This method will not perform any statistical calculations, rather it will return only precomputed attribute 234 * statistics which are included in the data source's metadata. Not all data sources include this information 235 * in the metadata, and even for sources with statistical metadata only some \a statistic values may be available. 236 * 237 * \throws ValueError if no matching precalculated statistic is available for the attribute. 238 */ 239 SIP_PYOBJECT metadataStatistic( const QString &attribute, QgsStatisticalSummary::Statistic statistic ) const; 240 % MethodCode 241 { 242 const QVariant res = sipCpp->metadataStatistic( *a0, a1 ); 243 if ( !res.isValid() ) 244 { 245 PyErr_SetString( PyExc_ValueError, QStringLiteral( "Statistic is not available" ).toUtf8().constData() ); 246 sipIsErr = 1; 247 } 248 else 249 { 250 QVariant *v = new QVariant( res ); 251 sipRes = sipConvertFromNewType( v, sipType_QVariant, Py_None ); 252 } 253 } 254 % End 255 #endif 256 257 /** 258 * Returns a list of existing classes which are present for the specified \a attribute, taken only from the 259 * metadata of the point cloud data source. 260 * 261 * This method will not perform any classification or scan for available classes, rather it will return only 262 * precomputed classes which are included in the data source's metadata. Not all data sources include this information 263 * in the metadata. 264 */ 265 virtual QVariantList metadataClasses( const QString &attribute ) const; 266 267 268 #ifndef SIP_RUN 269 270 /** 271 * Returns a statistic for one class \a value from the specified \a attribute, taken only from the metadata of the point cloud 272 * data source. 273 * 274 * This method will not perform any statistical calculations, rather it will return only precomputed class 275 * statistics which are included in the data source's metadata. Not all data sources include this information 276 * in the metadata, and even for sources with statistical metadata only some \a statistic values may be available. 277 * 278 * If no matching precalculated statistic is available then an invalid variant will be returned. 279 */ 280 virtual QVariant metadataClassStatistic( const QString &attribute, const QVariant &value, QgsStatisticalSummary::Statistic statistic ) const; 281 #else 282 283 /** 284 * Returns a statistic for one class \a value from the specified \a attribute, taken only from the metadata of the point cloud 285 * data source. 286 * This method will not perform any statistical calculations, rather it will return only precomputed class 287 * statistics which are included in the data source's metadata. Not all data sources include this information 288 * in the metadata, and even for sources with statistical metadata only some \a statistic values may be available. 289 * 290 * \throws ValueError if no matching precalculated statistic is available for the attribute. 291 */ 292 SIP_PYOBJECT metadataClassStatistic( const QString &attribute, const QVariant &value, QgsStatisticalSummary::Statistic statistic ) const; 293 % MethodCode 294 { 295 const QVariant res = sipCpp->metadataClassStatistic( *a0, *a1, a2 ); 296 if ( !res.isValid() ) 297 { 298 PyErr_SetString( PyExc_ValueError, QStringLiteral( "Statistic is not available" ).toUtf8().constData() ); 299 sipIsErr = 1; 300 } 301 else 302 { 303 QVariant *v = new QVariant( res ); 304 sipRes = sipConvertFromNewType( v, sipType_QVariant, Py_None ); 305 } 306 } 307 % End 308 #endif 309 310 /** 311 * Returns the map of LAS classification code to untranslated string value, corresponding to the ASPRS Standard 312 * Lidar Point Classes. 313 * 314 * \see translatedLasClassificationCodes() 315 */ 316 static QMap< int, QString > lasClassificationCodes(); 317 318 /** 319 * Returns the map of LAS classification code to translated string value, corresponding to the ASPRS Standard 320 * Lidar Point Classes. 321 * 322 * \see lasClassificationCodes() 323 */ 324 static QMap< int, QString > translatedLasClassificationCodes(); 325 326 /** 327 * Returns the map of LAS data format ID to untranslated string value. 328 * 329 * \see translatedDataFormatIds() 330 */ 331 static QMap< int, QString > dataFormatIds(); 332 333 /** 334 * Returns the map of LAS data format ID to translated string value. 335 * 336 * \see dataFormatIds() 337 */ 338 static QMap< int, QString > translatedDataFormatIds(); 339 340 signals: 341 342 /** 343 * Emitted when point cloud generation state is changed 344 */ 345 void indexGenerationStateChanged( PointCloudIndexGenerationState state ); 346 347 private: 348 QVector<IndexedPointCloudNode> traverseTree( const QgsPointCloudIndex *pc, IndexedPointCloudNode n, double maxError, double nodeError, const QgsGeometry &extentGeometry, const QgsDoubleRange &extentZRange ); 349 }; 350 351 #endif // QGSMESHDATAPROVIDER_H 352