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