QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgsgdalutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgdalutils.cpp
3  ----------------
4  begin : September 2018
5  copyright : (C) 2018 Even Rouault
6  email : even.rouault at spatialys.com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsgdalutils.h"
17 #include "qgslogger.h"
19 #include "qgssettings.h"
21 
22 #define CPL_SUPRESS_CPLUSPLUS //#spellok
23 #include "gdal.h"
24 #include "gdalwarper.h"
25 #include "cpl_string.h"
26 #include "qgsapplication.h"
27 
28 #include <QNetworkProxy>
29 #include <QString>
30 #include <QImage>
31 #include <QFileInfo>
32 #include <mutex>
33 
34 bool QgsGdalUtils::supportsRasterCreate( GDALDriverH driver )
35 {
36  const QString driverShortName = GDALGetDriverShortName( driver );
37  if ( driverShortName == QLatin1String( "SQLite" ) )
38  {
39  // it supports Create() but only for vector side
40  return false;
41  }
42  char **driverMetadata = GDALGetMetadata( driver, nullptr );
43  return CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) &&
44  CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false );
45 }
46 
47 gdal::dataset_unique_ptr QgsGdalUtils::createSingleBandMemoryDataset( GDALDataType dataType, const QgsRectangle &extent, int width, int height, const QgsCoordinateReferenceSystem &crs )
48 {
49  return createMultiBandMemoryDataset( dataType, 1, extent, width, height, crs );
50 }
51 
52 gdal::dataset_unique_ptr QgsGdalUtils::createMultiBandMemoryDataset( GDALDataType dataType, int bands, const QgsRectangle &extent, int width, int height, const QgsCoordinateReferenceSystem &crs )
53 {
54  GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
55  if ( !hDriverMem )
56  {
57  return gdal::dataset_unique_ptr();
58  }
59 
60  gdal::dataset_unique_ptr hSrcDS( GDALCreate( hDriverMem, "", width, height, bands, dataType, nullptr ) );
61 
62  const double cellSizeX = extent.width() / width;
63  const double cellSizeY = extent.height() / height;
64  double geoTransform[6];
65  geoTransform[0] = extent.xMinimum();
66  geoTransform[1] = cellSizeX;
67  geoTransform[2] = 0;
68  geoTransform[3] = extent.yMinimum() + ( cellSizeY * height );
69  geoTransform[4] = 0;
70  geoTransform[5] = -cellSizeY;
71 
72  GDALSetProjection( hSrcDS.get(), crs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).toLatin1().constData() );
73  GDALSetGeoTransform( hSrcDS.get(), geoTransform );
74  return hSrcDS;
75 }
76 
77 gdal::dataset_unique_ptr QgsGdalUtils::createSingleBandTiffDataset( const QString &filename, GDALDataType dataType, const QgsRectangle &extent, int width, int height, const QgsCoordinateReferenceSystem &crs )
78 {
79  const double cellSizeX = extent.width() / width;
80  const double cellSizeY = extent.height() / height;
81  double geoTransform[6];
82  geoTransform[0] = extent.xMinimum();
83  geoTransform[1] = cellSizeX;
84  geoTransform[2] = 0;
85  geoTransform[3] = extent.yMinimum() + ( cellSizeY * height );
86  geoTransform[4] = 0;
87  geoTransform[5] = -cellSizeY;
88 
89  GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
90  if ( !hDriver )
91  {
92  return gdal::dataset_unique_ptr();
93  }
94 
95  // Create the output file.
96  gdal::dataset_unique_ptr hDstDS( GDALCreate( hDriver, filename.toLocal8Bit().constData(), width, height, 1, dataType, nullptr ) );
97  if ( !hDstDS )
98  {
99  return gdal::dataset_unique_ptr();
100  }
101 
102  // Write out the projection definition.
103  GDALSetProjection( hDstDS.get(), crs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).toLatin1().constData() );
104  GDALSetGeoTransform( hDstDS.get(), geoTransform );
105  return hDstDS;
106 }
107 
109 {
110  if ( image.isNull() )
111  return nullptr;
112 
113  const QRgb *rgb = reinterpret_cast<const QRgb *>( image.constBits() );
114  GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
115  if ( !hDriverMem )
116  {
117  return nullptr;
118  }
119  gdal::dataset_unique_ptr hSrcDS( GDALCreate( hDriverMem, "", image.width(), image.height(), 0, GDT_Byte, nullptr ) );
120 
121  char **papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
122  << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
123  << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
124  << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) + 2 ) );
125  GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
126  CSLDestroy( papszOptions );
127 
128  papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
129  << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
130  << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
131  << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) + 1 ) );
132  GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
133  CSLDestroy( papszOptions );
134 
135  papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
136  << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
137  << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
138  << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) ) );
139  GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
140  CSLDestroy( papszOptions );
141 
142  papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
143  << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
144  << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
145  << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) + 3 ) );
146  GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
147  CSLDestroy( papszOptions );
148 
149  return hSrcDS;
150 }
151 
152 bool QgsGdalUtils::resampleSingleBandRaster( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, GDALResampleAlg resampleAlg, const char *pszCoordinateOperation )
153 {
154  gdal::warp_options_unique_ptr psWarpOptions( GDALCreateWarpOptions() );
155  psWarpOptions->hSrcDS = hSrcDS;
156  psWarpOptions->hDstDS = hDstDS;
157 
158  psWarpOptions->nBandCount = 1;
159  psWarpOptions->panSrcBands = reinterpret_cast< int * >( CPLMalloc( sizeof( int ) * 1 ) );
160  psWarpOptions->panDstBands = reinterpret_cast< int * >( CPLMalloc( sizeof( int ) * 1 ) );
161  psWarpOptions->panSrcBands[0] = 1;
162  psWarpOptions->panDstBands[0] = 1;
163 
164  psWarpOptions->eResampleAlg = resampleAlg;
165 
166  // Establish reprojection transformer.
167  char **papszOptions = nullptr;
168  if ( pszCoordinateOperation != nullptr )
169  papszOptions = CSLSetNameValue( papszOptions, "COORDINATE_OPERATION", pszCoordinateOperation );
170  psWarpOptions->pTransformerArg = GDALCreateGenImgProjTransformer2( hSrcDS, hDstDS, papszOptions );
171  CSLDestroy( papszOptions );
172 
173  if ( ! psWarpOptions->pTransformerArg )
174  {
175  return false;
176  }
177 
178  psWarpOptions->pfnTransformer = GDALGenImgProjTransform;
179 
180  // Initialize and execute the warp operation.
181  GDALWarpOperation oOperation;
182  oOperation.Initialize( psWarpOptions.get() );
183 
184  const bool retVal { oOperation.ChunkAndWarpImage( 0, 0, GDALGetRasterXSize( hDstDS ), GDALGetRasterYSize( hDstDS ) ) == CE_None };
185  GDALDestroyGenImgProjTransformer( psWarpOptions->pTransformerArg );
186  return retVal;
187 }
188 
189 QImage QgsGdalUtils::resampleImage( const QImage &image, QSize outputSize, GDALRIOResampleAlg resampleAlg )
190 {
192  if ( !srcDS )
193  return QImage();
194 
195  GDALRasterIOExtraArg extra;
196  INIT_RASTERIO_EXTRA_ARG( extra );
197  extra.eResampleAlg = resampleAlg;
198 
199  QImage res( outputSize, image.format() );
200  if ( res.isNull() )
201  return QImage();
202 
203  GByte *rgb = reinterpret_cast<GByte *>( res.bits() );
204 
205  CPLErr err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 1 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 2, outputSize.width(),
206  outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
207  if ( err != CE_None )
208  {
209  QgsDebugMsg( QStringLiteral( "failed to read red band" ) );
210  return QImage();
211  }
212 
213  err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 2 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 1, outputSize.width(),
214  outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
215  if ( err != CE_None )
216  {
217  QgsDebugMsg( QStringLiteral( "failed to read green band" ) );
218  return QImage();
219  }
220 
221  err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 3 ), GF_Read, 0, 0, image.width(), image.height(), rgb, outputSize.width(),
222  outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
223  if ( err != CE_None )
224  {
225  QgsDebugMsg( QStringLiteral( "failed to read blue band" ) );
226  return QImage();
227  }
228 
229  err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 4 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 3, outputSize.width(),
230  outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
231  if ( err != CE_None )
232  {
233  QgsDebugMsg( QStringLiteral( "failed to read alpha band" ) );
234  return QImage();
235  }
236 
237  return res;
238 }
239 
240 QString QgsGdalUtils::helpCreationOptionsFormat( const QString &format )
241 {
242  QString message;
243  GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
244  if ( myGdalDriver )
245  {
246  // first report details and help page
247  char **GDALmetadata = GDALGetMetadata( myGdalDriver, nullptr );
248  message += QLatin1String( "Format Details:\n" );
249  message += QStringLiteral( " Extension: %1\n" ).arg( CSLFetchNameValue( GDALmetadata, GDAL_DMD_EXTENSION ) );
250  message += QStringLiteral( " Short Name: %1" ).arg( GDALGetDriverShortName( myGdalDriver ) );
251  message += QStringLiteral( " / Long Name: %1\n" ).arg( GDALGetDriverLongName( myGdalDriver ) );
252  message += QStringLiteral( " Help page: http://www.gdal.org/%1\n\n" ).arg( CSLFetchNameValue( GDALmetadata, GDAL_DMD_HELPTOPIC ) );
253 
254  // next get creation options
255  // need to serialize xml to get newlines, should we make the basic xml prettier?
256  CPLXMLNode *psCOL = CPLParseXMLString( GDALGetMetadataItem( myGdalDriver,
257  GDAL_DMD_CREATIONOPTIONLIST, "" ) );
258  char *pszFormattedXML = CPLSerializeXMLTree( psCOL );
259  if ( pszFormattedXML )
260  message += QString( pszFormattedXML );
261  if ( psCOL )
262  CPLDestroyXMLNode( psCOL );
263  if ( pszFormattedXML )
264  CPLFree( pszFormattedXML );
265  }
266  return message;
267 }
268 
269 char **QgsGdalUtils::papszFromStringList( const QStringList &list )
270 {
271  char **papszRetList = nullptr;
272  const auto constList = list;
273  for ( const QString &elem : constList )
274  {
275  papszRetList = CSLAddString( papszRetList, elem.toLocal8Bit().constData() );
276  }
277  return papszRetList;
278 }
279 
280 QString QgsGdalUtils::validateCreationOptionsFormat( const QStringList &createOptions, const QString &format )
281 {
282  GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
283  if ( ! myGdalDriver )
284  return QStringLiteral( "invalid GDAL driver" );
285 
286  char **papszOptions = papszFromStringList( createOptions );
287  // get error string?
288  const int ok = GDALValidateCreationOptions( myGdalDriver, papszOptions );
289  CSLDestroy( papszOptions );
290 
291  if ( !ok )
292  return QStringLiteral( "Failed GDALValidateCreationOptions() test" );
293  return QString();
294 }
295 
296 #if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,2,0)
297 
298 GDALDatasetH GDALAutoCreateWarpedVRTEx( GDALDatasetH hSrcDS, const char *pszSrcWKT, const char *pszDstWKT, GDALResampleAlg eResampleAlg,
299  double dfMaxError, const GDALWarpOptions *psOptionsIn, char **papszTransformerOptions )
300 {
301  VALIDATE_POINTER1( hSrcDS, "GDALAutoCreateWarpedVRT", nullptr );
302 
303  /* -------------------------------------------------------------------- */
304  /* Populate the warp options. */
305  /* -------------------------------------------------------------------- */
306  GDALWarpOptions *psWO = nullptr;
307  if ( psOptionsIn != nullptr )
308  psWO = GDALCloneWarpOptions( psOptionsIn );
309  else
310  psWO = GDALCreateWarpOptions();
311 
312  psWO->eResampleAlg = eResampleAlg;
313 
314  psWO->hSrcDS = hSrcDS;
315 
316  GDALWarpInitDefaultBandMapping( psWO, GDALGetRasterCount( hSrcDS ) );
317 
318  /* -------------------------------------------------------------------- */
319  /* Setup no data values */
320  /* -------------------------------------------------------------------- */
321  for ( int i = 0; i < psWO->nBandCount; i++ )
322  {
323  GDALRasterBandH rasterBand = GDALGetRasterBand( psWO->hSrcDS, psWO->panSrcBands[i] );
324 
325  int hasNoDataValue;
326  double noDataValue = GDALGetRasterNoDataValue( rasterBand, &hasNoDataValue );
327 
328  if ( hasNoDataValue )
329  {
330  // Check if the nodata value is out of range
331  int bClamped = FALSE;
332  int bRounded = FALSE;
333  CPL_IGNORE_RET_VAL(
334  GDALAdjustValueToDataType( GDALGetRasterDataType( rasterBand ),
335  noDataValue, &bClamped, &bRounded ) );
336  if ( !bClamped )
337  {
338  GDALWarpInitNoDataReal( psWO, -1e10 );
339 
340  psWO->padfSrcNoDataReal[i] = noDataValue;
341  psWO->padfDstNoDataReal[i] = noDataValue;
342  }
343  }
344  }
345 
346  if ( psWO->padfDstNoDataReal != nullptr )
347  {
348  if ( CSLFetchNameValue( psWO->papszWarpOptions, "INIT_DEST" ) == nullptr )
349  {
350  psWO->papszWarpOptions =
351  CSLSetNameValue( psWO->papszWarpOptions, "INIT_DEST", "NO_DATA" );
352  }
353  }
354 
355  /* -------------------------------------------------------------------- */
356  /* Create the transformer. */
357  /* -------------------------------------------------------------------- */
358  psWO->pfnTransformer = GDALGenImgProjTransform;
359 
360  char **papszOptions = nullptr;
361  if ( pszSrcWKT != nullptr )
362  papszOptions = CSLSetNameValue( papszOptions, "SRC_SRS", pszSrcWKT );
363  if ( pszDstWKT != nullptr )
364  papszOptions = CSLSetNameValue( papszOptions, "DST_SRS", pszDstWKT );
365  papszOptions = CSLMerge( papszOptions, papszTransformerOptions );
366  psWO->pTransformerArg =
367  GDALCreateGenImgProjTransformer2( psWO->hSrcDS, nullptr,
368  papszOptions );
369  CSLDestroy( papszOptions );
370 
371  if ( psWO->pTransformerArg == nullptr )
372  {
373  GDALDestroyWarpOptions( psWO );
374  return nullptr;
375  }
376 
377  /* -------------------------------------------------------------------- */
378  /* Figure out the desired output bounds and resolution. */
379  /* -------------------------------------------------------------------- */
380  double adfDstGeoTransform[6] = { 0.0 };
381  int nDstPixels = 0;
382  int nDstLines = 0;
383  CPLErr eErr =
384  GDALSuggestedWarpOutput( hSrcDS, psWO->pfnTransformer,
385  psWO->pTransformerArg,
386  adfDstGeoTransform, &nDstPixels, &nDstLines );
387  if ( eErr != CE_None )
388  {
389  GDALDestroyTransformer( psWO->pTransformerArg );
390  GDALDestroyWarpOptions( psWO );
391  return nullptr;
392  }
393 
394  /* -------------------------------------------------------------------- */
395  /* Update the transformer to include an output geotransform */
396  /* back to pixel/line coordinates. */
397  /* */
398  /* -------------------------------------------------------------------- */
399  GDALSetGenImgProjTransformerDstGeoTransform(
400  psWO->pTransformerArg, adfDstGeoTransform );
401 
402  /* -------------------------------------------------------------------- */
403  /* Do we want to apply an approximating transformation? */
404  /* -------------------------------------------------------------------- */
405  if ( dfMaxError > 0.0 )
406  {
407  psWO->pTransformerArg =
408  GDALCreateApproxTransformer( psWO->pfnTransformer,
409  psWO->pTransformerArg,
410  dfMaxError );
411  psWO->pfnTransformer = GDALApproxTransform;
412  GDALApproxTransformerOwnsSubtransformer( psWO->pTransformerArg, TRUE );
413  }
414 
415  /* -------------------------------------------------------------------- */
416  /* Create the VRT file. */
417  /* -------------------------------------------------------------------- */
418  GDALDatasetH hDstDS
419  = GDALCreateWarpedVRT( hSrcDS, nDstPixels, nDstLines,
420  adfDstGeoTransform, psWO );
421 
422  GDALDestroyWarpOptions( psWO );
423 
424  if ( pszDstWKT != nullptr )
425  GDALSetProjection( hDstDS, pszDstWKT );
426  else if ( pszSrcWKT != nullptr )
427  GDALSetProjection( hDstDS, pszSrcWKT );
428  else if ( GDALGetGCPCount( hSrcDS ) > 0 )
429  GDALSetProjection( hDstDS, GDALGetGCPProjection( hSrcDS ) );
430  else
431  GDALSetProjection( hDstDS, GDALGetProjectionRef( hSrcDS ) );
432 
433  return hDstDS;
434 }
435 #endif
436 
437 
439  GDALDatasetH hSrcDS,
440  const char *pszSrcWKT,
441  const char *pszDstWKT,
442  GDALResampleAlg eResampleAlg,
443  double dfMaxError,
444  const GDALWarpOptions *psOptionsIn )
445 {
446  char **opts = nullptr;
447  if ( GDALGetMetadata( hSrcDS, "RPC" ) )
448  {
449  // well-behaved RPC should have height offset a good value for RPC_HEIGHT
450  const char *heightOffStr = GDALGetMetadataItem( hSrcDS, "HEIGHT_OFF", "RPC" );
451  if ( heightOffStr )
452  opts = CSLAddNameValue( opts, "RPC_HEIGHT", heightOffStr );
453  }
454 
455  return GDALAutoCreateWarpedVRTEx( hSrcDS, pszSrcWKT, pszDstWKT, eResampleAlg, dfMaxError, psOptionsIn, opts );
456 }
457 
458 void *QgsGdalUtils::rpcAwareCreateTransformer( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, char **papszOptions )
459 {
460  char **opts = CSLDuplicate( papszOptions );
461  if ( GDALGetMetadata( hSrcDS, "RPC" ) )
462  {
463  // well-behaved RPC should have height offset a good value for RPC_HEIGHT
464  const char *heightOffStr = GDALGetMetadataItem( hSrcDS, "HEIGHT_OFF", "RPC" );
465  if ( heightOffStr )
466  opts = CSLAddNameValue( opts, "RPC_HEIGHT", heightOffStr );
467  }
468  void *transformer = GDALCreateGenImgProjTransformer2( hSrcDS, hDstDS, opts );
469  CSLDestroy( opts );
470  return transformer;
471 }
472 
473 #ifndef QT_NO_NETWORKPROXY
475 {
476  // Check proxy configuration, they are application level but
477  // instead of adding an API and complex signal/slot connections
478  // given the limited cost of checking them on every provider instantiation
479  // we can do it here so that new settings are applied whenever a new layer
480  // is created.
481  const QgsSettings settings;
482  // Check that proxy is enabled
483  if ( settings.value( QStringLiteral( "proxy/proxyEnabled" ), false ).toBool() )
484  {
485  // Get the first configured proxy
486  QList<QNetworkProxy> proxies( QgsNetworkAccessManager::instance()->proxyFactory()->queryProxy( ) );
487  if ( ! proxies.isEmpty() )
488  {
489  const QNetworkProxy proxy( proxies.first() );
490  // TODO/FIXME: check excludes (the GDAL config options are global, we need a per-connection config option)
491  //QStringList excludes;
492  //excludes = settings.value( QStringLiteral( "proxy/proxyExcludedUrls" ), "" ).toStringList();
493 
494  const QString proxyHost( proxy.hostName() );
495  const qint16 proxyPort( proxy.port() );
496 
497  const QString proxyUser( proxy.user() );
498  const QString proxyPassword( proxy.password() );
499 
500  if ( ! proxyHost.isEmpty() )
501  {
502  QString connection( proxyHost );
503  if ( proxyPort )
504  {
505  connection += ':' + QString::number( proxyPort );
506  }
507  CPLSetConfigOption( "GDAL_HTTP_PROXY", connection.toUtf8() );
508  if ( ! proxyUser.isEmpty( ) )
509  {
510  QString credentials( proxyUser );
511  if ( ! proxyPassword.isEmpty( ) )
512  {
513  credentials += ':' + proxyPassword;
514  }
515  CPLSetConfigOption( "GDAL_HTTP_PROXYUSERPWD", credentials.toUtf8() );
516  }
517  }
518  }
519  }
520 }
521 
522 bool QgsGdalUtils::pathIsCheapToOpen( const QString &path, int smallFileSizeLimit )
523 {
524  const QFileInfo info( path );
525  const long long size = info.size();
526 
527  // if size could not be determined, safest to flag path as expensive
528  if ( size == 0 )
529  return false;
530 
531  const QString suffix = info.suffix().toLower();
532  static const QStringList sFileSizeDependentExtensions
533  {
534  QStringLiteral( "xlsx" ),
535  QStringLiteral( "ods" ),
536  QStringLiteral( "csv" )
537  };
538  if ( sFileSizeDependentExtensions.contains( suffix ) )
539  {
540  // path corresponds to a file type which is only cheap to open for small files
541  return size < smallFileSizeLimit;
542  }
543 
544  // treat all other formats as expensive.
545  // TODO -- flag formats which only require a quick header parse as cheap
546  return false;
547 }
548 
550 {
551 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,4,0)
552  // get supported extensions
553  static std::once_flag initialized;
554  static QStringList SUPPORTED_DB_LAYERS_EXTENSIONS;
555  std::call_once( initialized, [ = ]
556  {
557  // iterate through all of the supported drivers, adding the corresponding file extensions for
558  // types which advertise multilayer support
559  GDALDriverH driver = nullptr;
560 
561  QSet< QString > extensions;
562 
563  for ( int i = 0; i < GDALGetDriverCount(); ++i )
564  {
565  driver = GDALGetDriver( i );
566  if ( !driver )
567  {
568  QgsLogger::warning( "unable to get driver " + QString::number( i ) );
569  continue;
570  }
571 
572  bool isMultiLayer = false;
573  if ( QString( GDALGetMetadataItem( driver, GDAL_DCAP_RASTER, nullptr ) ) == QLatin1String( "YES" ) )
574  {
575  if ( GDALGetMetadataItem( driver, GDAL_DMD_SUBDATASETS, nullptr ) != nullptr )
576  {
577  isMultiLayer = true;
578  }
579  }
580  if ( !isMultiLayer && QString( GDALGetMetadataItem( driver, GDAL_DCAP_VECTOR, nullptr ) ) == QLatin1String( "YES" ) )
581  {
582  if ( GDALGetMetadataItem( driver, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, nullptr ) != nullptr )
583  {
584  isMultiLayer = true;
585  }
586  }
587 
588  if ( !isMultiLayer )
589  continue;
590 
591  const QString driverExtensions = GDALGetMetadataItem( driver, GDAL_DMD_EXTENSIONS, "" );
592  if ( driverExtensions.isEmpty() )
593  continue;
594 
595 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
596  const QStringList splitExtensions = driverExtensions.split( ' ', QString::SkipEmptyParts );
597 #else
598  const QStringList splitExtensions = driverExtensions.split( ' ', Qt::SkipEmptyParts );
599 #endif
600 
601  for ( const QString &ext : splitExtensions )
602  extensions.insert( ext );
603  }
604 
605  SUPPORTED_DB_LAYERS_EXTENSIONS = qgis::setToList( extensions );
606  } );
607  return SUPPORTED_DB_LAYERS_EXTENSIONS;
608 
609 #else
610  static const QStringList SUPPORTED_DB_LAYERS_EXTENSIONS
611  {
612  QStringLiteral( "gpkg" ),
613  QStringLiteral( "sqlite" ),
614  QStringLiteral( "db" ),
615  QStringLiteral( "gdb" ),
616  QStringLiteral( "kml" ),
617  QStringLiteral( "kmz" ),
618  QStringLiteral( "osm" ),
619  QStringLiteral( "mdb" ),
620  QStringLiteral( "accdb" ),
621  QStringLiteral( "xls" ),
622  QStringLiteral( "xlsx" ),
623  QStringLiteral( "ods" ),
624  QStringLiteral( "gpx" ),
625  QStringLiteral( "pdf" ),
626  QStringLiteral( "pbf" ),
627  QStringLiteral( "vrt" ),
628  QStringLiteral( "nc" ),
629  QStringLiteral( "shp.zip" ) };
630  return SUPPORTED_DB_LAYERS_EXTENSIONS;
631 #endif
632 }
633 
634 bool QgsGdalUtils::vrtMatchesLayerType( const QString &vrtPath, QgsMapLayerType type )
635 {
636  CPLPushErrorHandler( CPLQuietErrorHandler );
637  CPLErrorReset();
638  GDALDriverH hDriver = nullptr;
639 
640  switch ( type )
641  {
643  hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr );
644  break;
645 
647  hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_RASTER, nullptr, nullptr );
648  break;
649 
656  break;
657  }
658 
659  CPLPopErrorHandler();
660  return static_cast< bool >( hDriver );
661 }
662 #endif
This class represents a coordinate reference system (CRS).
@ WKT_PREFERRED_GDAL
Preferred format for conversion of CRS to WKT for use with the GDAL library.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
static bool pathIsCheapToOpen(const QString &path, int smallFileSizeLimit=50000)
Returns true if the dataset at the specified path is considered "cheap" to open.
static QString helpCreationOptionsFormat(const QString &format)
Gets creation options metadata for a given format.
static bool resampleSingleBandRaster(GDALDatasetH hSrcDS, GDALDatasetH hDstDS, GDALResampleAlg resampleAlg, const char *pszCoordinateOperation)
Resamples a single band raster to the destination dataset with different resolution (and possibly wit...
static GDALDatasetH rpcAwareAutoCreateWarpedVrt(GDALDatasetH hSrcDS, const char *pszSrcWKT, const char *pszDstWKT, GDALResampleAlg eResampleAlg, double dfMaxError, const GDALWarpOptions *psOptionsIn)
This is a copy of GDALAutoCreateWarpedVRT optimized for imagery using RPC georeferencing that also se...
static bool supportsRasterCreate(GDALDriverH driver)
Reads whether a driver supports GDALCreate() for raster purposes.
static gdal::dataset_unique_ptr createSingleBandTiffDataset(const QString &filename, GDALDataType dataType, const QgsRectangle &extent, int width, int height, const QgsCoordinateReferenceSystem &crs)
Creates a new single band TIFF dataset with given parameters.
static gdal::dataset_unique_ptr createSingleBandMemoryDataset(GDALDataType dataType, const QgsRectangle &extent, int width, int height, const QgsCoordinateReferenceSystem &crs)
Creates a new single band memory dataset with given parameters.
static QString validateCreationOptionsFormat(const QStringList &createOptions, const QString &format)
Validates creation options for a given format, regardless of layer.
static gdal::dataset_unique_ptr imageToMemoryDataset(const QImage &image)
Converts an image to a GDAL memory dataset by borrowing image data.
static void * rpcAwareCreateTransformer(GDALDatasetH hSrcDS, GDALDatasetH hDstDS=nullptr, char **papszOptions=nullptr)
This is a wrapper around GDALCreateGenImgProjTransformer2() that takes into account RPC georeferencin...
static void setupProxy()
Sets the gdal proxy variables.
static bool vrtMatchesLayerType(const QString &vrtPath, QgsMapLayerType type)
Returns true if the VRT file at the specified path is a VRT matching the given layer type.
static char ** papszFromStringList(const QStringList &list)
Helper function.
static QImage resampleImage(const QImage &image, QSize outputSize, GDALRIOResampleAlg resampleAlg)
Resamples a QImage image using GDAL resampler.
static gdal::dataset_unique_ptr createMultiBandMemoryDataset(GDALDataType dataType, int bands, const QgsRectangle &extent, int width, int height, const QgsCoordinateReferenceSystem &crs)
Creates a new multi band memory dataset with given parameters.
static QStringList multiLayerFileExtensions()
Returns a list of file extensions which potentially contain multiple layers representing GDAL raster ...
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:47
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
Definition: qgsogrutils.h:138
std::unique_ptr< GDALWarpOptions, GDALWarpOptionsDeleter > warp_options_unique_ptr
Scoped GDAL warp options.
Definition: qgsogrutils.h:153
void * GDALDatasetH
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs