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