QGIS API Documentation 4.3.0-Master (16eddb2670e)
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
481 GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
482 if ( myGdalDriver )
483 {
484 // first report details and help page
485 CSLConstList GDALmetadata = GDALGetMetadata( myGdalDriver, nullptr );
486 message += "<h3>" + QCoreApplication::translate( "QgsGdalUtils", "Format Details" ) + "</h3>";
487 message += "<b>" + QCoreApplication::translate( "QgsGdalUtils", "Extension:" ) + u"</b> %1<br>"_s.arg( CSLFetchNameValue( GDALmetadata, GDAL_DMD_EXTENSION ) );
488 message += "<b>"
489 + QCoreApplication::translate( "QgsGdalUtils", "Short Name:" )
490 + u"</b> %1 / <b>"_s.arg( GDALGetDriverShortName( myGdalDriver ) )
491 + QCoreApplication::translate( "QgsGdalUtils", "Long Name:" )
492 + u"</b> %2<br>"_s.arg( GDALGetDriverLongName( myGdalDriver ) );
493 const QString helpUrl = gdalDocumentationUrlForDriver( myGdalDriver );
494 if ( !helpUrl.isEmpty() )
495 {
496 message += "<b>" + QCoreApplication::translate( "QgsGdalUtils", "Help page:" ) + u"</b> <a href=\"%1\">%1</a>"_s.arg( helpUrl );
497 }
498
499 // next get creation options
500 // serialize xml and convert it to html
501 CPLXMLNode *psCOL = CPLParseXMLString( GDALGetMetadataItem( myGdalDriver, GDAL_DMD_CREATIONOPTIONLIST, "" ) );
502 char *pszFormattedXML = CPLSerializeXMLTree( psCOL );
503 if ( pszFormattedXML )
504 {
505 QDomDocument doc;
506 if ( doc.setContent( QString( pszFormattedXML ) ) )
507 {
508 message += "<h3>" + QCoreApplication::translate( "QgsGdalUtils", "Creation Options" ) + "</h3>";
509 message += "<dl>";
510 QDomNodeList options = doc.elementsByTagName( u"Option"_s );
511 for ( int i = 0; i < options.count(); ++i )
512 {
513 QDomElement element = options.at( i ).toElement();
514
515 const QString optionName = element.attribute( u"name"_s );
516 const QString optionType = element.attribute( u"type"_s );
517 const QString optionDefault = element.attribute( u"default"_s );
518 const QString optionDescription = element.attribute( u"description"_s );
519 message
520 += u"<dt><b>%1</b> (%2%3)</dt>"_s
521 .arg( optionName.toHtmlEscaped(), optionType.toHtmlEscaped(), optionDefault.isEmpty() ? QString() : ", " + QCoreApplication::translate( "QgsGdalUtils", "default: " ) + optionDefault.toHtmlEscaped() );
522 if ( !optionDescription.isEmpty() )
523 {
524 message += u"<dd>%1</dd>"_s.arg( optionDescription.toHtmlEscaped() );
525 }
526
527 // supported values for option
528 QDomNodeList nodes = element.elementsByTagName( u"Value"_s );
529 if ( !nodes.isEmpty() )
530 {
531 QStringList values;
532 for ( int j = 0; j < nodes.count(); ++j )
533 {
534 values << nodes.at( j ).toElement().text().toHtmlEscaped();
535 }
536 message += "<dd><i>" + QCoreApplication::translate( "QgsGdalUtils", "Possible values:" ) + u"</i> %1</dd>"_s.arg( values.join( ", " ) );
537 }
538 }
539
540 message += "</dl>";
541 }
542 }
543 if ( psCOL )
544 CPLDestroyXMLNode( psCOL );
545 if ( pszFormattedXML )
546 CPLFree( pszFormattedXML );
547 }
548
549 return message;
550}
551
552char **QgsGdalUtils::papszFromStringList( const QStringList &list )
553{
554 char **papszRetList = nullptr;
555 const auto constList = list;
556 for ( const QString &elem : constList )
557 {
558 papszRetList = CSLAddString( papszRetList, elem.toLocal8Bit().constData() );
559 }
560 return papszRetList;
561}
562
563QString QgsGdalUtils::validateCreationOptionsFormat( const QStringList &creationOptions, const QString &format )
564{
565 GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
566 if ( !myGdalDriver )
567 return u"invalid GDAL driver"_s;
568
569 char **papszOptions = papszFromStringList( creationOptions );
570 // get error string?
571 const int ok = GDALValidateCreationOptions( myGdalDriver, papszOptions );
572 CSLDestroy( papszOptions );
573
574 if ( !ok )
575 return u"Failed GDALValidateCreationOptions() test"_s;
576 return QString();
577}
578
579static void setRPCTransformerOptions( GDALDatasetH hSrcDS, char ***opts )
580{
581 if ( GDALGetMetadata( hSrcDS, "RPC" ) )
582 {
583 // Some RPC may contain a HEIGHT_DEFAULT entry, that must be used as the
584 // best default for RPC_HEIGHT. See https://github.com/OSGeo/gdal/pull/11989
585 const char *heightStr = GDALGetMetadataItem( hSrcDS, "HEIGHT_DEFAULT", "RPC" );
586 if ( !heightStr )
587 {
588 // Otherwise well-behaved RPC should have height offset which is also
589 // a resaonable default for RPC_HEIGHT.
590 heightStr = GDALGetMetadataItem( hSrcDS, "HEIGHT_OFF", "RPC" );
591 }
592 if ( heightStr )
593 *opts = CSLAddNameValue( *opts, "RPC_HEIGHT", heightStr );
594 }
595}
596
597GDALDatasetH QgsGdalUtils::rpcAwareAutoCreateWarpedVrt( GDALDatasetH hSrcDS, const char *pszSrcWKT, const char *pszDstWKT, GDALResampleAlg eResampleAlg, double dfMaxError, const GDALWarpOptions *psOptionsIn )
598{
599 char **opts = nullptr;
600 setRPCTransformerOptions( hSrcDS, &opts );
601 GDALDatasetH hRetDS = GDALAutoCreateWarpedVRTEx( hSrcDS, pszSrcWKT, pszDstWKT, eResampleAlg, dfMaxError, psOptionsIn, opts );
602 CSLDestroy( opts );
603 return hRetDS;
604}
605
606void *QgsGdalUtils::rpcAwareCreateTransformer( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, char **papszOptions )
607{
608 char **opts = CSLDuplicate( papszOptions );
609 setRPCTransformerOptions( hSrcDS, &opts );
610 void *transformer = GDALCreateGenImgProjTransformer2( hSrcDS, hDstDS, opts );
611 CSLDestroy( opts );
612 return transformer;
613}
614
616{
617 switch ( dataType )
618 {
620 return GDALDataType::GDT_Unknown;
621 break;
623 return GDALDataType::GDT_Byte;
624 break;
626#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 7, 0 )
627 return GDALDataType::GDT_Int8;
628#else
629 return GDALDataType::GDT_Unknown;
630#endif
631 break;
633 return GDALDataType::GDT_UInt16;
634 break;
636 return GDALDataType::GDT_Int16;
637 break;
639 return GDALDataType::GDT_UInt32;
640 break;
642 return GDALDataType::GDT_Int32;
643 break;
645 return GDALDataType::GDT_Float32;
646 break;
648 return GDALDataType::GDT_Float64;
649 break;
651 return GDALDataType::GDT_CInt16;
652 break;
654 return GDALDataType::GDT_CInt32;
655 break;
657 return GDALDataType::GDT_CFloat32;
658 break;
660 return GDALDataType::GDT_CFloat64;
661 break;
664 return GDALDataType::GDT_Unknown;
665 break;
666 };
667
668 return GDALDataType::GDT_Unknown;
669}
670
672{
673 GDALResampleAlg eResampleAlg = GRA_NearestNeighbour;
674 switch ( method )
675 {
677 case Qgis::RasterResamplingMethod::Gauss: // Gauss not available in GDALResampleAlg
678 eResampleAlg = GRA_NearestNeighbour;
679 break;
680
682 eResampleAlg = GRA_Bilinear;
683 break;
684
686 eResampleAlg = GRA_Cubic;
687 break;
688
690 eResampleAlg = GRA_CubicSpline;
691 break;
692
694 eResampleAlg = GRA_Lanczos;
695 break;
696
698 eResampleAlg = GRA_Average;
699 break;
700
702 eResampleAlg = GRA_Mode;
703 break;
704 }
705
706 return eResampleAlg;
707}
708
709#ifndef QT_NO_NETWORKPROXY
711{
712 // Check proxy configuration, they are application level but
713 // instead of adding an API and complex signal/slot connections
714 // given the limited cost of checking them on every provider instantiation
715 // we can do it here so that new settings are applied whenever a new layer
716 // is created.
717 // Check that proxy is enabled
719 {
720 // Get the first configured proxy
721 QList<QNetworkProxy> proxies( QgsNetworkAccessManager::instance()->proxyFactory()->queryProxy() );
722 if ( !proxies.isEmpty() )
723 {
724 const QNetworkProxy proxy( proxies.first() );
725 // TODO/FIXME: check excludes (the GDAL config options are global, we need a per-connection config option)
726
727 const QString proxyHost( proxy.hostName() );
728 const quint16 proxyPort( proxy.port() );
729
730 const QString proxyUser( proxy.user() );
731 const QString proxyPassword( proxy.password() );
732
733 if ( !proxyHost.isEmpty() )
734 {
735 QString connection( proxyHost );
736 if ( proxyPort )
737 {
738 connection += ':' + QString::number( proxyPort );
739 }
740 CPLSetConfigOption( "GDAL_HTTP_PROXY", connection.toUtf8() );
741 if ( !proxyUser.isEmpty() )
742 {
743 QString credentials( proxyUser );
744 if ( !proxyPassword.isEmpty() )
745 {
746 credentials += ':' + proxyPassword;
747 }
748 CPLSetConfigOption( "GDAL_HTTP_PROXYUSERPWD", credentials.toUtf8() );
749 }
750 }
751 }
752 }
753}
754
755bool QgsGdalUtils::pathIsCheapToOpen( const QString &path, int smallFileSizeLimit )
756{
757 const QFileInfo info( path );
758 const long long size = info.size();
759
760 // if size could not be determined, safest to flag path as expensive
761 if ( size == 0 )
762 return false;
763
764 const QString suffix = info.suffix().toLower();
765 static const QStringList sFileSizeDependentExtensions { u"xlsx"_s, u"ods"_s, u"csv"_s };
766 if ( sFileSizeDependentExtensions.contains( suffix ) )
767 {
768 // path corresponds to a file type which is only cheap to open for small files
769 return size < smallFileSizeLimit;
770 }
771
772 // treat all other formats as expensive.
773 // TODO -- flag formats which only require a quick header parse as cheap
774 return false;
775}
776
778{
779#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 4, 0 )
780 // get supported extensions
781 static std::once_flag initialized;
782 static QStringList SUPPORTED_DB_LAYERS_EXTENSIONS;
783 std::call_once( initialized, [] {
784 // iterate through all of the supported drivers, adding the corresponding file extensions for
785 // types which advertise multilayer support
786 GDALDriverH driver = nullptr;
787
788 QSet< QString > extensions;
789
790 for ( int i = 0; i < GDALGetDriverCount(); ++i )
791 {
792 driver = GDALGetDriver( i );
793 if ( !driver )
794 {
795 QgsLogger::warning( "unable to get driver " + QString::number( i ) );
796 continue;
797 }
798
799 bool isMultiLayer = false;
800 if ( QString( GDALGetMetadataItem( driver, GDAL_DCAP_RASTER, nullptr ) ) == "YES"_L1 )
801 {
802 if ( GDALGetMetadataItem( driver, GDAL_DMD_SUBDATASETS, nullptr ) )
803 {
804 isMultiLayer = true;
805 }
806 }
807 if ( !isMultiLayer && QString( GDALGetMetadataItem( driver, GDAL_DCAP_VECTOR, nullptr ) ) == "YES"_L1 )
808 {
809 if ( GDALGetMetadataItem( driver, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, nullptr ) )
810 {
811 isMultiLayer = true;
812 }
813 }
814
815 if ( !isMultiLayer )
816 continue;
817
818 const QString driverExtensions = GDALGetMetadataItem( driver, GDAL_DMD_EXTENSIONS, "" );
819 if ( driverExtensions.isEmpty() )
820 continue;
821
822 const QStringList splitExtensions = driverExtensions.split( ' ', Qt::SkipEmptyParts );
823
824 for ( const QString &ext : splitExtensions )
825 {
826 // maintain older behavior -- don't always expose tiff files as containers
827 if ( ext == "tif"_L1 || ext == "tiff"_L1 )
828 continue;
829
830 extensions.insert( ext );
831 }
832 }
833
834 SUPPORTED_DB_LAYERS_EXTENSIONS = QStringList( extensions.constBegin(), extensions.constEnd() );
835 } );
836 return SUPPORTED_DB_LAYERS_EXTENSIONS;
837
838#else
839 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,
840 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 };
841 return SUPPORTED_DB_LAYERS_EXTENSIONS;
842#endif
843}
844
845QString QgsGdalUtils::vsiPrefixForPath( const QString &path )
846{
847 const QStringList vsiPrefixes = QgsGdalUtils::vsiArchivePrefixes();
848
849 const thread_local QRegularExpression vsiRx( u"^(/vsi.+?/)"_s, QRegularExpression::PatternOption::CaseInsensitiveOption );
850 const QRegularExpressionMatch vsiMatch = vsiRx.match( path );
851 if ( vsiMatch.hasMatch() )
852 return vsiMatch.captured( 1 );
853
854 if ( path.endsWith( ".shp.zip"_L1, Qt::CaseInsensitive ) || path.endsWith( ".gdb.zip"_L1, Qt::CaseInsensitive ) )
855 {
856 // GDAL 3.1 Shapefile/OpenFileGDB drivers directly handle .shp.zip and .gdb.zip files
857 if ( GDALIdentifyDriverEx( path.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr ) )
858 return QString();
859 return u"/vsizip/"_s;
860 }
861 else if ( path.endsWith( ".zip"_L1, Qt::CaseInsensitive ) )
862 {
863 // GTFS driver directly handles .zip files
864 const char *const apszAllowedDrivers[] = { "GTFS", nullptr };
865 if ( GDALIdentifyDriverEx( path.toUtf8().constData(), GDAL_OF_VECTOR, apszAllowedDrivers, nullptr ) )
866 return QString();
867 return u"/vsizip/"_s;
868 }
869 else if ( path.endsWith( ".tar"_L1, Qt::CaseInsensitive ) || path.endsWith( ".tar.gz"_L1, Qt::CaseInsensitive ) || path.endsWith( ".tgz"_L1, Qt::CaseInsensitive ) )
870 return u"/vsitar/"_s;
871 else if ( path.endsWith( ".gz"_L1, Qt::CaseInsensitive ) )
872 return u"/vsigzip/"_s;
873#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 7, 0 )
874 else if ( vsiPrefixes.contains( u"/vsi7z/"_s ) &&
875 ( path.endsWith( ".7z"_L1, Qt::CaseInsensitive ) ||
876 path.endsWith( ".lpk"_L1, Qt::CaseInsensitive ) ||
877 path.endsWith( ".lpkx"_L1, Qt::CaseInsensitive ) ||
878 path.endsWith( ".mpk"_L1, Qt::CaseInsensitive ) ||
879 path.endsWith( ".mpkx"_L1, Qt::CaseInsensitive ) ) )
880 return u"/vsi7z/"_s;
881 else if ( vsiPrefixes.contains( u"/vsirar/"_s ) && path.endsWith( ".rar"_L1, Qt::CaseInsensitive ) )
882 return u"/vsirar/"_s;
883#endif
884
885 return QString();
886}
887
889{
890 QStringList res {
891 u"/vsizip/"_s,
892 u"/vsitar/"_s,
893 u"/vsigzip/"_s,
894 };
895#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 7, 0 )
896 res.append( u"/vsi7z/"_s );
897 res.append( u"/vsirar/"_s );
898#endif
899 return res;
900}
901
902QList<QgsGdalUtils::VsiNetworkFileSystemDetails> QgsGdalUtils::vsiNetworkFileSystems()
903{
904 // get supported extensions
905 static std::once_flag initialized;
906 static QList<QgsGdalUtils::VsiNetworkFileSystemDetails> VSI_FILE_SYSTEM_DETAILS;
907 std::call_once( initialized, [] {
908 if ( char **papszPrefixes = VSIGetFileSystemsPrefixes() )
909 {
910 for ( int i = 0; papszPrefixes[i]; i++ )
911 {
913 details.identifier = QString( papszPrefixes[i] );
914 if ( details.identifier.startsWith( '/' ) )
915 details.identifier = details.identifier.mid( 1 );
916 if ( details.identifier.endsWith( '/' ) )
917 details.identifier.chop( 1 );
918
919 if ( details.identifier == "vsicurl"_L1 )
920 details.name = QObject::tr( "HTTP/HTTPS/FTP" );
921 else if ( details.identifier == "vsis3"_L1 )
922 details.name = QObject::tr( "AWS S3" );
923 else if ( details.identifier == "vsigs"_L1 )
924 details.name = QObject::tr( "Google Cloud Storage" );
925 else if ( details.identifier == "vsiaz"_L1 )
926 details.name = QObject::tr( "Microsoft Azure Blob" );
927 else if ( details.identifier == "vsiadls"_L1 )
928 details.name = QObject::tr( "Microsoft Azure Data Lake Storage" );
929 else if ( details.identifier == "vsioss"_L1 )
930 details.name = QObject::tr( "Alibaba Cloud OSS" );
931 else if ( details.identifier == "vsiswift"_L1 )
932 details.name = QObject::tr( "OpenStack Swift Object Storage" );
933 else if ( details.identifier == "vsihdfs"_L1 )
934 details.name = QObject::tr( "Hadoop File System" );
935 else
936 continue;
937 VSI_FILE_SYSTEM_DETAILS.append( details );
938 }
939
940 CSLDestroy( papszPrefixes );
941 }
942 } );
943
944 return VSI_FILE_SYSTEM_DETAILS;
945}
946
947bool QgsGdalUtils::isVsiArchivePrefix( const QString &prefix )
948{
949 const QStringList prefixes = vsiArchivePrefixes();
950 for ( const QString &archivePrefix : prefixes )
951 {
952 // catch chained prefixes, eg "/vsizip/vsicurl"
953 if ( prefix.contains( archivePrefix ) )
954 return true;
955 }
956 return false;
957}
958
960{
961 QStringList res {
962 u".zip"_s,
963 u".tar"_s,
964 u".tar.gz"_s,
965 u".tgz"_s,
966 u".gz"_s,
967 };
968#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 7, 0 )
969 res.append( { u".7z"_s, u".lpk"_s, u".lpkx"_s, u".mpk"_s, u".mpkx"_s, u".rar"_s } );
970#endif
971 return res;
972}
973
974bool QgsGdalUtils::isVsiArchiveFileExtension( const QString &extension )
975{
976 const QString extWithDot = extension.startsWith( '.' ) ? extension : ( '.' + extension );
977 return vsiArchiveFileExtensions().contains( extWithDot.toLower() );
978}
979
981{
982 if ( prefix.isEmpty() )
984
985 QString vsiPrefix = prefix;
986 if ( vsiPrefix.startsWith( '/' ) )
987 vsiPrefix = vsiPrefix.mid( 1 );
988 if ( vsiPrefix.endsWith( '/' ) )
989 vsiPrefix.chop( 1 );
990
991 if ( !vsiPrefix.startsWith( "vsi"_L1 ) )
993
994 if ( vsiPrefix == "vsizip"_L1 || vsiPrefix == "vsigzip"_L1 || vsiPrefix == "vsitar"_L1 || vsiPrefix == "vsi7z"_L1 || vsiPrefix == "vsirar"_L1 )
996
997 else if ( vsiPrefix == "vsicurl"_L1 || vsiPrefix == "vsicurl_streaming"_L1 )
999
1000 else if ( vsiPrefix == "vsis3"_L1
1001 || vsiPrefix == "vsicurl_streaming"_L1
1002 || vsiPrefix == "vsigs"_L1
1003 || vsiPrefix == "vsigs_streaming"_L1
1004 || vsiPrefix == "vsiaz"_L1
1005 || vsiPrefix == "vsiaz_streaming"_L1
1006 || vsiPrefix == "vsiadls"_L1
1007 || vsiPrefix == "vsioss"_L1
1008 || vsiPrefix == "vsioss_streaming"_L1
1009 || vsiPrefix == "vsiswift"_L1
1010 || vsiPrefix == "vsiswift_streaming"_L1
1011 || vsiPrefix == "vsihdfs"_L1
1012 || vsiPrefix == "vsiwebhdfs"_L1 )
1014
1015 else if ( vsiPrefix == "vsimem"_L1 )
1017
1019}
1020
1021bool QgsGdalUtils::vrtMatchesLayerType( const QString &vrtPath, Qgis::LayerType type )
1022{
1023 CPLPushErrorHandler( CPLQuietErrorHandler );
1024 CPLErrorReset();
1025 GDALDriverH hDriver = nullptr;
1026
1027 switch ( type )
1028 {
1030 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr );
1031 break;
1032
1034 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_RASTER, nullptr, nullptr );
1035 break;
1036
1044 break;
1045 }
1046
1047 CPLPopErrorHandler();
1048 return static_cast< bool >( hDriver );
1049}
1050
1052{
1053 if ( hDriver )
1054 {
1055 const QString gdalDriverHelpTopic = GDALGetMetadataItem( hDriver, GDAL_DMD_HELPTOPIC, nullptr ); // e.g. "drivers/vector/ili.html"
1056 if ( !gdalDriverHelpTopic.isEmpty() )
1057 return u"https://gdal.org/%1"_s.arg( gdalDriverHelpTopic );
1058 }
1059 return QString();
1060}
1061
1063{
1064 GDALDriverH hDriver = GDALGetDriverByName( "MRF" );
1065 if ( hDriver )
1066 {
1067 const char *creationOptions = GDALGetMetadataItem( hDriver, GDAL_DMD_CREATIONOPTIONLIST, NULL );
1068 if ( creationOptions && strstr( creationOptions, "LERC" ) )
1069 {
1070 return true;
1071 }
1072 }
1073 return false;
1074}
1075
1077{
1078 GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
1079 if ( hDriver )
1080 {
1081 const char *creationOptions = GDALGetMetadataItem( hDriver, GDAL_DMD_CREATIONOPTIONLIST, NULL );
1082 if ( creationOptions && strstr( creationOptions, "LERC" ) )
1083 {
1084 return true;
1085 }
1086 }
1087 return false;
1088}
1089
1090bool QgsGdalUtils::applyVsiCredentialOptions( const QString &prefix, const QString &path, const QVariantMap &options )
1091{
1092 QString vsiPrefix = prefix;
1093 if ( !vsiPrefix.startsWith( '/' ) )
1094 vsiPrefix.prepend( '/' );
1095 if ( !vsiPrefix.endsWith( '/' ) )
1096 vsiPrefix.append( '/' );
1097
1098 QString vsiPath = path;
1099 if ( vsiPath.endsWith( '/' ) )
1100 vsiPath.chop( 1 );
1101
1102 const QString bucket = vsiPrefix + vsiPath;
1103
1104 for ( auto it = options.constBegin(); it != options.constEnd(); ++it )
1105 {
1106#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 6, 0 )
1107 VSISetPathSpecificOption( bucket.toUtf8().constData(), it.key().toUtf8().constData(), it.value().toString().toUtf8().constData() );
1108#elif GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 5, 0 )
1109 VSISetCredential( bucket.toUtf8().constData(), it.key().toUtf8().constData(), it.value().toString().toUtf8().constData() );
1110#else
1111 ( void ) bucket;
1112 QgsMessageLog::logMessage( QObject::tr( "Cannot use VSI credential options on GDAL versions earlier than 3.5" ), u"GDAL"_s, Qgis::MessageLevel::Critical );
1113 return false;
1114#endif
1115 }
1116 return true;
1117}
1118#endif
1119
1120QgsGdalProgressAdapter::QgsGdalProgressAdapter( QgsFeedback *feedback, double startPercentage, double endPercentage )
1121 : mFeedback( feedback )
1122 , mStartPercentage( startPercentage )
1123 , mEndPercentage( endPercentage )
1124{}
1125
1126/* static */
1127int CPL_STDCALL QgsGdalProgressAdapter::progressCallback( double dfComplete, const char * /* pszMessage */, void *pProgressArg )
1128{
1129 const QgsGdalProgressAdapter *adapter = static_cast<QgsGdalProgressAdapter *>( pProgressArg );
1130
1131 if ( QgsFeedback *feedback = adapter->mFeedback )
1132 {
1133 feedback->setProgress( adapter->mStartPercentage + ( adapter->mEndPercentage - adapter->mStartPercentage ) * dfComplete );
1134
1135 if ( feedback->isCanceled() )
1136 return false;
1137 }
1138
1139 return true;
1140}
VsiHandlerType
GDAL VSI handler types.
Definition qgis.h:6564
@ Memory
In-memory types (e.g. vsimem).
Definition qgis.h:6569
@ Invalid
Invalid type, i.e. not a valid VSI handler.
Definition qgis.h:6565
@ Other
All other types.
Definition qgis.h:6570
@ Cloud
Specific cloud provider types (e.g. vsis3).
Definition qgis.h:6568
@ Archive
File archive type (e.g. vsizip).
Definition qgis.h:6566
@ Network
Generic network types (e.g. vsicurl).
Definition qgis.h:6567
RasterResamplingMethod
Resampling method for raster provider-level resampling.
Definition qgis.h:1653
@ Lanczos
Lanczos windowed sinc interpolation (6x6 kernel).
Definition qgis.h:1658
@ Nearest
Nearest-neighbour resampling.
Definition qgis.h:1654
@ Mode
Mode (selects the value which appears most often of all the sampled points).
Definition qgis.h:1660
@ Bilinear
Bilinear (2x2 kernel) resampling.
Definition qgis.h:1655
@ Average
Average resampling.
Definition qgis.h:1659
@ CubicSpline
Cubic B-Spline Approximation (4x4 kernel).
Definition qgis.h:1657
@ Cubic
Cubic Convolution Approximation (4x4 kernel) resampling.
Definition qgis.h:1656
@ Gauss
Gauss blurring.
Definition qgis.h:1661
@ Critical
Critical/error message.
Definition qgis.h:163
DataType
Raster data types.
Definition qgis.h:413
@ CInt32
Complex Int32.
Definition qgis.h:424
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:421
@ CFloat64
Complex Float64.
Definition qgis.h:426
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:418
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:428
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30).
Definition qgis.h:416
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:417
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:415
@ UnknownDataType
Unknown or unspecified type.
Definition qgis.h:414
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition qgis.h:427
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:420
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:422
@ CFloat32
Complex Float32.
Definition qgis.h:425
@ CInt16
Complex Int16.
Definition qgis.h:423
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:419
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:2634
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:71
Encapsulates details for a GDAL VSI network file system.
QString name
Translated, user-friendly name.
QString identifier
VSI handler identifier, eg "vsis3".