64
64
static QString PROVIDER_KEY = QStringLiteral( " gdal" );
65
65
static QString PROVIDER_DESCRIPTION = QStringLiteral( " GDAL provider" );
66
66
67
+ // To avoid potential races when destroying related instances ("main" and clones)
68
+ static QMutex gGdaProviderMutex ( QMutex::Recursive );
69
+
70
+ QMap< QgsGdalProvider *, QVector<QgsGdalProvider::DatasetPair> > QgsGdalProvider::mgDatasetCache;
71
+
72
+ int QgsGdalProvider::mgDatasetCacheSize = 0 ;
73
+
74
+ // Number of cached datasets from which we will try to do eviction when a
75
+ // provider has 2 or more cached datasets
76
+ const int MIN_THRESHOLD_FOR_CACHE_CLEANUP = 10 ;
77
+
78
+ // Maximum number of cached datasets
79
+ // We try to keep at least 1 cached dataset per parent provider between
80
+ // MIN_THRESHOLD_FOR_CACHE_CLEANUP and MAX_CACHE_SIZE. But we don't want to
81
+ // maintain more than MAX_CACHE_SIZE datasets opened to avoid running short of
82
+ // file descriptors.
83
+ const int MAX_CACHE_SIZE = 50 ;
84
+
67
85
struct QgsGdalProgress
68
86
{
69
87
int type;
@@ -106,6 +124,8 @@ int CPL_STDCALL progressCallback( double dfComplete,
106
124
107
125
QgsGdalProvider::QgsGdalProvider ( const QString &uri, const QgsError &error )
108
126
: QgsRasterDataProvider( uri )
127
+ , mpRefCounter( new QAtomicInt( 1 ) )
128
+ , mpLightRefCounter( new QAtomicInt( 1 ) )
109
129
, mUpdate( false )
110
130
{
111
131
mGeoTransform [0 ] = 0 ;
@@ -119,6 +139,10 @@ QgsGdalProvider::QgsGdalProvider( const QString &uri, const QgsError &error )
119
139
120
140
QgsGdalProvider::QgsGdalProvider ( const QString &uri, bool update, GDALDatasetH dataset )
121
141
: QgsRasterDataProvider( uri )
142
+ , mpRefCounter( new QAtomicInt( 1 ) )
143
+ , mpMutex( new QMutex( QMutex::Recursive ) )
144
+ , mpParent( new QgsGdalProvider * ( this ) )
145
+ , mpLightRefCounter( new QAtomicInt( 1 ) )
122
146
, mUpdate( update )
123
147
{
124
148
mGeoTransform [0 ] = 0 ;
@@ -159,40 +183,80 @@ QgsGdalProvider::QgsGdalProvider( const QString &uri, bool update, GDALDatasetH
159
183
if ( dataset )
160
184
{
161
185
mGdalBaseDataset = dataset;
186
+ initBaseDataset ();
162
187
}
163
188
else
164
189
{
165
- // Try to open using VSIFileHandler (see qgsogrprovider.cpp)
166
- QString vsiPrefix = QgsZipItem::vsiPrefix ( uri );
167
- if ( !vsiPrefix.isEmpty () )
168
- {
169
- if ( !uri.startsWith ( vsiPrefix ) )
170
- setDataSourceUri ( vsiPrefix + uri );
171
- QgsDebugMsg ( QString ( " Trying %1 syntax, uri= %2" ).arg ( vsiPrefix, dataSourceUri () ) );
172
- }
190
+ initIfNeeded ();
191
+ }
192
+ }
173
193
174
- QString gdalUri = dataSourceUri ();
194
+ QgsGdalProvider::QgsGdalProvider ( const QgsGdalProvider &other )
195
+ : QgsRasterDataProvider( other.dataSourceUri() )
196
+ , mUpdate( false )
197
+ {
198
+ // The JP2OPENJPEG driver might consume too much memory on large datasets
199
+ // so make sure to really use a single one.
200
+ bool forceUseSameDataset =
201
+ ( other.mGdalBaseDataset &&
202
+ GDALGetDatasetDriver ( other.mGdalBaseDataset ) == GDALGetDriverByName ( " JP2OPENJPEG" ) ) ||
203
+ CSLTestBoolean ( CPLGetConfigOption ( " QGIS_GDAL_FORCE_USE_SAME_DATASET" , " FALSE" ) );
175
204
176
- CPLErrorReset ();
177
- mGdalBaseDataset = gdalOpen ( gdalUri.toUtf8 ().constData (), mUpdate ? GA_Update : GA_ReadOnly );
205
+ if ( forceUseSameDataset )
206
+ {
207
+ ++ ( *other.mpRefCounter );
208
+ mpRefCounter = other.mpRefCounter ;
209
+ mpMutex = other.mpMutex ;
210
+ mpLightRefCounter = new QAtomicInt ( 1 );
211
+ mHasInit = other.mHasInit ;
212
+ mValid = other.mValid ;
213
+ mGdalBaseDataset = other.mGdalBaseDataset ;
214
+ mGdalDataset = other.mGdalDataset ;
215
+ }
216
+ else
217
+ {
218
+
219
+ ++ ( *other.mpLightRefCounter );
220
+
221
+ mpRefCounter = new QAtomicInt ( 1 );
222
+ mpLightRefCounter = other.mpLightRefCounter ;
223
+ mpMutex = new QMutex ( QMutex::Recursive );
224
+ mpParent = other.mpParent ;
178
225
179
- if ( ! mGdalBaseDataset )
226
+ if ( getCachedGdalHandles ( const_cast <QgsGdalProvider *>( &other ), mGdalBaseDataset , mGdalDataset ) )
180
227
{
181
- QString msg = QStringLiteral ( " Cannot open GDAL dataset %1:\n %2" ).arg ( dataSourceUri (), QString::fromUtf8 ( CPLGetLastErrorMsg () ) );
182
- appendError ( ERRMSG ( msg ) );
183
- return ;
228
+ QgsDebugMsgLevel ( " recycling already opened dataset" , 5 );
229
+ mHasInit = true ;
230
+ mValid = other.mValid ;
231
+ }
232
+ else
233
+ {
234
+ QgsDebugMsgLevel ( " will need to open new dataset" , 5 );
235
+ mHasInit = false ;
236
+ mValid = false ;
184
237
}
185
238
186
- QgsDebugMsg ( " GdalDataset opened" );
187
239
}
188
- initBaseDataset ();
240
+
241
+ mHasPyramids = other.mHasPyramids ;
242
+ mGdalDataType = other.mGdalDataType ;
243
+ mExtent = other.mExtent ;
244
+ mWidth = other.mWidth ;
245
+ mHeight = other.mHeight ;
246
+ mXBlockSize = other.mXBlockSize ;
247
+ mYBlockSize = other.mYBlockSize ;
248
+ memcpy ( mGeoTransform , other.mGeoTransform , sizeof ( mGeoTransform ) );
249
+ mCrs = other.mCrs ;
250
+ mPyramidList = other.mPyramidList ;
251
+ mSubLayers = other.mSubLayers ;
252
+ mMaskBandExposedAsAlpha = other.mMaskBandExposedAsAlpha ;
253
+ mBandCount = other.mBandCount ;
254
+ copyBaseSettings ( other );
189
255
}
190
256
191
257
QgsGdalProvider *QgsGdalProvider::clone () const
192
258
{
193
- QgsGdalProvider *provider = new QgsGdalProvider ( dataSourceUri () );
194
- provider->copyBaseSettings ( *this );
195
- return provider;
259
+ return new QgsGdalProvider ( *this );
196
260
}
197
261
198
262
bool QgsGdalProvider::crsFromWkt ( const char *wkt )
@@ -233,15 +297,163 @@ bool QgsGdalProvider::crsFromWkt( const char *wkt )
233
297
return mCrs .isValid ();
234
298
}
235
299
236
- QgsGdalProvider::~QgsGdalProvider ()
300
+ bool QgsGdalProvider::getCachedGdalHandles ( QgsGdalProvider *provider,
301
+ GDALDatasetH &gdalBaseDataset,
302
+ GDALDatasetH &gdalDataset )
237
303
{
238
- if ( mGdalBaseDataset )
304
+ QMutexLocker locker ( &gGdaProviderMutex );
305
+
306
+ auto iter = mgDatasetCache.find ( provider );
307
+ if ( iter == mgDatasetCache.end () )
239
308
{
240
- GDALDereferenceDataset ( mGdalBaseDataset ) ;
309
+ return false ;
241
310
}
242
- if ( mGdalDataset )
311
+
312
+ if ( !iter.value ().isEmpty () )
243
313
{
244
- GDALClose ( mGdalDataset );
314
+ DatasetPair pair = iter.value ().takeFirst ();
315
+ mgDatasetCacheSize --;
316
+ gdalBaseDataset = pair.mGdalBaseDataset ;
317
+ gdalDataset = pair.mGdalDataset ;
318
+ return true ;
319
+ }
320
+ return false ;
321
+ }
322
+
323
+ bool QgsGdalProvider::cacheGdalHandlesForLaterReuse ( QgsGdalProvider *provider,
324
+ GDALDatasetH gdalBaseDataset,
325
+ GDALDatasetH gdalDataset )
326
+ {
327
+ QMutexLocker locker ( &gGdaProviderMutex );
328
+
329
+ // If the cache size goes above the soft limit, try to do evict a cached
330
+ // dataset for the provider that has the most cached entries
331
+ if ( mgDatasetCacheSize >= MIN_THRESHOLD_FOR_CACHE_CLEANUP )
332
+ {
333
+ auto iter = mgDatasetCache.find ( provider );
334
+ if ( iter == mgDatasetCache.end () || iter.value ().isEmpty () )
335
+ {
336
+ QgsGdalProvider *candidateProvider = nullptr ;
337
+ int nLargestCountOfCachedDatasets = 0 ;
338
+ for ( iter = mgDatasetCache.begin (); iter != mgDatasetCache.end (); ++iter )
339
+ {
340
+ if ( iter.value ().size () > nLargestCountOfCachedDatasets )
341
+ {
342
+ candidateProvider = iter.key ();
343
+ nLargestCountOfCachedDatasets = iter.value ().size ();
344
+ }
345
+ }
346
+
347
+ Q_ASSERT ( candidateProvider );
348
+ Q_ASSERT ( !mgDatasetCache[ candidateProvider ].isEmpty () );
349
+
350
+ // If the candidate is ourselves, then do nothing
351
+ if ( candidateProvider == provider )
352
+ return false ;
353
+
354
+ // If the candidate provider has at least 2 cached datasets, then
355
+ // we can evict one.
356
+ // In the case where providers have at most one cached dataset, then
357
+ // evict one arbitrarily
358
+ if ( nLargestCountOfCachedDatasets >= 2 ||
359
+ mgDatasetCacheSize >= MAX_CACHE_SIZE )
360
+ {
361
+ mgDatasetCacheSize --;
362
+ DatasetPair pair = mgDatasetCache[ candidateProvider ].takeLast ();
363
+ if ( pair.mGdalBaseDataset )
364
+ {
365
+ GDALDereferenceDataset ( pair.mGdalBaseDataset );
366
+ }
367
+ if ( pair.mGdalDataset )
368
+ {
369
+ GDALClose ( pair.mGdalDataset );
370
+ }
371
+ }
372
+ }
373
+ else
374
+ {
375
+ return false ;
376
+ }
377
+ }
378
+
379
+ // Add handles to the cache
380
+ auto iter = mgDatasetCache.find ( provider );
381
+ if ( iter == mgDatasetCache.end () )
382
+ {
383
+ mgDatasetCache[provider] = QVector<DatasetPair>();
384
+ iter = mgDatasetCache.find ( provider );
385
+ }
386
+
387
+ mgDatasetCacheSize ++;
388
+ DatasetPair pair;
389
+ pair.mGdalBaseDataset = gdalBaseDataset;
390
+ pair.mGdalDataset = gdalDataset;
391
+ iter.value ().push_back ( pair );
392
+
393
+ return true ;
394
+ }
395
+
396
+ void QgsGdalProvider::closeCachedGdalHandlesFor ( QgsGdalProvider *provider )
397
+ {
398
+ QMutexLocker locker ( &gGdaProviderMutex );
399
+ auto iter = mgDatasetCache.find ( provider );
400
+ if ( iter != mgDatasetCache.end () )
401
+ {
402
+ while ( !iter.value ().isEmpty () )
403
+ {
404
+ mgDatasetCacheSize --;
405
+ DatasetPair pair = iter.value ().takeLast ();
406
+ if ( pair.mGdalBaseDataset )
407
+ {
408
+ GDALDereferenceDataset ( pair.mGdalBaseDataset );
409
+ }
410
+ if ( pair.mGdalDataset )
411
+ {
412
+ GDALClose ( pair.mGdalDataset );
413
+ }
414
+ }
415
+ mgDatasetCache.erase ( iter );
416
+ }
417
+ }
418
+
419
+
420
+ QgsGdalProvider::~QgsGdalProvider ()
421
+ {
422
+ QMutexLocker locker ( &gGdaProviderMutex );
423
+
424
+ int lightRefCounter = -- ( *mpLightRefCounter );
425
+ int refCounter = -- ( *mpRefCounter );
426
+ if ( refCounter == 0 )
427
+ {
428
+ if ( mpParent && *mpParent && *mpParent != this && mGdalBaseDataset &&
429
+ cacheGdalHandlesForLaterReuse ( *mpParent, mGdalBaseDataset , mGdalDataset ) )
430
+ {
431
+ // do nothing
432
+ }
433
+ else
434
+ {
435
+ if ( mGdalBaseDataset )
436
+ {
437
+ GDALDereferenceDataset ( mGdalBaseDataset );
438
+ }
439
+ if ( mGdalDataset )
440
+ {
441
+ GDALClose ( mGdalDataset );
442
+ }
443
+
444
+ if ( mpParent && *mpParent == this )
445
+ {
446
+ *mpParent = nullptr ;
447
+ closeCachedGdalHandlesFor ( this );
448
+ }
449
+ }
450
+ delete mpMutex;
451
+ delete mpRefCounter;
452
+ if ( lightRefCounter == 0 )
453
+ {
454
+ delete mpLightRefCounter;
455
+ delete mpParent;
456
+ }
245
457
}
246
458
}
247
459
@@ -264,6 +476,9 @@ void QgsGdalProvider::closeDataset()
264
476
265
477
QString QgsGdalProvider::metadata ()
266
478
{
479
+ QMutexLocker locker ( mpMutex );
480
+ if ( !initIfNeeded () )
481
+ return QString ();
267
482
QString myMetadata;
268
483
myMetadata += QString ( GDALGetDescription ( GDALGetDatasetDriver ( mGdalDataset ) ) );
269
484
myMetadata += QLatin1String ( " <br>" );
@@ -399,6 +614,8 @@ QString QgsGdalProvider::metadata()
399
614
QgsRasterBlock *QgsGdalProvider::block ( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback )
400
615
{
401
616
QgsRasterBlock *block = new QgsRasterBlock ( dataType ( bandNo ), width, height );
617
+ if ( !initIfNeeded () )
618
+ return block;
402
619
if ( sourceHasNoDataValue ( bandNo ) && useSourceNoDataValue ( bandNo ) )
403
620
{
404
621
block->setNoDataValue ( sourceNoDataValue ( bandNo ) );
@@ -423,6 +640,10 @@ QgsRasterBlock *QgsGdalProvider::block( int bandNo, const QgsRectangle &extent,
423
640
424
641
void QgsGdalProvider::readBlock ( int bandNo, int xBlock, int yBlock, void *block )
425
642
{
643
+ QMutexLocker locker ( mpMutex );
644
+ if ( !initIfNeeded () )
645
+ return ;
646
+
426
647
// TODO!!!: Check data alignment!!! May it happen that nearest value which
427
648
// is not nearest is assigned to an output cell???
428
649
@@ -440,6 +661,10 @@ void QgsGdalProvider::readBlock( int bandNo, int xBlock, int yBlock, void *block
440
661
441
662
void QgsGdalProvider::readBlock ( int bandNo, QgsRectangle const &extent, int pixelWidth, int pixelHeight, void *block, QgsRasterBlockFeedback *feedback )
442
663
{
664
+ QMutexLocker locker ( mpMutex );
665
+ if ( !initIfNeeded () )
666
+ return ;
667
+
443
668
QgsDebugMsgLevel ( " thePixelWidth = " + QString::number ( pixelWidth ), 5 );
444
669
QgsDebugMsgLevel ( " thePixelHeight = " + QString::number ( pixelHeight ), 5 );
445
670
QgsDebugMsgLevel ( " theExtent: " + extent.toString (), 5 );
@@ -872,6 +1097,9 @@ void QgsGdalProvider::computeMinMax( int bandNo ) const
872
1097
*/
873
1098
QList<QgsColorRampShader::ColorRampItem> QgsGdalProvider::colorTable ( int bandNumber )const
874
1099
{
1100
+ QMutexLocker locker ( mpMutex );
1101
+ if ( !const_cast <QgsGdalProvider *>( this )->initIfNeeded () )
1102
+ return QList<QgsColorRampShader::ColorRampItem>();
875
1103
return QgsGdalProviderBase::colorTable ( mGdalDataset , bandNumber );
876
1104
}
877
1105
@@ -903,6 +1131,10 @@ int QgsGdalProvider::ySize() const { return mHeight; }
903
1131
904
1132
QString QgsGdalProvider::generateBandName ( int bandNumber ) const
905
1133
{
1134
+ QMutexLocker locker ( mpMutex );
1135
+ if ( !const_cast <QgsGdalProvider *>( this )->initIfNeeded () )
1136
+ return QString ();
1137
+
906
1138
if ( strcmp ( GDALGetDriverShortName ( GDALGetDatasetDriver ( mGdalDataset ) ), " netCDF" ) == 0 )
907
1139
{
908
1140
char **GDALmetadata = GDALGetMetadata ( mGdalDataset , nullptr );
@@ -971,6 +1203,10 @@ QString QgsGdalProvider::generateBandName( int bandNumber ) const
971
1203
972
1204
QgsRasterIdentifyResult QgsGdalProvider::identify ( const QgsPointXY &point, QgsRaster::IdentifyFormat format, const QgsRectangle &boundingBox, int width, int height, int /* dpi*/ )
973
1205
{
1206
+ QMutexLocker locker ( mpMutex );
1207
+ if ( !initIfNeeded () )
1208
+ return QgsRasterIdentifyResult ( ERR ( tr ( " Cannot read data" ) ) );
1209
+
974
1210
QgsDebugMsg ( QString ( " thePoint = %1 %2" ).arg ( point.x (), 0 , ' g' , 10 ).arg ( point.y (), 0 , ' g' , 10 ) );
975
1211
976
1212
QMap<int , QVariant> results;
@@ -1058,6 +1294,10 @@ QgsRasterIdentifyResult QgsGdalProvider::identify( const QgsPointXY &point, QgsR
1058
1294
1059
1295
int QgsGdalProvider::capabilities () const
1060
1296
{
1297
+ QMutexLocker locker ( mpMutex );
1298
+ if ( !const_cast <QgsGdalProvider *>( this )->initIfNeeded () )
1299
+ return 0 ;
1300
+
1061
1301
int capability = QgsRasterDataProvider::Identify
1062
1302
| QgsRasterDataProvider::IdentifyValue
1063
1303
| QgsRasterDataProvider::Size
@@ -1076,6 +1316,10 @@ int QgsGdalProvider::capabilities() const
1076
1316
1077
1317
Qgis::DataType QgsGdalProvider::sourceDataType ( int bandNo ) const
1078
1318
{
1319
+ QMutexLocker locker ( mpMutex );
1320
+ if ( !const_cast <QgsGdalProvider *>( this )->initIfNeeded () )
1321
+ return dataTypeFromGdal ( GDT_Byte );
1322
+
1079
1323
if ( mMaskBandExposedAsAlpha && bandNo == GDALGetRasterCount ( mGdalDataset ) + 1 )
1080
1324
return dataTypeFromGdal ( GDT_Byte );
1081
1325
@@ -1118,7 +1362,7 @@ Qgis::DataType QgsGdalProvider::sourceDataType( int bandNo ) const
1118
1362
1119
1363
Qgis::DataType QgsGdalProvider::dataType ( int bandNo ) const
1120
1364
{
1121
- if ( mMaskBandExposedAsAlpha && bandNo == GDALGetRasterCount ( mGdalDataset ) + 1 )
1365
+ if ( mMaskBandExposedAsAlpha && bandNo == mBandCount )
1122
1366
return dataTypeFromGdal ( GDT_Byte );
1123
1367
1124
1368
if ( bandNo <= 0 || bandNo > mGdalDataType .count () ) return Qgis::UnknownDataType;
@@ -1128,6 +1372,10 @@ Qgis::DataType QgsGdalProvider::dataType( int bandNo ) const
1128
1372
1129
1373
double QgsGdalProvider::bandScale ( int bandNo ) const
1130
1374
{
1375
+ QMutexLocker locker ( mpMutex );
1376
+ if ( !const_cast <QgsGdalProvider *>( this )->initIfNeeded () )
1377
+ return 1.0 ;
1378
+
1131
1379
GDALRasterBandH myGdalBand = getBand ( bandNo );
1132
1380
int bGotScale;
1133
1381
double myScale = GDALGetRasterScale ( myGdalBand, &bGotScale );
@@ -1139,6 +1387,10 @@ double QgsGdalProvider::bandScale( int bandNo ) const
1139
1387
1140
1388
double QgsGdalProvider::bandOffset ( int bandNo ) const
1141
1389
{
1390
+ QMutexLocker locker ( mpMutex );
1391
+ if ( !const_cast <QgsGdalProvider *>( this )->initIfNeeded () )
1392
+ return 0.0 ;
1393
+
1142
1394
GDALRasterBandH myGdalBand = getBand ( bandNo );
1143
1395
int bGotOffset;
1144
1396
double myOffset = GDALGetRasterOffset ( myGdalBand, &bGotOffset );
@@ -1150,14 +1402,15 @@ double QgsGdalProvider::bandOffset( int bandNo ) const
1150
1402
1151
1403
int QgsGdalProvider::bandCount () const
1152
1404
{
1153
- if ( mGdalDataset )
1154
- return GDALGetRasterCount ( mGdalDataset ) + ( mMaskBandExposedAsAlpha ? 1 : 0 );
1155
- else
1156
- return 1 ;
1405
+ return mBandCount ;
1157
1406
}
1158
1407
1159
1408
int QgsGdalProvider::colorInterpretation ( int bandNo ) const
1160
1409
{
1410
+ QMutexLocker locker ( mpMutex );
1411
+ if ( !const_cast <QgsGdalProvider *>( this )->initIfNeeded () )
1412
+ return colorInterpretationFromGdal ( GCI_Undefined );
1413
+
1161
1414
if ( mMaskBandExposedAsAlpha && bandNo == GDALGetRasterCount ( mGdalDataset ) + 1 )
1162
1415
return colorInterpretationFromGdal ( GCI_AlphaBand );
1163
1416
GDALRasterBandH myGdalBand = GDALGetRasterBand ( mGdalDataset , bandNo );
@@ -1231,6 +1484,10 @@ bool QgsGdalProvider::hasHistogram( int bandNo,
1231
1484
int sampleSize,
1232
1485
bool includeOutOfRange )
1233
1486
{
1487
+ QMutexLocker locker ( mpMutex );
1488
+ if ( !initIfNeeded () )
1489
+ return false ;
1490
+
1234
1491
QgsDebugMsg ( QString ( " theBandNo = %1 binCount = %2 minimum = %3 maximum = %4 sampleSize = %5" ).arg ( bandNo ).arg ( binCount ).arg ( minimum ).arg ( maximum ).arg ( sampleSize ) );
1235
1492
1236
1493
// First check if cached in mHistograms
@@ -1314,6 +1571,10 @@ QgsRasterHistogram QgsGdalProvider::histogram( int bandNo,
1314
1571
int sampleSize,
1315
1572
bool includeOutOfRange, QgsRasterBlockFeedback *feedback )
1316
1573
{
1574
+ QMutexLocker locker ( mpMutex );
1575
+ if ( !initIfNeeded () )
1576
+ return QgsRasterHistogram ();
1577
+
1317
1578
QgsDebugMsg ( QString ( " theBandNo = %1 binCount = %2 minimum = %3 maximum = %4 sampleSize = %5" ).arg ( bandNo ).arg ( binCount ).arg ( minimum ).arg ( maximum ).arg ( sampleSize ) );
1318
1579
1319
1580
QgsRasterHistogram myHistogram;
@@ -1467,6 +1728,8 @@ QString QgsGdalProvider::buildPyramids( const QList<QgsRasterPyramid> &rasterPyr
1467
1728
const QString &resamplingMethod, QgsRaster::RasterPyramidsFormat format,
1468
1729
const QStringList &configOptions, QgsRasterBlockFeedback *feedback )
1469
1730
{
1731
+ QMutexLocker locker ( mpMutex );
1732
+
1470
1733
// TODO: Consider making rasterPyramidList modifyable by this method to indicate if the pyramid exists after build attempt
1471
1734
// without requiring the user to rebuild the pyramid list to get the updated information
1472
1735
@@ -1766,6 +2029,8 @@ QList<QgsRasterPyramid> QgsGdalProvider::buildPyramidList()
1766
2029
1767
2030
QList<QgsRasterPyramid> QgsGdalProvider::buildPyramidList ( QList<int > overviewList )
1768
2031
{
2032
+ QMutexLocker locker ( mpMutex );
2033
+
1769
2034
int myWidth = mWidth ;
1770
2035
int myHeight = mHeight ;
1771
2036
GDALRasterBandH myGDALBand = GDALGetRasterBand ( mGdalDataset , 1 ); // just use the first band
@@ -2126,6 +2391,10 @@ bool QgsGdalProvider::hasStatistics( int bandNo,
2126
2391
const QgsRectangle &boundingBox,
2127
2392
int sampleSize )
2128
2393
{
2394
+ QMutexLocker locker ( mpMutex );
2395
+ if ( !initIfNeeded () )
2396
+ return false ;
2397
+
2129
2398
QgsDebugMsg ( QString ( " theBandNo = %1 sampleSize = %2" ).arg ( bandNo ).arg ( sampleSize ) );
2130
2399
2131
2400
// First check if cached in mStatistics
@@ -2208,6 +2477,10 @@ bool QgsGdalProvider::hasStatistics( int bandNo,
2208
2477
2209
2478
QgsRasterBandStats QgsGdalProvider::bandStatistics ( int bandNo, int stats, const QgsRectangle &boundingBox, int sampleSize, QgsRasterBlockFeedback *feedback )
2210
2479
{
2480
+ QMutexLocker locker ( mpMutex );
2481
+ if ( !initIfNeeded () )
2482
+ return QgsRasterBandStats ();
2483
+
2211
2484
QgsDebugMsg ( QString ( " theBandNo = %1 sampleSize = %2" ).arg ( bandNo ).arg ( sampleSize ) );
2212
2485
2213
2486
// TODO: null values set on raster layer!!!
@@ -2367,8 +2640,47 @@ QgsRasterBandStats QgsGdalProvider::bandStatistics( int bandNo, int stats, const
2367
2640
2368
2641
} // QgsGdalProvider::bandStatistics
2369
2642
2643
+ bool QgsGdalProvider::initIfNeeded ()
2644
+ {
2645
+ if ( mHasInit )
2646
+ return mValid ;
2647
+
2648
+ mHasInit = true ;
2649
+
2650
+ QString gdalUri = dataSourceUri ();
2651
+
2652
+ // Try to open using VSIFileHandler (see qgsogrprovider.cpp)
2653
+ QString vsiPrefix = QgsZipItem::vsiPrefix ( gdalUri );
2654
+ if ( !vsiPrefix.isEmpty () )
2655
+ {
2656
+ if ( !gdalUri.startsWith ( vsiPrefix ) )
2657
+ setDataSourceUri ( vsiPrefix + gdalUri );
2658
+ QgsDebugMsg ( QString ( " Trying %1 syntax, uri= %2" ).arg ( vsiPrefix, dataSourceUri () ) );
2659
+ }
2660
+
2661
+ gdalUri = dataSourceUri ();
2662
+
2663
+ CPLErrorReset ();
2664
+ mGdalBaseDataset = gdalOpen ( gdalUri.toUtf8 ().constData (), mUpdate ? GA_Update : GA_ReadOnly );
2665
+
2666
+ if ( !mGdalBaseDataset )
2667
+ {
2668
+ QString msg = QStringLiteral ( " Cannot open GDAL dataset %1:\n %2" ).arg ( dataSourceUri (), QString::fromUtf8 ( CPLGetLastErrorMsg () ) );
2669
+ appendError ( ERRMSG ( msg ) );
2670
+ return false ;
2671
+ }
2672
+
2673
+ QgsDebugMsg ( " GdalDataset opened" );
2674
+
2675
+ initBaseDataset ();
2676
+ return mValid ;
2677
+ }
2678
+
2679
+
2370
2680
void QgsGdalProvider::initBaseDataset ()
2371
2681
{
2682
+ mHasInit = true ;
2683
+ mValid = true ;
2372
2684
#if 0
2373
2685
for ( int i = 0; i < GDALGetRasterCount( mGdalBaseDataset ); i++ )
2374
2686
{
@@ -2441,13 +2753,15 @@ void QgsGdalProvider::initBaseDataset()
2441
2753
2442
2754
GDALClose ( mGdalDataset );
2443
2755
mGdalDataset = nullptr ;
2756
+ mValid = false ;
2444
2757
return ;
2445
2758
}
2446
2759
// if there are subdatasets, leave the dataset open for subsequent queries
2447
2760
else
2448
2761
{
2449
2762
QgsDebugMsg ( QObject::tr ( " Cannot get GDAL raster band: %1" ).arg ( msg ) +
2450
2763
QString ( " but dataset has %1 subdatasets" ).arg ( mSubLayers .size () ) );
2764
+ mValid = false ;
2451
2765
return ;
2452
2766
}
2453
2767
}
@@ -2503,13 +2817,22 @@ void QgsGdalProvider::initBaseDataset()
2503
2817
mWidth = GDALGetRasterXSize ( mGdalDataset );
2504
2818
mHeight = GDALGetRasterYSize ( mGdalDataset );
2505
2819
2820
+ // Check if the dataset has a mask band, that applies to the whole dataset
2821
+ // If so then expose it as an alpha band.
2822
+ int nMaskFlags = GDALGetMaskFlags ( myGDALBand );
2823
+ const int bandCount = GDALGetRasterCount ( mGdalDataset );
2824
+ if ( ( nMaskFlags == 0 && bandCount == 1 ) || nMaskFlags == GMF_PER_DATASET )
2825
+ {
2826
+ mMaskBandExposedAsAlpha = true ;
2827
+ }
2828
+
2829
+ mBandCount = bandCount + ( mMaskBandExposedAsAlpha ? 1 : 0 );
2506
2830
2507
2831
GDALGetBlockSize ( GDALGetRasterBand ( mGdalDataset , 1 ), &mXBlockSize , &mYBlockSize );
2508
2832
//
2509
2833
// Determine the nodata value and data type
2510
2834
//
2511
2835
// mValidNoDataValue = true;
2512
- const int bandCount = GDALGetRasterCount ( mGdalBaseDataset );
2513
2836
for ( int i = 1 ; i <= bandCount; i++ )
2514
2837
{
2515
2838
GDALRasterBandH myGdalBand = GDALGetRasterBand ( mGdalDataset , i );
@@ -2616,19 +2939,13 @@ void QgsGdalProvider::initBaseDataset()
2616
2939
// QgsDebugMsg( QString( "mInternalNoDataValue[%1] = %2" ).arg( i - 1 ).arg( mInternalNoDataValue[i-1] ) );
2617
2940
}
2618
2941
2619
- // Check if the dataset has a mask band, that applies to the whole dataset
2620
- // If so then expose it as an alpha band.
2621
- int nMaskFlags = GDALGetMaskFlags ( myGDALBand );
2622
- if ( ( nMaskFlags == 0 && bandCount == 1 ) || nMaskFlags == GMF_PER_DATASET )
2942
+ if ( mMaskBandExposedAsAlpha )
2623
2943
{
2624
- mMaskBandExposedAsAlpha = true ;
2625
2944
mSrcNoDataValue .append ( std::numeric_limits<double >::quiet_NaN () );
2626
2945
mSrcHasNoDataValue .append ( false );
2627
2946
mUseSrcNoDataValue .append ( false );
2628
2947
mGdalDataType .append ( GDT_Byte );
2629
2948
}
2630
-
2631
- mValid = true ;
2632
2949
}
2633
2950
2634
2951
char **papszFromStringList ( const QStringList &list )
@@ -2686,6 +3003,10 @@ QGISEXTERN QgsGdalProvider *create(
2686
3003
2687
3004
bool QgsGdalProvider::write ( void *data, int band, int width, int height, int xOffset, int yOffset )
2688
3005
{
3006
+ QMutexLocker locker ( mpMutex );
3007
+ if ( !initIfNeeded () )
3008
+ return false ;
3009
+
2689
3010
if ( !mGdalDataset )
2690
3011
{
2691
3012
return false ;
@@ -2700,6 +3021,10 @@ bool QgsGdalProvider::write( void *data, int band, int width, int height, int xO
2700
3021
2701
3022
bool QgsGdalProvider::setNoDataValue ( int bandNo, double noDataValue )
2702
3023
{
3024
+ QMutexLocker locker ( mpMutex );
3025
+ if ( !initIfNeeded () )
3026
+ return false ;
3027
+
2703
3028
if ( !mGdalDataset )
2704
3029
{
2705
3030
return false ;
@@ -2721,6 +3046,16 @@ bool QgsGdalProvider::setNoDataValue( int bandNo, double noDataValue )
2721
3046
2722
3047
bool QgsGdalProvider::remove ()
2723
3048
{
3049
+ QMutexLocker locker ( mpMutex );
3050
+ if ( !initIfNeeded () )
3051
+ return false ;
3052
+
3053
+ while ( *mpRefCounter != 1 )
3054
+ {
3055
+ QgsDebugMsg ( QString ( " Waiting for ref counter for %1 to drop to 1" ).arg ( dataSourceUri () ) );
3056
+ QThread::msleep ( 100 );
3057
+ }
3058
+
2724
3059
if ( mGdalDataset )
2725
3060
{
2726
3061
GDALDriverH driver = GDALGetDatasetDriver ( mGdalDataset );
@@ -2903,6 +3238,10 @@ bool QgsGdalProvider::isEditable() const
2903
3238
2904
3239
bool QgsGdalProvider::setEditable ( bool enabled )
2905
3240
{
3241
+ QMutexLocker locker ( mpMutex );
3242
+ if ( !initIfNeeded () )
3243
+ return false ;
3244
+
2906
3245
if ( enabled == mUpdate )
2907
3246
return false ;
2908
3247
@@ -2912,6 +3251,12 @@ bool QgsGdalProvider::setEditable( bool enabled )
2912
3251
if ( mGdalDataset != mGdalBaseDataset )
2913
3252
return false ; // ignore the case of warped VRT for now (more complicated setup)
2914
3253
3254
+ while ( *mpRefCounter != 1 )
3255
+ {
3256
+ QgsDebugMsg ( QString ( " Waiting for ref counter for %1 to drop to 1" ).arg ( dataSourceUri () ) );
3257
+ QThread::msleep ( 100 );
3258
+ }
3259
+
2915
3260
closeDataset ();
2916
3261
2917
3262
mUpdate = enabled;
@@ -2933,6 +3278,10 @@ bool QgsGdalProvider::setEditable( bool enabled )
2933
3278
2934
3279
GDALRasterBandH QgsGdalProvider::getBand ( int bandNo ) const
2935
3280
{
3281
+ QMutexLocker locker ( mpMutex );
3282
+ if ( !const_cast <QgsGdalProvider *>( this )->initIfNeeded () )
3283
+ return nullptr ;
3284
+
2936
3285
if ( mMaskBandExposedAsAlpha && bandNo == GDALGetRasterCount ( mGdalDataset ) + 1 )
2937
3286
return GDALGetMaskBand ( GDALGetRasterBand ( mGdalDataset , 1 ) );
2938
3287
else
0 commit comments