1 /*************************************************************************** 2 qgsgdalprovider.h - QGIS Data provider for 3 GDAL rasters 4 ------------------- 5 begin : November, 2010 6 copyright : (C) 2010 by Radim Blazek 7 email : radim dot blazek at gmail dot com 8 ***************************************************************************/ 9 10 /*************************************************************************** 11 * * 12 * This program is free software; you can redistribute it and/or modify * 13 * it under the terms of the GNU General Public License as published by * 14 * the Free Software Foundation; either version 2 of the License, or * 15 * (at your option) any later version. * 16 * * 17 ***************************************************************************/ 18 19 #ifndef QGSGDALPROVIDER_H 20 #define QGSGDALPROVIDER_H 21 22 #include "qgscoordinatereferencesystem.h" 23 #include "qgsrasterdataprovider.h" 24 #include "qgsgdalproviderbase.h" 25 #include "qgsrectangle.h" 26 #include "qgscolorrampshader.h" 27 #include "qgsrasterbandstats.h" 28 #include "qgsprovidermetadata.h" 29 #include "qgsprovidersublayerdetails.h" 30 31 #include <QString> 32 #include <QStringList> 33 #include <QDomElement> 34 #include <QMap> 35 #include <QVector> 36 37 #include "qgis_sip.h" 38 39 ///@cond PRIVATE 40 #define SIP_NO_FILE 41 42 class QMutex; 43 44 class QgsRasterPyramid; 45 46 /** 47 * \ingroup core 48 * \brief A call back function for showing progress of gdal operations. 49 */ 50 int CPL_STDCALL progressCallback( double dfComplete, 51 const char *pszMessage, 52 void *pProgressArg ); 53 54 55 class QgsCoordinateTransform; 56 57 /** 58 * 59 * \brief Data provider for GDAL layers. 60 * 61 * This provider implements the interface defined in the QgsDataProvider class 62 * to provide access to spatial data residing in a GDAL layers. 63 * 64 */ 65 class QgsGdalProvider final: public QgsRasterDataProvider, QgsGdalProviderBase 66 { 67 Q_OBJECT 68 69 public: 70 71 /** 72 * Constructor for the provider. 73 * 74 * \param uri file name 75 * \param update whether to open in update mode 76 * \param newDataset handle of newly created dataset. 77 * 78 */ 79 QgsGdalProvider( const QString &uri, const QgsDataProvider::ProviderOptions &providerOptions, bool update = false, GDALDatasetH newDataset = nullptr ); 80 81 //! Create invalid provider with error 82 QgsGdalProvider( const QString &uri, const QgsError &error ); 83 84 ~QgsGdalProvider() override; 85 86 /** 87 * Gets the data source specification. This may be a path or a protocol 88 * connection string 89 * \param expandAuthConfig Whether to expand any assigned authentication configuration 90 * \returns data source specification 91 * \note The default authentication configuration expansion is FALSE. This keeps credentials 92 * out of layer data source URIs and project files. Expansion should be specifically done 93 * only when needed within a provider 94 */ 95 QString dataSourceUri( bool expandAuthConfig = false ) const override; 96 97 /** 98 * Clone the provider. 99 * 100 * The underlying GDAL dataset is shared among the main provider and its 101 * clones. 102 */ 103 QgsGdalProvider *clone() const override; 104 105 QString name() const override; 106 107 /** 108 * GDAL data provider key 109 * \since QGIS 3.10 110 */ 111 static QString providerKey(); 112 113 /** 114 * This helper checks to see whether the file name appears to be a valid 115 * raster file name. If the file name looks like it could be valid, 116 * but some sort of error occurs in processing the file, the error is 117 * returned in \a retErrMsg. 118 * \since QGIS 3.10 119 */ 120 static bool isValidRasterFileName( QString const &fileNameQString, QString &retErrMsg ); 121 122 /** 123 * Gets creation options metadata for a given format 124 * \since QGIS 3.10 125 */ 126 static QString helpCreationOptionsFormat( QString format ); 127 128 /** 129 * Replaces the authcfg part of the string with authentication information 130 * \since QGIS 3.22 131 */ 132 static QString expandAuthConfig( const QString &dsName ); 133 134 QString description() const override; 135 QgsRasterDataProvider::ProviderCapabilities providerCapabilities() const override; 136 QgsCoordinateReferenceSystem crs() const override; 137 QgsRectangle extent() const override; 138 bool isValid() const override; 139 QgsRasterIdentifyResult identify( const QgsPointXY &point, QgsRaster::IdentifyFormat format, const QgsRectangle &boundingBox = QgsRectangle(), int width = 0, int height = 0, int dpi = 96 ) override; 140 double sample( const QgsPointXY &point, int band, bool *ok = nullptr, const QgsRectangle &boundingBox = QgsRectangle(), int width = 0, int height = 0, int dpi = 96 ) override; 141 QString lastErrorTitle() override; 142 QString lastError() override; 143 int capabilities() const override; 144 Qgis::DataType dataType( int bandNo ) const override; 145 Qgis::DataType sourceDataType( int bandNo ) const override; 146 int bandCount() const override; 147 int colorInterpretation( int bandNo ) const override; 148 int xBlockSize() const override; 149 int yBlockSize() const override; 150 int xSize() const override; 151 int ySize() const override; 152 QString generateBandName( int bandNumber ) const override; 153 154 // Reimplemented from QgsRasterDataProvider to bypass second resampling (more efficient for local file based sources) 155 QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback = nullptr ) override; 156 157 bool readBlock( int bandNo, int xBlock, int yBlock, void *data ) override; 158 bool readBlock( int bandNo, QgsRectangle const &viewExtent, int width, int height, void *data, QgsRasterBlockFeedback *feedback = nullptr ) override; 159 160 double bandScale( int bandNo ) const override; 161 double bandOffset( int bandNo ) const override; 162 QList<QgsColorRampShader::ColorRampItem> colorTable( int bandNo )const override; 163 QString htmlMetadata() override; 164 QStringList subLayers() const override; 165 166 static QList< QgsProviderSublayerDetails > sublayerDetails( GDALDatasetH dataset, const QString &baseUri ); 167 168 bool hasStatistics( int bandNo, 169 int stats = QgsRasterBandStats::All, 170 const QgsRectangle &boundingBox = QgsRectangle(), 171 int sampleSize = 0 ) override; 172 173 QgsRasterBandStats bandStatistics( int bandNo, 174 int stats = QgsRasterBandStats::All, 175 const QgsRectangle &boundingBox = QgsRectangle(), 176 int sampleSize = 0, QgsRasterBlockFeedback *feedback = nullptr ) override; 177 178 bool hasHistogram( int bandNo, 179 int binCount = 0, 180 double minimum = std::numeric_limits<double>::quiet_NaN(), 181 double maximum = std::numeric_limits<double>::quiet_NaN(), 182 const QgsRectangle &boundingBox = QgsRectangle(), 183 int sampleSize = 0, 184 bool includeOutOfRange = false ) override; 185 186 QgsRasterHistogram histogram( int bandNo, 187 int binCount = 0, 188 double minimum = std::numeric_limits<double>::quiet_NaN(), 189 double maximum = std::numeric_limits<double>::quiet_NaN(), 190 const QgsRectangle &boundingBox = QgsRectangle(), 191 int sampleSize = 0, 192 bool includeOutOfRange = false, QgsRasterBlockFeedback *feedback = nullptr ) override; 193 194 QString buildPyramids( const QList<QgsRasterPyramid> &rasterPyramidList, 195 const QString &resamplingMethod = "NEAREST", 196 QgsRaster::RasterPyramidsFormat format = QgsRaster::PyramidsGTiff, 197 const QStringList &createOptions = QStringList(), 198 QgsRasterBlockFeedback *feedback = nullptr ) override; 199 QList<QgsRasterPyramid> buildPyramidList( const QList<int> &overviewList = QList<int>() ) override; 200 201 static QMap<QString, QString> supportedMimes(); 202 203 bool isEditable() const override; 204 bool setEditable( bool enabled ) override; 205 bool write( void *data, int band, int width, int height, int xOffset, int yOffset ) override; 206 207 bool setNoDataValue( int bandNo, double noDataValue ) override; 208 bool remove() override; 209 210 QString validateCreationOptions( const QStringList &createOptions, const QString &format ) override; 211 QString validatePyramidsConfigOptions( QgsRaster::RasterPyramidsFormat pyramidsFormat, 212 const QStringList &configOptions, const QString &fileFormat ) override; 213 214 QgsPoint transformCoordinates( const QgsPoint &point, TransformType type ) override; 215 enableProviderResampling(bool enable)216 bool enableProviderResampling( bool enable ) override { mProviderResamplingEnabled = enable; return true; } setZoomedInResamplingMethod(ResamplingMethod method)217 bool setZoomedInResamplingMethod( ResamplingMethod method ) override { mZoomedInResamplingMethod = method; return true; } setZoomedOutResamplingMethod(ResamplingMethod method)218 bool setZoomedOutResamplingMethod( ResamplingMethod method ) override { mZoomedOutResamplingMethod = method; return true; } setMaxOversampling(double factor)219 bool setMaxOversampling( double factor ) override { mMaxOversampling = factor; return true; } 220 221 private: 222 QgsGdalProvider( const QgsGdalProvider &other ); 223 QgsGdalProvider &operator=( const QgsGdalProvider & ) = delete; 224 225 //! Whether mGdalDataset and mGdalBaseDataset have been attempted to be set 226 bool mHasInit = false; 227 228 //! Open mGdalDataset/mGdalBaseDataset if needed. 229 bool initIfNeeded(); 230 231 // There are 2 cloning mechanisms. 232 // * Either the cloned provider use the same GDAL handles as the main provider 233 // instance, in which case *mpRefCounter is used to count how many providers 234 // use the main provider. And *mpMutex is used to protect access to the 235 // GDAL resource. 236 // * Or the cloned provider use its own GDAL handles, but with a cache mechanism 237 // to avoid constant opening/closing of datasets. In that case *mpParent is 238 // used to point to the main provider, and *mpLightRefCounter to count how 239 // many providers point to that main provider. 240 241 // reference counter to know how many main and shared provider instances are linked 242 QAtomicInt *mpRefCounter = nullptr; 243 244 // mutex to protect access to mGdalDataset among main and shared provider instances 245 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) 246 QMutex *mpMutex = nullptr; 247 #else 248 QRecursiveMutex *mpMutex = nullptr; 249 #endif 250 251 252 // pointer to a QgsGdalProvider* that is the parent. Note when *mpParent == this, we are the parent. 253 QgsGdalProvider **mpParent = nullptr; 254 255 // reference counter to know how many main and related provider instances are linked 256 mutable QAtomicInt *mpLightRefCounter = nullptr; 257 258 // update mode 259 bool mUpdate; 260 261 //! Do some initialization on the dataset (e.g. handling of south-up datasets) 262 void initBaseDataset(); 263 264 /** 265 * Flag indicating if the layer data source is a valid layer 266 */ 267 bool mValid = false; 268 269 //! \brief Whether this raster has overviews / pyramids or not 270 bool mHasPyramids = false; 271 272 /** 273 * \brief Gdal data types used to represent data in in QGIS, 274 * may be longer than source data type to keep nulls 275 * indexed from 0 276 */ 277 QList<GDALDataType> mGdalDataType; 278 279 QgsRectangle mExtent; 280 int mWidth = 0; 281 int mHeight = 0; 282 int mXBlockSize = 0; 283 int mYBlockSize = 0; 284 int mBandCount = 1; 285 286 //mutable QList<bool> mMinMaxComputed; 287 288 // List of estimated min values, index 0 for band 1 289 //mutable QList<double> mMinimum; 290 291 // List of estimated max values, index 0 for band 1 292 //mutable QList<double> mMaximum; 293 294 //! \brief Pointer to the gdaldataset 295 GDALDatasetH mGdalBaseDataset = nullptr; 296 297 //! \brief Pointer to the gdaldataset (possibly warped vrt) 298 GDALDatasetH mGdalDataset = nullptr; 299 300 //! \brief Values for mapping pixel to world coordinates. Contents of this array are the same as the GDAL adfGeoTransform 301 double mGeoTransform[6]; 302 303 QgsCoordinateReferenceSystem mCrs; 304 305 QList<QgsRasterPyramid> mPyramidList; 306 307 //! \brief sublayers list saved for subsequent access 308 QList< QgsProviderSublayerDetails > mSubLayers; 309 310 //! Whether a per-dataset mask band is exposed as an alpha band for the point of view of the rest of the application. 311 bool mMaskBandExposedAsAlpha = false; 312 313 //! \brief Driver short name. 314 // It is kept in case the driver would be de-registered after the provider has been created. 315 // Which is a very dangerous situation (see #29212) 316 QString mDriverName; 317 318 //! Wrapper for GDALGetRasterBand() that takes into account mMaskBandExposedAsAlpha. 319 GDALRasterBandH getBand( int bandNo ) const; 320 321 //! \brief Close data set and release related data 322 void closeDataset(); 323 324 //! Pair of GDAL base dataset and "real" dataset handles. 325 struct DatasetPair 326 { 327 GDALDatasetH mGdalBaseDataset; 328 GDALDatasetH mGdalDataset; 329 }; 330 331 // Dataset cache 332 static QHash< QgsGdalProvider *, QVector<DatasetPair> > mgDatasetCache; 333 334 // Number of cached datasets in mgDatasetCache ( == sum(iter.value().size() ) 335 static int mgDatasetCacheSize; 336 337 //! Add handles to the cache if possible for the specified parent provider, in which case true is returned. If false returned, then the handles should be processed appropriately by the caller 338 static bool cacheGdalHandlesForLaterReuse( QgsGdalProvider *provider, GDALDatasetH gdalBaseDataset, GDALDatasetH gdalDataset ); 339 340 //! Gets cached handles for the specified provider, in which case true is returned and 2 handles are set. 341 static bool getCachedGdalHandles( QgsGdalProvider *provider, GDALDatasetH &gdalBaseDataset, GDALDatasetH &gdalDataset ); 342 343 //! Close all cached dataset for the specified provider. 344 static void closeCachedGdalHandlesFor( QgsGdalProvider *provider ); 345 346 /** 347 * Converts a world (\a x, \a y) coordinate to a pixel \a row and \a col. 348 */ 349 bool worldToPixel( double x, double y, int &col, int &row ) const; 350 351 bool mStatisticsAreReliable = false; 352 353 /** 354 * Closes and reinits dataset 355 */ 356 void reloadProviderData() override; 357 358 //! Instance of GDAL transformer function used in transformCoordinates() for conversion between image and layer coordinates 359 void *mGdalTransformerArg = nullptr; 360 361 bool canDoResampling( 362 int bandNo, 363 const QgsRectangle &reqExtent, 364 int bufferWidthPix, 365 int bufferHeightPix ); 366 }; 367 368 /** 369 * Metadata of the GDAL data provider 370 */ 371 class QgsGdalProviderMetadata final: public QgsProviderMetadata 372 { 373 public: 374 QgsGdalProviderMetadata(); 375 QVariantMap decodeUri( const QString &uri ) const override; 376 QString encodeUri( const QVariantMap &parts ) const override; 377 bool uriIsBlocklisted( const QString &uri ) const override; 378 QgsGdalProvider *createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags() ) override; 379 QgsGdalProvider *createRasterDataProvider( 380 const QString &uri, 381 const QString &format, 382 int nBands, 383 Qgis::DataType type, 384 int width, 385 int height, 386 double *geoTransform, 387 const QgsCoordinateReferenceSystem &crs, 388 const QStringList &createOptions ) override; 389 QString filters( FilterType type ) override; 390 QList<QPair<QString, QString> > pyramidResamplingMethods() override; 391 QgsProviderMetadata::ProviderMetadataCapabilities capabilities() const override; 392 ProviderCapabilities providerCapabilities() const override; 393 QList< QgsProviderSublayerDetails > querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags = Qgis::SublayerQueryFlags(), QgsFeedback *feedback = nullptr ) const override; 394 QStringList sidecarFilesForUri( const QString &uri ) const override; 395 }; 396 397 ///@endcond 398 #endif 399