QGIS API Documentation 3.43.0-Master (3ee7834ace6)
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#include "qgsmessagelog.h"
23
24#define CPL_SUPRESS_CPLUSPLUS //#spellok
25#include "gdal.h"
26#include "gdalwarper.h"
27#include "cpl_string.h"
28
29#include <QNetworkProxy>
30#include <QString>
31#include <QImage>
32#include <QFileInfo>
33#include <mutex>
34
35
37{
38 if ( node->eType != CXT_Element || !EQUAL( node->pszValue, "Option" ) )
39 return {};
40
41 const QString optionName( CPLGetXMLValue( node, "name", nullptr ) );
42 if ( optionName.isEmpty() )
43 return {};
44
45 QgsGdalOption option;
46 option.name = optionName;
47
48 option.description = QString( CPLGetXMLValue( node, "description", nullptr ) );
49 option.scope = QString( CPLGetXMLValue( node, "scope", nullptr ) );
50
52
53 const char *pszType = CPLGetXMLValue( node, "type", nullptr );
54 const char *pszDefault = CPLGetXMLValue( node, "default", nullptr );
55 if ( pszType && EQUAL( pszType, "string-select" ) )
56 {
58 for ( auto psOption = node->psChild; psOption != nullptr; psOption = psOption->psNext )
59 {
60 if ( psOption->eType != CXT_Element ||
61 !EQUAL( psOption->pszValue, "Value" ) ||
62 !psOption->psChild )
63 {
64 continue;
65 }
66 option.options << psOption->psChild->pszValue;
67 }
68 option.defaultValue = pszDefault ? QString( pszDefault ) : option.options.value( 0 );
69 return option;
70 }
71 else if ( pszType && EQUAL( pszType, "boolean" ) )
72 {
74 option.defaultValue = pszDefault ? QString( pszDefault ) : QStringLiteral( "YES" );
75 return option;
76 }
77 else if ( pszType && EQUAL( pszType, "string" ) )
78 {
80 if ( pszDefault )
81 option.defaultValue = QString( pszDefault );
82 return option;
83 }
84 else if ( pszType && ( EQUAL( pszType, "int" ) || EQUAL( pszType, "integer" ) ) )
85 {
87 if ( pszDefault )
88 {
89 bool ok = false;
90 const int defaultInt = QString( pszDefault ).toInt( &ok );
91 if ( ok )
92 option.defaultValue = defaultInt;
93 }
94
95 if ( const char *pszMin = CPLGetXMLValue( node, "min", nullptr ) )
96 {
97 bool ok = false;
98 const int minInt = QString( pszMin ).toInt( &ok );
99 if ( ok )
100 option.minimum = minInt;
101 }
102 if ( const char *pszMax = CPLGetXMLValue( node, "max", nullptr ) )
103 {
104 bool ok = false;
105 const int maxInt = QString( pszMax ).toInt( &ok );
106 if ( ok )
107 option.maximum = maxInt;
108 }
109 return option;
110 }
111 else if ( pszType && ( EQUAL( pszType, "double" ) || EQUAL( pszType, "float" ) ) )
112 {
114 if ( pszDefault )
115 {
116 bool ok = false;
117 const double defaultDouble = QString( pszDefault ).toDouble( &ok );
118 if ( ok )
119 option.defaultValue = defaultDouble;
120 }
121
122 if ( const char *pszMin = CPLGetXMLValue( node, "min", nullptr ) )
123 {
124 bool ok = false;
125 const double minDouble = QString( pszMin ).toDouble( &ok );
126 if ( ok )
127 option.minimum = minDouble;
128 }
129 if ( const char *pszMax = CPLGetXMLValue( node, "max", nullptr ) )
130 {
131 bool ok = false;
132 const double maxDouble = QString( pszMax ).toDouble( &ok );
133 if ( ok )
134 option.maximum = maxDouble;
135 }
136 return option;
137 }
138
139 QgsDebugError( QStringLiteral( "Unhandled GDAL option type: %1" ).arg( pszType ) );
140 return {};
141}
142
143QList<QgsGdalOption> QgsGdalOption::optionsFromXml( const CPLXMLNode *node )
144{
145 QList< QgsGdalOption > options;
146 for ( auto psItem = node->psChild; psItem != nullptr; psItem = psItem->psNext )
147 {
148 const QgsGdalOption option = fromXmlNode( psItem );
149 if ( option.type == QgsGdalOption::Type::Invalid )
150 continue;
151
152 options << option;
153 }
154 return options;
155}
156
157
158//
159// QgsGdalUtils
160//
161
162bool QgsGdalUtils::supportsRasterCreate( GDALDriverH driver )
163{
164 const QString driverShortName = GDALGetDriverShortName( driver );
165 if ( driverShortName == QLatin1String( "SQLite" ) ||
166 driverShortName == QLatin1String( "PDF" ) )
167 {
168 // it supports Create() but only for vector side
169 return false;
170 }
171 return GDALGetMetadataItem( driver, GDAL_DCAP_CREATE, nullptr ) &&
172 GDALGetMetadataItem( driver, GDAL_DCAP_RASTER, nullptr );
173}
174
176{
177 return createMultiBandMemoryDataset( dataType, 1, extent, width, height, crs );
178}
179
180gdal::dataset_unique_ptr QgsGdalUtils::createMultiBandMemoryDataset( GDALDataType dataType, int bands, const QgsRectangle &extent, int width, int height, const QgsCoordinateReferenceSystem &crs )
181{
182 GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
183 if ( !hDriverMem )
184 {
186 }
187
188 gdal::dataset_unique_ptr hSrcDS( GDALCreate( hDriverMem, "", width, height, bands, dataType, nullptr ) );
189
190 const double cellSizeX = extent.width() / width;
191 const double cellSizeY = extent.height() / height;
192 double geoTransform[6];
193 geoTransform[0] = extent.xMinimum();
194 geoTransform[1] = cellSizeX;
195 geoTransform[2] = 0;
196 geoTransform[3] = extent.yMinimum() + ( cellSizeY * height );
197 geoTransform[4] = 0;
198 geoTransform[5] = -cellSizeY;
199
200 GDALSetProjection( hSrcDS.get(), crs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toLatin1().constData() );
201 GDALSetGeoTransform( hSrcDS.get(), geoTransform );
202 return hSrcDS;
203}
204
205gdal::dataset_unique_ptr QgsGdalUtils::createSingleBandTiffDataset( const QString &filename, GDALDataType dataType, const QgsRectangle &extent, int width, int height, const QgsCoordinateReferenceSystem &crs )
206{
207 const double cellSizeX = extent.width() / width;
208 const double cellSizeY = extent.height() / height;
209 double geoTransform[6];
210 geoTransform[0] = extent.xMinimum();
211 geoTransform[1] = cellSizeX;
212 geoTransform[2] = 0;
213 geoTransform[3] = extent.yMinimum() + ( cellSizeY * height );
214 geoTransform[4] = 0;
215 geoTransform[5] = -cellSizeY;
216
217 GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
218 if ( !hDriver )
219 {
221 }
222
223 // Create the output file.
224 gdal::dataset_unique_ptr hDstDS( GDALCreate( hDriver, filename.toUtf8().constData(), width, height, 1, dataType, nullptr ) );
225 if ( !hDstDS )
226 {
228 }
229
230 // Write out the projection definition.
231 GDALSetProjection( hDstDS.get(), crs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toLatin1().constData() );
232 GDALSetGeoTransform( hDstDS.get(), geoTransform );
233 return hDstDS;
234}
235
237{
238 if ( image.isNull() )
239 return nullptr;
240
241 const QRgb *rgb = reinterpret_cast<const QRgb *>( image.constBits() );
242 GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
243 if ( !hDriverMem )
244 {
245 return nullptr;
246 }
247 gdal::dataset_unique_ptr hSrcDS( GDALCreate( hDriverMem, "", image.width(), image.height(), 0, GDT_Byte, nullptr ) );
248
249 char **papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
250 << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
251 << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
252 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) + 2 ) );
253 GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
254 CSLDestroy( papszOptions );
255
256 papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
257 << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
258 << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
259 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) + 1 ) );
260 GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
261 CSLDestroy( papszOptions );
262
263 papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
264 << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
265 << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
266 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) ) );
267 GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
268 CSLDestroy( papszOptions );
269
270 papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
271 << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
272 << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
273 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) + 3 ) );
274 GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
275 CSLDestroy( papszOptions );
276
277 return hSrcDS;
278}
279
280gdal::dataset_unique_ptr QgsGdalUtils::blockToSingleBandMemoryDataset( int pixelWidth, int pixelHeight, const QgsRectangle &extent, void *block, GDALDataType dataType )
281{
282 if ( !block )
283 return nullptr;
284
285 GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
286 if ( !hDriverMem )
287 return nullptr;
288
289 const double cellSizeX = extent.width() / pixelWidth;
290 const double cellSizeY = extent.height() / pixelHeight;
291 double geoTransform[6];
292 geoTransform[0] = extent.xMinimum();
293 geoTransform[1] = cellSizeX;
294 geoTransform[2] = 0;
295 geoTransform[3] = extent.yMinimum() + ( cellSizeY * pixelHeight );
296 geoTransform[4] = 0;
297 geoTransform[5] = -cellSizeY;
298
299 gdal::dataset_unique_ptr hDstDS( GDALCreate( hDriverMem, "", pixelWidth, pixelHeight, 0, dataType, nullptr ) );
300
301 int dataTypeSize = GDALGetDataTypeSizeBytes( dataType );
302 char **papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
303 << QStringLiteral( "PIXELOFFSET=%1" ).arg( dataTypeSize )
304 << QStringLiteral( "LINEOFFSET=%1" ).arg( pixelWidth * dataTypeSize )
305 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( block ) ) );
306 GDALAddBand( hDstDS.get(), dataType, papszOptions );
307 CSLDestroy( papszOptions );
308
309 GDALSetGeoTransform( hDstDS.get(), geoTransform );
310
311 return hDstDS;
312}
313
315{
316 if ( !block )
317 return nullptr;
318
319 gdal::dataset_unique_ptr ret = blockToSingleBandMemoryDataset( block->width(), block->height(), extent, block->bits(), gdalDataTypeFromQgisDataType( block->dataType() ) );
320 if ( ret )
321 {
322 GDALRasterBandH band = GDALGetRasterBand( ret.get(), 1 );
323 if ( band )
324 GDALSetRasterNoDataValue( band, block->noDataValue() );
325 }
326
327 return ret;
328}
329
330
331
333 const QgsPointXY &origin,
334 double gridXSize,
335 double gridYSize,
336 QgsRasterBlock *block )
337{
338 if ( !block )
339 return nullptr;
340
341 GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
342 if ( !hDriverMem )
343 return nullptr;
344
345 const double cellSizeX = gridXSize / block->width();
346 const double cellSizeY = gridYSize / block->height();
347 double geoTransform[6];
348 geoTransform[0] = origin.x();
349 geoTransform[1] = cellSizeX * std::cos( rotation );
350 geoTransform[2] = cellSizeY * std::sin( rotation );
351 geoTransform[3] = origin.y();
352 geoTransform[4] = cellSizeX * std::sin( rotation );
353 geoTransform[5] = -cellSizeY * std::cos( rotation );
354
355 GDALDataType dataType = gdalDataTypeFromQgisDataType( block->dataType() );
356 gdal::dataset_unique_ptr hDstDS( GDALCreate( hDriverMem, "", block->width(), block->height(), 0, dataType, nullptr ) );
357
358 int dataTypeSize = GDALGetDataTypeSizeBytes( dataType );
359 char **papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
360 << QStringLiteral( "PIXELOFFSET=%1" ).arg( dataTypeSize )
361 << QStringLiteral( "LINEOFFSET=%1" ).arg( block->width() * dataTypeSize )
362 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( block->bits() ) ) );
363 GDALAddBand( hDstDS.get(), dataType, papszOptions );
364 CSLDestroy( papszOptions );
365
366 GDALSetGeoTransform( hDstDS.get(), geoTransform );
367
368 GDALRasterBandH band = GDALGetRasterBand( hDstDS.get(), 1 );
369 if ( band )
370 GDALSetRasterNoDataValue( band, block->noDataValue() );
371
372 return hDstDS;
373}
374
375static bool resampleSingleBandRasterStatic( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, GDALResampleAlg resampleAlg, char **papszOptions )
376{
377 gdal::warp_options_unique_ptr psWarpOptions( GDALCreateWarpOptions() );
378 psWarpOptions->hSrcDS = hSrcDS;
379 psWarpOptions->hDstDS = hDstDS;
380
381 psWarpOptions->nBandCount = 1;
382 psWarpOptions->panSrcBands = reinterpret_cast< int * >( CPLMalloc( sizeof( int ) * 1 ) );
383 psWarpOptions->panDstBands = reinterpret_cast< int * >( CPLMalloc( sizeof( int ) * 1 ) );
384 psWarpOptions->panSrcBands[0] = 1;
385 psWarpOptions->panDstBands[0] = 1;
386 double noDataValue = GDALGetRasterNoDataValue( GDALGetRasterBand( hDstDS, 1 ), nullptr );
387 psWarpOptions->padfDstNoDataReal = reinterpret_cast< double * >( CPLMalloc( sizeof( double ) * 1 ) );
388 psWarpOptions->padfDstNoDataReal[0] = noDataValue;
389 psWarpOptions->eResampleAlg = resampleAlg;
390
391 // Establish reprojection transformer.
392 psWarpOptions->pTransformerArg = GDALCreateGenImgProjTransformer2( hSrcDS, hDstDS, papszOptions );
393
394 if ( ! psWarpOptions->pTransformerArg )
395 {
396 return false;
397 }
398
399 psWarpOptions->pfnTransformer = GDALGenImgProjTransform;
400 psWarpOptions->papszWarpOptions = CSLSetNameValue( psWarpOptions-> papszWarpOptions, "INIT_DEST", "NO_DATA" );
401
402 // Initialize and execute the warp operation.
403 bool retVal = false;
404 GDALWarpOperation oOperation;
405 CPLErr initResult = oOperation.Initialize( psWarpOptions.get() );
406 if ( initResult != CE_Failure )
407 retVal = oOperation.ChunkAndWarpImage( 0, 0, GDALGetRasterXSize( hDstDS ), GDALGetRasterYSize( hDstDS ) ) == CE_None;
408 GDALDestroyGenImgProjTransformer( psWarpOptions->pTransformerArg );
409 return retVal;
410}
411
412bool QgsGdalUtils::resampleSingleBandRaster( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, GDALResampleAlg resampleAlg, const char *pszCoordinateOperation )
413{
414 char **papszOptions = nullptr;
415 if ( pszCoordinateOperation )
416 papszOptions = CSLSetNameValue( papszOptions, "COORDINATE_OPERATION", pszCoordinateOperation );
417
418 bool result = resampleSingleBandRasterStatic( hSrcDS, hDstDS, resampleAlg, papszOptions );
419 CSLDestroy( papszOptions );
420 return result;
421}
422
424 GDALDatasetH hDstDS,
425 GDALResampleAlg resampleAlg,
426 const QgsCoordinateReferenceSystem &sourceCrs,
427 const QgsCoordinateReferenceSystem &destinationCrs )
428{
429 char **papszOptions = nullptr;
430
431 papszOptions = CSLSetNameValue( papszOptions, "SRC_SRS", sourceCrs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toUtf8().constData() );
432 papszOptions = CSLSetNameValue( papszOptions, "DST_SRS", destinationCrs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toUtf8().constData() );
433
434 bool result = resampleSingleBandRasterStatic( hSrcDS, hDstDS, resampleAlg, papszOptions );
435 CSLDestroy( papszOptions );
436 return result;
437}
438
439QImage QgsGdalUtils::resampleImage( const QImage &image, QSize outputSize, GDALRIOResampleAlg resampleAlg )
440{
442 if ( !srcDS )
443 return QImage();
444
445 GDALRasterIOExtraArg extra;
446 INIT_RASTERIO_EXTRA_ARG( extra );
447 extra.eResampleAlg = resampleAlg;
448
449 QImage res( outputSize, image.format() );
450 if ( res.isNull() )
451 return QImage();
452
453 GByte *rgb = reinterpret_cast<GByte *>( res.bits() );
454
455 CPLErr err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 1 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 2, outputSize.width(),
456 outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
457 if ( err != CE_None )
458 {
459 QgsDebugError( QStringLiteral( "failed to read red band" ) );
460 return QImage();
461 }
462
463 err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 2 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 1, outputSize.width(),
464 outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
465 if ( err != CE_None )
466 {
467 QgsDebugError( QStringLiteral( "failed to read green band" ) );
468 return QImage();
469 }
470
471 err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 3 ), GF_Read, 0, 0, image.width(), image.height(), rgb, outputSize.width(),
472 outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
473 if ( err != CE_None )
474 {
475 QgsDebugError( QStringLiteral( "failed to read blue band" ) );
476 return QImage();
477 }
478
479 err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 4 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 3, outputSize.width(),
480 outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
481 if ( err != CE_None )
482 {
483 QgsDebugError( QStringLiteral( "failed to read alpha band" ) );
484 return QImage();
485 }
486
487 return res;
488}
489
490QString QgsGdalUtils::helpCreationOptionsFormat( const QString &format )
491{
492 QString message;
493 GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
494 if ( myGdalDriver )
495 {
496 // first report details and help page
497 char **GDALmetadata = GDALGetMetadata( myGdalDriver, nullptr );
498 message += QLatin1String( "Format Details:\n" );
499 message += QStringLiteral( " Extension: %1\n" ).arg( CSLFetchNameValue( GDALmetadata, GDAL_DMD_EXTENSION ) );
500 message += QStringLiteral( " Short Name: %1" ).arg( GDALGetDriverShortName( myGdalDriver ) );
501 message += QStringLiteral( " / Long Name: %1\n" ).arg( GDALGetDriverLongName( myGdalDriver ) );
502 const QString helpUrl = gdalDocumentationUrlForDriver( myGdalDriver );
503 if ( !helpUrl.isEmpty() )
504 message += QStringLiteral( " Help page: %1\n\n" ).arg( helpUrl );
505
506 // next get creation options
507 // need to serialize xml to get newlines, should we make the basic xml prettier?
508 CPLXMLNode *psCOL = CPLParseXMLString( GDALGetMetadataItem( myGdalDriver,
509 GDAL_DMD_CREATIONOPTIONLIST, "" ) );
510 char *pszFormattedXML = CPLSerializeXMLTree( psCOL );
511 if ( pszFormattedXML )
512 message += QString( pszFormattedXML );
513 if ( psCOL )
514 CPLDestroyXMLNode( psCOL );
515 if ( pszFormattedXML )
516 CPLFree( pszFormattedXML );
517 }
518 return message;
519}
520
521char **QgsGdalUtils::papszFromStringList( const QStringList &list )
522{
523 char **papszRetList = nullptr;
524 const auto constList = list;
525 for ( const QString &elem : constList )
526 {
527 papszRetList = CSLAddString( papszRetList, elem.toLocal8Bit().constData() );
528 }
529 return papszRetList;
530}
531
532QString QgsGdalUtils::validateCreationOptionsFormat( const QStringList &createOptions, const QString &format )
533{
534 GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
535 if ( ! myGdalDriver )
536 return QStringLiteral( "invalid GDAL driver" );
537
538 char **papszOptions = papszFromStringList( createOptions );
539 // get error string?
540 const int ok = GDALValidateCreationOptions( myGdalDriver, papszOptions );
541 CSLDestroy( papszOptions );
542
543 if ( !ok )
544 return QStringLiteral( "Failed GDALValidateCreationOptions() test" );
545 return QString();
546}
547
548static void setRPCTransformerOptions( GDALDatasetH hSrcDS, char ***opts )
549{
550 if ( GDALGetMetadata( hSrcDS, "RPC" ) )
551 {
552 // Some RPC may contain a HEIGHT_AVERAGE entry, that must be used as the
553 // best default for RPC_HEIGHT. See https://github.com/OSGeo/gdal/pull/11989
554 const char *heightStr = GDALGetMetadataItem( hSrcDS, "HEIGHT_AVERAGE", "RPC" );
555 if ( !heightStr )
556 {
557 // Otherwise well-behaved RPC should have height offset which is also
558 // a resaonable default for RPC_HEIGHT.
559 heightStr = GDALGetMetadataItem( hSrcDS, "HEIGHT_OFF", "RPC" );
560 }
561 if ( heightStr )
562 *opts = CSLAddNameValue( *opts, "RPC_HEIGHT", heightStr );
563 }
564}
565
567 GDALDatasetH hSrcDS,
568 const char *pszSrcWKT,
569 const char *pszDstWKT,
570 GDALResampleAlg eResampleAlg,
571 double dfMaxError,
572 const GDALWarpOptions *psOptionsIn )
573{
574 char **opts = nullptr;
575 setRPCTransformerOptions( hSrcDS, &opts );
576 GDALDatasetH hRetDS = GDALAutoCreateWarpedVRTEx( hSrcDS, pszSrcWKT, pszDstWKT, eResampleAlg, dfMaxError, psOptionsIn, opts );
577 CSLDestroy( opts );
578 return hRetDS;
579}
580
581void *QgsGdalUtils::rpcAwareCreateTransformer( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, char **papszOptions )
582{
583 char **opts = CSLDuplicate( papszOptions );
584 setRPCTransformerOptions( hSrcDS, &opts );
585 void *transformer = GDALCreateGenImgProjTransformer2( hSrcDS, hDstDS, opts );
586 CSLDestroy( opts );
587 return transformer;
588}
589
591{
592 switch ( dataType )
593 {
595 return GDALDataType::GDT_Unknown;
596 break;
598 return GDALDataType::GDT_Byte;
599 break;
601#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
602 return GDALDataType::GDT_Int8;
603#else
604 return GDALDataType::GDT_Unknown;
605#endif
606 break;
608 return GDALDataType::GDT_UInt16;
609 break;
611 return GDALDataType::GDT_Int16;
612 break;
614 return GDALDataType::GDT_UInt32;
615 break;
617 return GDALDataType::GDT_Int32;
618 break;
620 return GDALDataType::GDT_Float32;
621 break;
623 return GDALDataType::GDT_Float64;
624 break;
626 return GDALDataType::GDT_CInt16;
627 break;
629 return GDALDataType::GDT_CInt32;
630 break;
632 return GDALDataType::GDT_CFloat32;
633 break;
635 return GDALDataType::GDT_CFloat64;
636 break;
639 return GDALDataType::GDT_Unknown;
640 break;
641 };
642
643 return GDALDataType::GDT_Unknown;
644}
645
647{
648 GDALResampleAlg eResampleAlg = GRA_NearestNeighbour;
649 switch ( method )
650 {
652 case Qgis::RasterResamplingMethod::Gauss: // Gauss not available in GDALResampleAlg
653 eResampleAlg = GRA_NearestNeighbour;
654 break;
655
657 eResampleAlg = GRA_Bilinear;
658 break;
659
661 eResampleAlg = GRA_Cubic;
662 break;
663
665 eResampleAlg = GRA_CubicSpline;
666 break;
667
669 eResampleAlg = GRA_Lanczos;
670 break;
671
673 eResampleAlg = GRA_Average;
674 break;
675
677 eResampleAlg = GRA_Mode;
678 break;
679 }
680
681 return eResampleAlg;
682}
683
684#ifndef QT_NO_NETWORKPROXY
686{
687 // Check proxy configuration, they are application level but
688 // instead of adding an API and complex signal/slot connections
689 // given the limited cost of checking them on every provider instantiation
690 // we can do it here so that new settings are applied whenever a new layer
691 // is created.
692 const QgsSettings settings;
693 // Check that proxy is enabled
694 if ( settings.value( QStringLiteral( "proxy/proxyEnabled" ), false ).toBool() )
695 {
696 // Get the first configured proxy
697 QList<QNetworkProxy> proxies( QgsNetworkAccessManager::instance()->proxyFactory()->queryProxy( ) );
698 if ( ! proxies.isEmpty() )
699 {
700 const QNetworkProxy proxy( proxies.first() );
701 // TODO/FIXME: check excludes (the GDAL config options are global, we need a per-connection config option)
702 //QStringList excludes;
703 //excludes = settings.value( QStringLiteral( "proxy/proxyExcludedUrls" ), "" ).toStringList();
704
705 const QString proxyHost( proxy.hostName() );
706 const quint16 proxyPort( proxy.port() );
707
708 const QString proxyUser( proxy.user() );
709 const QString proxyPassword( proxy.password() );
710
711 if ( ! proxyHost.isEmpty() )
712 {
713 QString connection( proxyHost );
714 if ( proxyPort )
715 {
716 connection += ':' + QString::number( proxyPort );
717 }
718 CPLSetConfigOption( "GDAL_HTTP_PROXY", connection.toUtf8() );
719 if ( ! proxyUser.isEmpty( ) )
720 {
721 QString credentials( proxyUser );
722 if ( ! proxyPassword.isEmpty( ) )
723 {
724 credentials += ':' + proxyPassword;
725 }
726 CPLSetConfigOption( "GDAL_HTTP_PROXYUSERPWD", credentials.toUtf8() );
727 }
728 }
729 }
730 }
731}
732
733bool QgsGdalUtils::pathIsCheapToOpen( const QString &path, int smallFileSizeLimit )
734{
735 const QFileInfo info( path );
736 const long long size = info.size();
737
738 // if size could not be determined, safest to flag path as expensive
739 if ( size == 0 )
740 return false;
741
742 const QString suffix = info.suffix().toLower();
743 static const QStringList sFileSizeDependentExtensions
744 {
745 QStringLiteral( "xlsx" ),
746 QStringLiteral( "ods" ),
747 QStringLiteral( "csv" )
748 };
749 if ( sFileSizeDependentExtensions.contains( suffix ) )
750 {
751 // path corresponds to a file type which is only cheap to open for small files
752 return size < smallFileSizeLimit;
753 }
754
755 // treat all other formats as expensive.
756 // TODO -- flag formats which only require a quick header parse as cheap
757 return false;
758}
759
761{
762#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,4,0)
763 // get supported extensions
764 static std::once_flag initialized;
765 static QStringList SUPPORTED_DB_LAYERS_EXTENSIONS;
766 std::call_once( initialized, [ = ]
767 {
768 // iterate through all of the supported drivers, adding the corresponding file extensions for
769 // types which advertise multilayer support
770 GDALDriverH driver = nullptr;
771
772 QSet< QString > extensions;
773
774 for ( int i = 0; i < GDALGetDriverCount(); ++i )
775 {
776 driver = GDALGetDriver( i );
777 if ( !driver )
778 {
779 QgsLogger::warning( "unable to get driver " + QString::number( i ) );
780 continue;
781 }
782
783 bool isMultiLayer = false;
784 if ( QString( GDALGetMetadataItem( driver, GDAL_DCAP_RASTER, nullptr ) ) == QLatin1String( "YES" ) )
785 {
786 if ( GDALGetMetadataItem( driver, GDAL_DMD_SUBDATASETS, nullptr ) )
787 {
788 isMultiLayer = true;
789 }
790 }
791 if ( !isMultiLayer && QString( GDALGetMetadataItem( driver, GDAL_DCAP_VECTOR, nullptr ) ) == QLatin1String( "YES" ) )
792 {
793 if ( GDALGetMetadataItem( driver, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, nullptr ) )
794 {
795 isMultiLayer = true;
796 }
797 }
798
799 if ( !isMultiLayer )
800 continue;
801
802 const QString driverExtensions = GDALGetMetadataItem( driver, GDAL_DMD_EXTENSIONS, "" );
803 if ( driverExtensions.isEmpty() )
804 continue;
805
806 const QStringList splitExtensions = driverExtensions.split( ' ', Qt::SkipEmptyParts );
807
808 for ( const QString &ext : splitExtensions )
809 {
810 // maintain older behavior -- don't always expose tiff files as containers
811 if ( ext == QLatin1String( "tif" ) || ext == QLatin1String( "tiff" ) )
812 continue;
813
814 extensions.insert( ext );
815 }
816 }
817
818 SUPPORTED_DB_LAYERS_EXTENSIONS = QStringList( extensions.constBegin(), extensions.constEnd() );
819 } );
820 return SUPPORTED_DB_LAYERS_EXTENSIONS;
821
822#else
823 static const QStringList SUPPORTED_DB_LAYERS_EXTENSIONS
824 {
825 QStringLiteral( "gpkg" ),
826 QStringLiteral( "sqlite" ),
827 QStringLiteral( "db" ),
828 QStringLiteral( "gdb" ),
829 QStringLiteral( "kml" ),
830 QStringLiteral( "kmz" ),
831 QStringLiteral( "osm" ),
832 QStringLiteral( "mdb" ),
833 QStringLiteral( "accdb" ),
834 QStringLiteral( "xls" ),
835 QStringLiteral( "xlsx" ),
836 QStringLiteral( "ods" ),
837 QStringLiteral( "gpx" ),
838 QStringLiteral( "pdf" ),
839 QStringLiteral( "pbf" ),
840 QStringLiteral( "vrt" ),
841 QStringLiteral( "nc" ),
842 QStringLiteral( "dxf" ),
843 QStringLiteral( "shp.zip" ) };
844 return SUPPORTED_DB_LAYERS_EXTENSIONS;
845#endif
846}
847
848QString QgsGdalUtils::vsiPrefixForPath( const QString &path )
849{
850 const QStringList vsiPrefixes = QgsGdalUtils::vsiArchivePrefixes();
851
852 const thread_local QRegularExpression vsiRx( QStringLiteral( "^(/vsi.+?/)" ), QRegularExpression::PatternOption::CaseInsensitiveOption );
853 const QRegularExpressionMatch vsiMatch = vsiRx.match( path );
854 if ( vsiMatch.hasMatch() )
855 return vsiMatch.captured( 1 );
856
857 if ( path.endsWith( QLatin1String( ".shp.zip" ), Qt::CaseInsensitive ) )
858 {
859 // GDAL 3.1 Shapefile driver directly handles .shp.zip files
860 if ( GDALIdentifyDriverEx( path.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr ) )
861 return QString();
862 return QStringLiteral( "/vsizip/" );
863 }
864 else if ( path.endsWith( QLatin1String( ".zip" ), Qt::CaseInsensitive ) )
865 {
866 // GTFS driver directly handles .zip files
867 const char *const apszAllowedDrivers[] = { "GTFS", nullptr };
868 if ( GDALIdentifyDriverEx( path.toUtf8().constData(), GDAL_OF_VECTOR, apszAllowedDrivers, nullptr ) )
869 return QString();
870 return QStringLiteral( "/vsizip/" );
871 }
872 else if ( path.endsWith( QLatin1String( ".tar" ), Qt::CaseInsensitive ) ||
873 path.endsWith( QLatin1String( ".tar.gz" ), Qt::CaseInsensitive ) ||
874 path.endsWith( QLatin1String( ".tgz" ), Qt::CaseInsensitive ) )
875 return QStringLiteral( "/vsitar/" );
876 else if ( path.endsWith( QLatin1String( ".gz" ), Qt::CaseInsensitive ) )
877 return QStringLiteral( "/vsigzip/" );
878#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
879 else if ( vsiPrefixes.contains( QStringLiteral( "/vsi7z/" ) ) &&
880 ( path.endsWith( QLatin1String( ".7z" ), Qt::CaseInsensitive ) ||
881 path.endsWith( QLatin1String( ".lpk" ), Qt::CaseInsensitive ) ||
882 path.endsWith( QLatin1String( ".lpkx" ), Qt::CaseInsensitive ) ||
883 path.endsWith( QLatin1String( ".mpk" ), Qt::CaseInsensitive ) ||
884 path.endsWith( QLatin1String( ".mpkx" ), Qt::CaseInsensitive ) ) )
885 return QStringLiteral( "/vsi7z/" );
886 else if ( vsiPrefixes.contains( QStringLiteral( "/vsirar/" ) ) &&
887 path.endsWith( QLatin1String( ".rar" ), Qt::CaseInsensitive ) )
888 return QStringLiteral( "/vsirar/" );
889#endif
890
891 return QString();
892}
893
895{
896 QStringList res { QStringLiteral( "/vsizip/" ),
897 QStringLiteral( "/vsitar/" ),
898 QStringLiteral( "/vsigzip/" ),
899 };
900#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
901 res.append( QStringLiteral( "/vsi7z/" ) );
902 res.append( QStringLiteral( "/vsirar/" ) );
903#endif
904 return res;
905}
906
907QList<QgsGdalUtils::VsiNetworkFileSystemDetails> QgsGdalUtils::vsiNetworkFileSystems()
908{
909 // get supported extensions
910 static std::once_flag initialized;
911 static QList<QgsGdalUtils::VsiNetworkFileSystemDetails> VSI_FILE_SYSTEM_DETAILS;
912 std::call_once( initialized, [ = ]
913 {
914 if ( char **papszPrefixes = VSIGetFileSystemsPrefixes() )
915 {
916 for ( int i = 0; papszPrefixes[i]; i++ )
917 {
919 details.identifier = QString( papszPrefixes[i] );
920 if ( details.identifier.startsWith( '/' ) )
921 details.identifier = details.identifier.mid( 1 );
922 if ( details.identifier.endsWith( '/' ) )
923 details.identifier.chop( 1 );
924
925 if ( details.identifier == QLatin1String( "vsicurl" ) )
926 details.name = QObject::tr( "HTTP/HTTPS/FTP" );
927 else if ( details.identifier == QLatin1String( "vsis3" ) )
928 details.name = QObject::tr( "AWS S3" );
929 else if ( details.identifier == QLatin1String( "vsigs" ) )
930 details.name = QObject::tr( "Google Cloud Storage" );
931 else if ( details.identifier == QLatin1String( "vsiaz" ) )
932 details.name = QObject::tr( "Microsoft Azure Blob" );
933 else if ( details.identifier == QLatin1String( "vsiadls" ) )
934 details.name = QObject::tr( "Microsoft Azure Data Lake Storage" );
935 else if ( details.identifier == QLatin1String( "vsioss" ) )
936 details.name = QObject::tr( "Alibaba Cloud OSS" );
937 else if ( details.identifier == QLatin1String( "vsiswift" ) )
938 details.name = QObject::tr( "OpenStack Swift Object Storage" );
939 else if ( details.identifier == QLatin1String( "vsihdfs" ) )
940 details.name = QObject::tr( "Hadoop File System" );
941 else
942 continue;
943 VSI_FILE_SYSTEM_DETAILS.append( details );
944 }
945
946 CSLDestroy( papszPrefixes );
947 }
948 } );
949
950 return VSI_FILE_SYSTEM_DETAILS;
951}
952
953bool QgsGdalUtils::isVsiArchivePrefix( const QString &prefix )
954{
955 return vsiArchivePrefixes().contains( prefix );
956}
957
959{
960 QStringList res { QStringLiteral( ".zip" ),
961 QStringLiteral( ".tar" ),
962 QStringLiteral( ".tar.gz" ),
963 QStringLiteral( ".tgz" ),
964 QStringLiteral( ".gz" ),
965 };
966#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
967 res.append( { QStringLiteral( ".7z" ),
968 QStringLiteral( ".lpk" ),
969 QStringLiteral( ".lpkx" ),
970 QStringLiteral( ".mpk" ),
971 QStringLiteral( ".mpkx" ),
972 QStringLiteral( ".rar" )
973 } );
974#endif
975 return res;
976}
977
978bool QgsGdalUtils::isVsiArchiveFileExtension( const QString &extension )
979{
980 const QString extWithDot = extension.startsWith( '.' ) ? extension : ( '.' + extension );
981 return vsiArchiveFileExtensions().contains( extWithDot.toLower() );
982}
983
985{
986 if ( prefix.isEmpty() )
988
989 QString vsiPrefix = prefix;
990 if ( vsiPrefix.startsWith( '/' ) )
991 vsiPrefix = vsiPrefix.mid( 1 );
992 if ( vsiPrefix.endsWith( '/' ) )
993 vsiPrefix.chop( 1 );
994
995 if ( !vsiPrefix.startsWith( QLatin1String( "vsi" ) ) )
997
998 if ( vsiPrefix == QLatin1String( "vsizip" ) ||
999 vsiPrefix == QLatin1String( "vsigzip" ) ||
1000 vsiPrefix == QLatin1String( "vsitar" ) ||
1001 vsiPrefix == QLatin1String( "vsi7z" ) ||
1002 vsiPrefix == QLatin1String( "vsirar" ) )
1004
1005 else if ( vsiPrefix == QLatin1String( "vsicurl" ) ||
1006 vsiPrefix == QLatin1String( "vsicurl_streaming" ) )
1008
1009 else if ( vsiPrefix == QLatin1String( "vsis3" ) ||
1010 vsiPrefix == QLatin1String( "vsicurl_streaming" ) ||
1011 vsiPrefix == QLatin1String( "vsigs" ) ||
1012 vsiPrefix == QLatin1String( "vsigs_streaming" ) ||
1013 vsiPrefix == QLatin1String( "vsiaz" ) ||
1014 vsiPrefix == QLatin1String( "vsiaz_streaming" ) ||
1015 vsiPrefix == QLatin1String( "vsiadls" ) ||
1016 vsiPrefix == QLatin1String( "vsioss" ) ||
1017 vsiPrefix == QLatin1String( "vsioss_streaming" ) ||
1018 vsiPrefix == QLatin1String( "vsiswift" ) ||
1019 vsiPrefix == QLatin1String( "vsiswift_streaming" ) ||
1020 vsiPrefix == QLatin1String( "vsihdfs" ) ||
1021 vsiPrefix == QLatin1String( "vsiwebhdfs" ) )
1023
1024 else if ( vsiPrefix == QLatin1String( "vsimem" ) )
1026
1028}
1029
1030bool QgsGdalUtils::vrtMatchesLayerType( const QString &vrtPath, Qgis::LayerType type )
1031{
1032 CPLPushErrorHandler( CPLQuietErrorHandler );
1033 CPLErrorReset();
1034 GDALDriverH hDriver = nullptr;
1035
1036 switch ( type )
1037 {
1039 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr );
1040 break;
1041
1043 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_RASTER, nullptr, nullptr );
1044 break;
1045
1053 break;
1054 }
1055
1056 CPLPopErrorHandler();
1057 return static_cast< bool >( hDriver );
1058}
1059
1061{
1062 if ( hDriver )
1063 {
1064 const QString gdalDriverHelpTopic = GDALGetMetadataItem( hDriver, GDAL_DMD_HELPTOPIC, nullptr ); // e.g. "drivers/vector/ili.html"
1065 if ( !gdalDriverHelpTopic.isEmpty() )
1066 return QStringLiteral( "https://gdal.org/%1" ).arg( gdalDriverHelpTopic );
1067 }
1068 return QString();
1069}
1070
1071bool QgsGdalUtils::applyVsiCredentialOptions( const QString &prefix, const QString &path, const QVariantMap &options )
1072{
1073 QString vsiPrefix = prefix;
1074 if ( !vsiPrefix.startsWith( '/' ) )
1075 vsiPrefix.prepend( '/' );
1076 if ( !vsiPrefix.endsWith( '/' ) )
1077 vsiPrefix.append( '/' );
1078
1079 QString vsiPath = path;
1080 if ( vsiPath.endsWith( '/' ) )
1081 vsiPath.chop( 1 );
1082
1083 const QString bucket = vsiPrefix + vsiPath;
1084
1085 for ( auto it = options.constBegin(); it != options.constEnd(); ++it )
1086 {
1087#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 6, 0)
1088 VSISetPathSpecificOption( bucket.toUtf8().constData(), it.key().toUtf8().constData(), it.value().toString().toUtf8().constData() );
1089#elif GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3, 5, 0)
1090 VSISetCredential( bucket.toUtf8().constData(), it.key().toUtf8().constData(), it.value().toString().toUtf8().constData() );
1091#else
1092 ( void )bucket;
1093 QgsMessageLog::logMessage( QObject::tr( "Cannot use VSI credential options on GDAL versions earlier than 3.5" ), QStringLiteral( "GDAL" ), Qgis::MessageLevel::Critical );
1094 return false;
1095#endif
1096 }
1097 return true;
1098}
1099#endif
VsiHandlerType
GDAL VSI handler types.
Definition qgis.h:5518
@ Memory
In-memory types (e.g. vsimem)
@ Invalid
Invalid type, i.e. not a valid VSI handler.
@ Other
All other types.
@ Cloud
Specific cloud provider types (e.g. vsis3)
@ Archive
File archive type (e.g. vsizip)
@ Network
Generic network types (e.g. vsicurl)
RasterResamplingMethod
Resampling method for raster provider-level resampling.
Definition qgis.h:1447
@ Lanczos
Lanczos windowed sinc interpolation (6x6 kernel)
@ Nearest
Nearest-neighbour resampling.
@ Mode
Mode (selects the value which appears most often of all the sampled points)
@ Bilinear
Bilinear (2x2 kernel) resampling.
@ Average
Average resampling.
@ CubicSpline
Cubic B-Spline Approximation (4x4 kernel)
@ Cubic
Cubic Convolution Approximation (4x4 kernel) resampling.
@ Critical
Critical/error message.
Definition qgis.h:157
DataType
Raster data types.
Definition qgis.h:351
@ CInt32
Complex Int32.
@ Float32
Thirty two bit floating point (float)
@ CFloat64
Complex Float64.
@ Int16
Sixteen bit signed integer (qint16)
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30)
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ UnknownDataType
Unknown or unspecified type.
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
@ Int32
Thirty two bit signed integer (qint32)
@ Float64
Sixty four bit floating point (double)
@ CFloat32
Complex Float32.
@ CInt16
Complex Int16.
@ UInt32
Thirty two bit unsigned integer (quint32)
LayerType
Types of layers that can be added to a map.
Definition qgis.h:169
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
This class represents a coordinate reference system (CRS).
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Encapsulates the definition of a GDAL configuration option.
QVariant defaultValue
Default value.
QVariant maximum
Maximum acceptable value.
QString name
Option name.
QStringList options
Available choices, for Select options.
QVariant minimum
Minimum acceptable value.
@ Int
Integer option.
@ Boolean
Boolean option.
@ Invalid
Invalid option.
@ Text
Text option.
@ Double
Double option.
@ Select
Selection option.
QString scope
Option scope.
static QList< QgsGdalOption > optionsFromXml(const CPLXMLNode *node)
Returns a list of all GDAL options from an XML node.
static QgsGdalOption fromXmlNode(const CPLXMLNode *node)
Creates a QgsGdalOption from an XML node.
QString description
Option description.
Type type
Option type.
static Qgis::VsiHandlerType vsiHandlerType(const QString &prefix)
Returns the VSI handler type for a given VSI prefix.
static bool applyVsiCredentialOptions(const QString &prefix, const QString &path, const QVariantMap &options)
Attempts to apply VSI credential options.
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 vsiPrefixForPath(const QString &path)
Returns a the vsi prefix which corresponds to a file path, or an empty string if the path is not asso...
static QString helpCreationOptionsFormat(const QString &format)
Gets creation options metadata for a given format.
static bool vrtMatchesLayerType(const QString &vrtPath, Qgis::LayerType type)
Returns true if the VRT file at the specified path is a VRT matching the given layer type.
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 bool isVsiArchiveFileExtension(const QString &extension)
Returns true if a file extension is a supported archive style container (e.g.
static GDALResampleAlg gdalResamplingAlgorithm(Qgis::RasterResamplingMethod method)
Returns the GDAL resampling method corresponding to the QGIS resampling method.
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 GDALDataType gdalDataTypeFromQgisDataType(Qgis::DataType dataType)
Returns the GDAL data type corresponding to the QGIS data type dataType.
static bool isVsiArchivePrefix(const QString &prefix)
Returns true if prefix is a supported archive style container prefix (e.g.
static QString gdalDocumentationUrlForDriver(GDALDriverH hDriver)
Returns the URL for the GDAL documentation for the specified driver.
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 a data block to a single band GDAL memory dataset.
static QList< VsiNetworkFileSystemDetails > vsiNetworkFileSystems()
Returns a list of available GDAL VSI network file systems.
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 QStringList vsiArchiveFileExtensions()
Returns a list of file extensions which correspond to archive style containers supported by GDAL (e....
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 QStringList vsiArchivePrefixes()
Returns a list of vsi prefixes which correspond to archive style containers (eg vsizip).
static void warning(const QString &msg)
Goes to qWarning.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
A class to represent a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
Raster data container.
int height() const
Returns the height (number of rows) of the raster block.
char * bits(int row, int column)
Returns a pointer to block data.
double noDataValue() const
Returns no data value.
Qgis::DataType dataType() const
Returns data type.
int width() const
Returns the width (number of columns) of the raster block.
A rectangle specified with double values.
double xMinimum
double yMinimum
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
std::unique_ptr< GDALWarpOptions, GDALWarpOptionsDeleter > warp_options_unique_ptr
Scoped GDAL warp options.
void * GDALDatasetH
#define QgsDebugError(str)
Definition qgslogger.h:40
const QgsCoordinateReferenceSystem & crs
Encapsulates details for a GDAL VSI network file system.
QString name
Translated, user-friendly name.
QString identifier
VSI handler identifier, eg "vsis3".