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