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