QGIS API Documentation 3.27.0-Master (f261cc1f8b)
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
35bool 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
49{
50 return createMultiBandMemoryDataset( dataType, 1, extent, width, height, crs );
51}
52
53gdal::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 {
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
78gdal::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 {
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 {
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
153gdal::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
187bool 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
224QImage 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
275QString 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
304char **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
315QString 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
333GDALDatasetH 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
493void *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
557bool 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
669bool 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:140
std::unique_ptr< GDALWarpOptions, GDALWarpOptionsDeleter > warp_options_unique_ptr
Scoped GDAL warp options.
Definition: qgsogrutils.h:155
void * GDALDatasetH
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs