QGIS API Documentation 4.1.0-Master (467af3bbe65)
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"
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 // Check that proxy is enabled
674 {
675 // Get the first configured proxy
676 QList<QNetworkProxy> proxies( QgsNetworkAccessManager::instance()->proxyFactory()->queryProxy() );
677 if ( !proxies.isEmpty() )
678 {
679 const QNetworkProxy proxy( proxies.first() );
680 // TODO/FIXME: check excludes (the GDAL config options are global, we need a per-connection config option)
681
682 const QString proxyHost( proxy.hostName() );
683 const quint16 proxyPort( proxy.port() );
684
685 const QString proxyUser( proxy.user() );
686 const QString proxyPassword( proxy.password() );
687
688 if ( !proxyHost.isEmpty() )
689 {
690 QString connection( proxyHost );
691 if ( proxyPort )
692 {
693 connection += ':' + QString::number( proxyPort );
694 }
695 CPLSetConfigOption( "GDAL_HTTP_PROXY", connection.toUtf8() );
696 if ( !proxyUser.isEmpty() )
697 {
698 QString credentials( proxyUser );
699 if ( !proxyPassword.isEmpty() )
700 {
701 credentials += ':' + proxyPassword;
702 }
703 CPLSetConfigOption( "GDAL_HTTP_PROXYUSERPWD", credentials.toUtf8() );
704 }
705 }
706 }
707 }
708}
709
710bool QgsGdalUtils::pathIsCheapToOpen( const QString &path, int smallFileSizeLimit )
711{
712 const QFileInfo info( path );
713 const long long size = info.size();
714
715 // if size could not be determined, safest to flag path as expensive
716 if ( size == 0 )
717 return false;
718
719 const QString suffix = info.suffix().toLower();
720 static const QStringList sFileSizeDependentExtensions { u"xlsx"_s, u"ods"_s, u"csv"_s };
721 if ( sFileSizeDependentExtensions.contains( suffix ) )
722 {
723 // path corresponds to a file type which is only cheap to open for small files
724 return size < smallFileSizeLimit;
725 }
726
727 // treat all other formats as expensive.
728 // TODO -- flag formats which only require a quick header parse as cheap
729 return false;
730}
731
733{
734#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 4, 0 )
735 // get supported extensions
736 static std::once_flag initialized;
737 static QStringList SUPPORTED_DB_LAYERS_EXTENSIONS;
738 std::call_once( initialized, [] {
739 // iterate through all of the supported drivers, adding the corresponding file extensions for
740 // types which advertise multilayer support
741 GDALDriverH driver = nullptr;
742
743 QSet< QString > extensions;
744
745 for ( int i = 0; i < GDALGetDriverCount(); ++i )
746 {
747 driver = GDALGetDriver( i );
748 if ( !driver )
749 {
750 QgsLogger::warning( "unable to get driver " + QString::number( i ) );
751 continue;
752 }
753
754 bool isMultiLayer = false;
755 if ( QString( GDALGetMetadataItem( driver, GDAL_DCAP_RASTER, nullptr ) ) == "YES"_L1 )
756 {
757 if ( GDALGetMetadataItem( driver, GDAL_DMD_SUBDATASETS, nullptr ) )
758 {
759 isMultiLayer = true;
760 }
761 }
762 if ( !isMultiLayer && QString( GDALGetMetadataItem( driver, GDAL_DCAP_VECTOR, nullptr ) ) == "YES"_L1 )
763 {
764 if ( GDALGetMetadataItem( driver, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, nullptr ) )
765 {
766 isMultiLayer = true;
767 }
768 }
769
770 if ( !isMultiLayer )
771 continue;
772
773 const QString driverExtensions = GDALGetMetadataItem( driver, GDAL_DMD_EXTENSIONS, "" );
774 if ( driverExtensions.isEmpty() )
775 continue;
776
777 const QStringList splitExtensions = driverExtensions.split( ' ', Qt::SkipEmptyParts );
778
779 for ( const QString &ext : splitExtensions )
780 {
781 // maintain older behavior -- don't always expose tiff files as containers
782 if ( ext == "tif"_L1 || ext == "tiff"_L1 )
783 continue;
784
785 extensions.insert( ext );
786 }
787 }
788
789 SUPPORTED_DB_LAYERS_EXTENSIONS = QStringList( extensions.constBegin(), extensions.constEnd() );
790 } );
791 return SUPPORTED_DB_LAYERS_EXTENSIONS;
792
793#else
794 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,
795 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 };
796 return SUPPORTED_DB_LAYERS_EXTENSIONS;
797#endif
798}
799
800QString QgsGdalUtils::vsiPrefixForPath( const QString &path )
801{
802 const QStringList vsiPrefixes = QgsGdalUtils::vsiArchivePrefixes();
803
804 const thread_local QRegularExpression vsiRx( u"^(/vsi.+?/)"_s, QRegularExpression::PatternOption::CaseInsensitiveOption );
805 const QRegularExpressionMatch vsiMatch = vsiRx.match( path );
806 if ( vsiMatch.hasMatch() )
807 return vsiMatch.captured( 1 );
808
809 if ( path.endsWith( ".shp.zip"_L1, Qt::CaseInsensitive ) || path.endsWith( ".gdb.zip"_L1, Qt::CaseInsensitive ) )
810 {
811 // GDAL 3.1 Shapefile/OpenFileGDB drivers directly handle .shp.zip and .gdb.zip files
812 if ( GDALIdentifyDriverEx( path.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr ) )
813 return QString();
814 return u"/vsizip/"_s;
815 }
816 else if ( path.endsWith( ".zip"_L1, Qt::CaseInsensitive ) )
817 {
818 // GTFS driver directly handles .zip files
819 const char *const apszAllowedDrivers[] = { "GTFS", nullptr };
820 if ( GDALIdentifyDriverEx( path.toUtf8().constData(), GDAL_OF_VECTOR, apszAllowedDrivers, nullptr ) )
821 return QString();
822 return u"/vsizip/"_s;
823 }
824 else if ( path.endsWith( ".tar"_L1, Qt::CaseInsensitive ) || path.endsWith( ".tar.gz"_L1, Qt::CaseInsensitive ) || path.endsWith( ".tgz"_L1, Qt::CaseInsensitive ) )
825 return u"/vsitar/"_s;
826 else if ( path.endsWith( ".gz"_L1, Qt::CaseInsensitive ) )
827 return u"/vsigzip/"_s;
828#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 7, 0 )
829 else if ( vsiPrefixes.contains( u"/vsi7z/"_s ) &&
830 ( path.endsWith( ".7z"_L1, Qt::CaseInsensitive ) ||
831 path.endsWith( ".lpk"_L1, Qt::CaseInsensitive ) ||
832 path.endsWith( ".lpkx"_L1, Qt::CaseInsensitive ) ||
833 path.endsWith( ".mpk"_L1, Qt::CaseInsensitive ) ||
834 path.endsWith( ".mpkx"_L1, Qt::CaseInsensitive ) ) )
835 return u"/vsi7z/"_s;
836 else if ( vsiPrefixes.contains( u"/vsirar/"_s ) && path.endsWith( ".rar"_L1, Qt::CaseInsensitive ) )
837 return u"/vsirar/"_s;
838#endif
839
840 return QString();
841}
842
844{
845 QStringList res {
846 u"/vsizip/"_s,
847 u"/vsitar/"_s,
848 u"/vsigzip/"_s,
849 };
850#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 7, 0 )
851 res.append( u"/vsi7z/"_s );
852 res.append( u"/vsirar/"_s );
853#endif
854 return res;
855}
856
857QList<QgsGdalUtils::VsiNetworkFileSystemDetails> QgsGdalUtils::vsiNetworkFileSystems()
858{
859 // get supported extensions
860 static std::once_flag initialized;
861 static QList<QgsGdalUtils::VsiNetworkFileSystemDetails> VSI_FILE_SYSTEM_DETAILS;
862 std::call_once( initialized, [] {
863 if ( char **papszPrefixes = VSIGetFileSystemsPrefixes() )
864 {
865 for ( int i = 0; papszPrefixes[i]; i++ )
866 {
868 details.identifier = QString( papszPrefixes[i] );
869 if ( details.identifier.startsWith( '/' ) )
870 details.identifier = details.identifier.mid( 1 );
871 if ( details.identifier.endsWith( '/' ) )
872 details.identifier.chop( 1 );
873
874 if ( details.identifier == "vsicurl"_L1 )
875 details.name = QObject::tr( "HTTP/HTTPS/FTP" );
876 else if ( details.identifier == "vsis3"_L1 )
877 details.name = QObject::tr( "AWS S3" );
878 else if ( details.identifier == "vsigs"_L1 )
879 details.name = QObject::tr( "Google Cloud Storage" );
880 else if ( details.identifier == "vsiaz"_L1 )
881 details.name = QObject::tr( "Microsoft Azure Blob" );
882 else if ( details.identifier == "vsiadls"_L1 )
883 details.name = QObject::tr( "Microsoft Azure Data Lake Storage" );
884 else if ( details.identifier == "vsioss"_L1 )
885 details.name = QObject::tr( "Alibaba Cloud OSS" );
886 else if ( details.identifier == "vsiswift"_L1 )
887 details.name = QObject::tr( "OpenStack Swift Object Storage" );
888 else if ( details.identifier == "vsihdfs"_L1 )
889 details.name = QObject::tr( "Hadoop File System" );
890 else
891 continue;
892 VSI_FILE_SYSTEM_DETAILS.append( details );
893 }
894
895 CSLDestroy( papszPrefixes );
896 }
897 } );
898
899 return VSI_FILE_SYSTEM_DETAILS;
900}
901
902bool QgsGdalUtils::isVsiArchivePrefix( const QString &prefix )
903{
904 const QStringList prefixes = vsiArchivePrefixes();
905 for ( const QString &archivePrefix : prefixes )
906 {
907 // catch chained prefixes, eg "/vsizip/vsicurl"
908 if ( prefix.contains( archivePrefix ) )
909 return true;
910 }
911 return false;
912}
913
915{
916 QStringList res {
917 u".zip"_s,
918 u".tar"_s,
919 u".tar.gz"_s,
920 u".tgz"_s,
921 u".gz"_s,
922 };
923#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 7, 0 )
924 res.append( { u".7z"_s, u".lpk"_s, u".lpkx"_s, u".mpk"_s, u".mpkx"_s, u".rar"_s } );
925#endif
926 return res;
927}
928
929bool QgsGdalUtils::isVsiArchiveFileExtension( const QString &extension )
930{
931 const QString extWithDot = extension.startsWith( '.' ) ? extension : ( '.' + extension );
932 return vsiArchiveFileExtensions().contains( extWithDot.toLower() );
933}
934
936{
937 if ( prefix.isEmpty() )
939
940 QString vsiPrefix = prefix;
941 if ( vsiPrefix.startsWith( '/' ) )
942 vsiPrefix = vsiPrefix.mid( 1 );
943 if ( vsiPrefix.endsWith( '/' ) )
944 vsiPrefix.chop( 1 );
945
946 if ( !vsiPrefix.startsWith( "vsi"_L1 ) )
948
949 if ( vsiPrefix == "vsizip"_L1 || vsiPrefix == "vsigzip"_L1 || vsiPrefix == "vsitar"_L1 || vsiPrefix == "vsi7z"_L1 || vsiPrefix == "vsirar"_L1 )
951
952 else if ( vsiPrefix == "vsicurl"_L1 || vsiPrefix == "vsicurl_streaming"_L1 )
954
955 else if ( vsiPrefix == "vsis3"_L1
956 || vsiPrefix == "vsicurl_streaming"_L1
957 || vsiPrefix == "vsigs"_L1
958 || vsiPrefix == "vsigs_streaming"_L1
959 || vsiPrefix == "vsiaz"_L1
960 || vsiPrefix == "vsiaz_streaming"_L1
961 || vsiPrefix == "vsiadls"_L1
962 || vsiPrefix == "vsioss"_L1
963 || vsiPrefix == "vsioss_streaming"_L1
964 || vsiPrefix == "vsiswift"_L1
965 || vsiPrefix == "vsiswift_streaming"_L1
966 || vsiPrefix == "vsihdfs"_L1
967 || vsiPrefix == "vsiwebhdfs"_L1 )
969
970 else if ( vsiPrefix == "vsimem"_L1 )
972
974}
975
976bool QgsGdalUtils::vrtMatchesLayerType( const QString &vrtPath, Qgis::LayerType type )
977{
978 CPLPushErrorHandler( CPLQuietErrorHandler );
979 CPLErrorReset();
980 GDALDriverH hDriver = nullptr;
981
982 switch ( type )
983 {
985 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr );
986 break;
987
989 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_RASTER, nullptr, nullptr );
990 break;
991
999 break;
1000 }
1001
1002 CPLPopErrorHandler();
1003 return static_cast< bool >( hDriver );
1004}
1005
1007{
1008 if ( hDriver )
1009 {
1010 const QString gdalDriverHelpTopic = GDALGetMetadataItem( hDriver, GDAL_DMD_HELPTOPIC, nullptr ); // e.g. "drivers/vector/ili.html"
1011 if ( !gdalDriverHelpTopic.isEmpty() )
1012 return u"https://gdal.org/%1"_s.arg( gdalDriverHelpTopic );
1013 }
1014 return QString();
1015}
1016
1018{
1019 GDALDriverH hDriver = GDALGetDriverByName( "MRF" );
1020 if ( hDriver )
1021 {
1022 const char *creationOptions = GDALGetMetadataItem( hDriver, GDAL_DMD_CREATIONOPTIONLIST, NULL );
1023 if ( creationOptions && strstr( creationOptions, "LERC" ) )
1024 {
1025 return true;
1026 }
1027 }
1028 return false;
1029}
1030
1032{
1033 GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
1034 if ( hDriver )
1035 {
1036 const char *creationOptions = GDALGetMetadataItem( hDriver, GDAL_DMD_CREATIONOPTIONLIST, NULL );
1037 if ( creationOptions && strstr( creationOptions, "LERC" ) )
1038 {
1039 return true;
1040 }
1041 }
1042 return false;
1043}
1044
1045bool QgsGdalUtils::applyVsiCredentialOptions( const QString &prefix, const QString &path, const QVariantMap &options )
1046{
1047 QString vsiPrefix = prefix;
1048 if ( !vsiPrefix.startsWith( '/' ) )
1049 vsiPrefix.prepend( '/' );
1050 if ( !vsiPrefix.endsWith( '/' ) )
1051 vsiPrefix.append( '/' );
1052
1053 QString vsiPath = path;
1054 if ( vsiPath.endsWith( '/' ) )
1055 vsiPath.chop( 1 );
1056
1057 const QString bucket = vsiPrefix + vsiPath;
1058
1059 for ( auto it = options.constBegin(); it != options.constEnd(); ++it )
1060 {
1061#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 6, 0 )
1062 VSISetPathSpecificOption( bucket.toUtf8().constData(), it.key().toUtf8().constData(), it.value().toString().toUtf8().constData() );
1063#elif GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 5, 0 )
1064 VSISetCredential( bucket.toUtf8().constData(), it.key().toUtf8().constData(), it.value().toString().toUtf8().constData() );
1065#else
1066 ( void ) bucket;
1067 QgsMessageLog::logMessage( QObject::tr( "Cannot use VSI credential options on GDAL versions earlier than 3.5" ), u"GDAL"_s, Qgis::MessageLevel::Critical );
1068 return false;
1069#endif
1070 }
1071 return true;
1072}
1073#endif
1074
1075QgsGdalProgressAdapter::QgsGdalProgressAdapter( QgsFeedback *feedback, double startPercentage, double endPercentage )
1076 : mFeedback( feedback )
1077 , mStartPercentage( startPercentage )
1078 , mEndPercentage( endPercentage )
1079{}
1080
1081/* static */
1082int CPL_STDCALL QgsGdalProgressAdapter::progressCallback( double dfComplete, const char * /* pszMessage */, void *pProgressArg )
1083{
1084 const QgsGdalProgressAdapter *adapter = static_cast<QgsGdalProgressAdapter *>( pProgressArg );
1085
1086 if ( QgsFeedback *feedback = adapter->mFeedback )
1087 {
1088 feedback->setProgress( adapter->mStartPercentage + ( adapter->mEndPercentage - adapter->mStartPercentage ) * dfComplete );
1089
1090 if ( feedback->isCanceled() )
1091 return false;
1092 }
1093
1094 return true;
1095}
VsiHandlerType
GDAL VSI handler types.
Definition qgis.h:6199
@ Memory
In-memory types (e.g. vsimem).
Definition qgis.h:6204
@ Invalid
Invalid type, i.e. not a valid VSI handler.
Definition qgis.h:6200
@ Other
All other types.
Definition qgis.h:6205
@ Cloud
Specific cloud provider types (e.g. vsis3).
Definition qgis.h:6203
@ Archive
File archive type (e.g. vsizip).
Definition qgis.h:6201
@ Network
Generic network types (e.g. vsicurl).
Definition qgis.h:6202
RasterResamplingMethod
Resampling method for raster provider-level resampling.
Definition qgis.h:1602
@ Lanczos
Lanczos windowed sinc interpolation (6x6 kernel).
Definition qgis.h:1607
@ Nearest
Nearest-neighbour resampling.
Definition qgis.h:1603
@ Mode
Mode (selects the value which appears most often of all the sampled points).
Definition qgis.h:1609
@ Bilinear
Bilinear (2x2 kernel) resampling.
Definition qgis.h:1604
@ Average
Average resampling.
Definition qgis.h:1608
@ CubicSpline
Cubic B-Spline Approximation (4x4 kernel).
Definition qgis.h:1606
@ Cubic
Cubic Convolution Approximation (4x4 kernel) resampling.
Definition qgis.h:1605
@ Gauss
Gauss blurring.
Definition qgis.h:1610
@ 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:2582
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 supportsTiffLercCompression()
Returns true if the GDAL library used by this QGIS install supports the LERC compression technique fo...
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 bool supportsMrfLercCompression()
Returns true if the GDAL library used by this QGIS install supports the LERC compression technique fo...
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 const QgsSettingsEntryBool * settingsProxyEnabled
Settings entry for whether proxy is enabled.
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
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".