QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsgdalutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgdalutils.cpp
3 ----------------
4 begin : September 2018
5 copyright : (C) 2018 Even Rouault
6 email : even.rouault at spatialys.com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsgdalutils.h"
17#include "qgslogger.h"
19#include "qgssettings.h"
21#include "qgsrasterblock.h"
22
23#define CPL_SUPRESS_CPLUSPLUS //#spellok
24#include "gdal.h"
25#include "gdalwarper.h"
26#include "cpl_string.h"
27
28#include <QNetworkProxy>
29#include <QString>
30#include <QImage>
31#include <QFileInfo>
32#include <mutex>
33
34bool QgsGdalUtils::supportsRasterCreate( GDALDriverH driver )
35{
36 const QString driverShortName = GDALGetDriverShortName( driver );
37 if ( driverShortName == QLatin1String( "SQLite" ) )
38 {
39 // it supports Create() but only for vector side
40 return false;
41 }
42 return GDALGetMetadataItem( driver, GDAL_DCAP_CREATE, nullptr ) &&
43 GDALGetMetadataItem( driver, GDAL_DCAP_RASTER, nullptr );
44}
45
47{
48 return createMultiBandMemoryDataset( dataType, 1, extent, width, height, crs );
49}
50
51gdal::dataset_unique_ptr QgsGdalUtils::createMultiBandMemoryDataset( GDALDataType dataType, int bands, const QgsRectangle &extent, int width, int height, const QgsCoordinateReferenceSystem &crs )
52{
53 GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
54 if ( !hDriverMem )
55 {
57 }
58
59 gdal::dataset_unique_ptr hSrcDS( GDALCreate( hDriverMem, "", width, height, bands, dataType, nullptr ) );
60
61 const double cellSizeX = extent.width() / width;
62 const double cellSizeY = extent.height() / height;
63 double geoTransform[6];
64 geoTransform[0] = extent.xMinimum();
65 geoTransform[1] = cellSizeX;
66 geoTransform[2] = 0;
67 geoTransform[3] = extent.yMinimum() + ( cellSizeY * height );
68 geoTransform[4] = 0;
69 geoTransform[5] = -cellSizeY;
70
71 GDALSetProjection( hSrcDS.get(), crs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toLatin1().constData() );
72 GDALSetGeoTransform( hSrcDS.get(), geoTransform );
73 return hSrcDS;
74}
75
76gdal::dataset_unique_ptr QgsGdalUtils::createSingleBandTiffDataset( const QString &filename, GDALDataType dataType, const QgsRectangle &extent, int width, int height, const QgsCoordinateReferenceSystem &crs )
77{
78 const double cellSizeX = extent.width() / width;
79 const double cellSizeY = extent.height() / height;
80 double geoTransform[6];
81 geoTransform[0] = extent.xMinimum();
82 geoTransform[1] = cellSizeX;
83 geoTransform[2] = 0;
84 geoTransform[3] = extent.yMinimum() + ( cellSizeY * height );
85 geoTransform[4] = 0;
86 geoTransform[5] = -cellSizeY;
87
88 GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
89 if ( !hDriver )
90 {
92 }
93
94 // Create the output file.
95 gdal::dataset_unique_ptr hDstDS( GDALCreate( hDriver, filename.toUtf8().constData(), width, height, 1, dataType, nullptr ) );
96 if ( !hDstDS )
97 {
99 }
100
101 // Write out the projection definition.
102 GDALSetProjection( hDstDS.get(), crs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toLatin1().constData() );
103 GDALSetGeoTransform( hDstDS.get(), geoTransform );
104 return hDstDS;
105}
106
108{
109 if ( image.isNull() )
110 return nullptr;
111
112 const QRgb *rgb = reinterpret_cast<const QRgb *>( image.constBits() );
113 GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
114 if ( !hDriverMem )
115 {
116 return nullptr;
117 }
118 gdal::dataset_unique_ptr hSrcDS( GDALCreate( hDriverMem, "", image.width(), image.height(), 0, GDT_Byte, nullptr ) );
119
120 char **papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
121 << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
122 << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
123 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) + 2 ) );
124 GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
125 CSLDestroy( papszOptions );
126
127 papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
128 << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
129 << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
130 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) + 1 ) );
131 GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
132 CSLDestroy( papszOptions );
133
134 papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
135 << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
136 << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
137 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) ) );
138 GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
139 CSLDestroy( papszOptions );
140
141 papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
142 << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
143 << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
144 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) + 3 ) );
145 GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
146 CSLDestroy( papszOptions );
147
148 return hSrcDS;
149}
150
151gdal::dataset_unique_ptr QgsGdalUtils::blockToSingleBandMemoryDataset( int pixelWidth, int pixelHeight, const QgsRectangle &extent, void *block, GDALDataType dataType )
152{
153 if ( !block )
154 return nullptr;
155
156 GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
157 if ( !hDriverMem )
158 return nullptr;
159
160 const double cellSizeX = extent.width() / pixelWidth;
161 const double cellSizeY = extent.height() / pixelHeight;
162 double geoTransform[6];
163 geoTransform[0] = extent.xMinimum();
164 geoTransform[1] = cellSizeX;
165 geoTransform[2] = 0;
166 geoTransform[3] = extent.yMinimum() + ( cellSizeY * pixelHeight );
167 geoTransform[4] = 0;
168 geoTransform[5] = -cellSizeY;
169
170 gdal::dataset_unique_ptr hDstDS( GDALCreate( hDriverMem, "", pixelWidth, pixelHeight, 0, dataType, nullptr ) );
171
172 int dataTypeSize = GDALGetDataTypeSizeBytes( dataType );
173 char **papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
174 << QStringLiteral( "PIXELOFFSET=%1" ).arg( dataTypeSize )
175 << QStringLiteral( "LINEOFFSET=%1" ).arg( pixelWidth * dataTypeSize )
176 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( block ) ) );
177 GDALAddBand( hDstDS.get(), dataType, papszOptions );
178 CSLDestroy( papszOptions );
179
180 GDALSetGeoTransform( hDstDS.get(), geoTransform );
181
182 return hDstDS;
183}
184
186{
187 if ( !block )
188 return nullptr;
189
190 gdal::dataset_unique_ptr ret = blockToSingleBandMemoryDataset( block->width(), block->height(), extent, block->bits(), gdalDataTypeFromQgisDataType( block->dataType() ) );
191 if ( ret )
192 {
193 GDALRasterBandH band = GDALGetRasterBand( ret.get(), 1 );
194 if ( band )
195 GDALSetRasterNoDataValue( band, block->noDataValue() );
196 }
197
198 return ret;
199}
200
201
202
204 const QgsPointXY &origin,
205 double gridXSize,
206 double gridYSize,
207 QgsRasterBlock *block )
208{
209 if ( !block )
210 return nullptr;
211
212 GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
213 if ( !hDriverMem )
214 return nullptr;
215
216 const double cellSizeX = gridXSize / block->width();
217 const double cellSizeY = gridYSize / block->height();
218 double geoTransform[6];
219 geoTransform[0] = origin.x();
220 geoTransform[1] = cellSizeX * std::cos( rotation );
221 geoTransform[2] = cellSizeY * std::sin( rotation );
222 geoTransform[3] = origin.y();
223 geoTransform[4] = cellSizeX * std::sin( rotation );
224 geoTransform[5] = -cellSizeY * std::cos( rotation );
225
226 GDALDataType dataType = gdalDataTypeFromQgisDataType( block->dataType() );
227 gdal::dataset_unique_ptr hDstDS( GDALCreate( hDriverMem, "", block->width(), block->height(), 0, dataType, nullptr ) );
228
229 int dataTypeSize = GDALGetDataTypeSizeBytes( dataType );
230 char **papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
231 << QStringLiteral( "PIXELOFFSET=%1" ).arg( dataTypeSize )
232 << QStringLiteral( "LINEOFFSET=%1" ).arg( block->width() * dataTypeSize )
233 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( block->bits() ) ) );
234 GDALAddBand( hDstDS.get(), dataType, papszOptions );
235 CSLDestroy( papszOptions );
236
237 GDALSetGeoTransform( hDstDS.get(), geoTransform );
238
239 GDALRasterBandH band = GDALGetRasterBand( hDstDS.get(), 1 );
240 if ( band )
241 GDALSetRasterNoDataValue( band, block->noDataValue() );
242
243 return hDstDS;
244}
245
246static bool resampleSingleBandRasterStatic( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, GDALResampleAlg resampleAlg, char **papszOptions )
247{
248 gdal::warp_options_unique_ptr psWarpOptions( GDALCreateWarpOptions() );
249 psWarpOptions->hSrcDS = hSrcDS;
250 psWarpOptions->hDstDS = hDstDS;
251
252 psWarpOptions->nBandCount = 1;
253 psWarpOptions->panSrcBands = reinterpret_cast< int * >( CPLMalloc( sizeof( int ) * 1 ) );
254 psWarpOptions->panDstBands = reinterpret_cast< int * >( CPLMalloc( sizeof( int ) * 1 ) );
255 psWarpOptions->panSrcBands[0] = 1;
256 psWarpOptions->panDstBands[0] = 1;
257 double noDataValue = GDALGetRasterNoDataValue( GDALGetRasterBand( hDstDS, 1 ), nullptr );
258 psWarpOptions->padfDstNoDataReal = reinterpret_cast< double * >( CPLMalloc( sizeof( double ) * 1 ) );
259 psWarpOptions->padfDstNoDataReal[0] = noDataValue;
260 psWarpOptions->eResampleAlg = resampleAlg;
261
262 // Establish reprojection transformer.
263 psWarpOptions->pTransformerArg = GDALCreateGenImgProjTransformer2( hSrcDS, hDstDS, papszOptions );
264
265 if ( ! psWarpOptions->pTransformerArg )
266 {
267 return false;
268 }
269
270 psWarpOptions->pfnTransformer = GDALGenImgProjTransform;
271 psWarpOptions->papszWarpOptions = CSLSetNameValue( psWarpOptions-> papszWarpOptions, "INIT_DEST", "NO_DATA" );
272
273 // Initialize and execute the warp operation.
274 bool retVal = false;
275 GDALWarpOperation oOperation;
276 CPLErr initResult = oOperation.Initialize( psWarpOptions.get() );
277 if ( initResult != CE_Failure )
278 retVal = oOperation.ChunkAndWarpImage( 0, 0, GDALGetRasterXSize( hDstDS ), GDALGetRasterYSize( hDstDS ) ) == CE_None;
279 GDALDestroyGenImgProjTransformer( psWarpOptions->pTransformerArg );
280 return retVal;
281}
282
283bool QgsGdalUtils::resampleSingleBandRaster( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, GDALResampleAlg resampleAlg, const char *pszCoordinateOperation )
284{
285 char **papszOptions = nullptr;
286 if ( pszCoordinateOperation != nullptr )
287 papszOptions = CSLSetNameValue( papszOptions, "COORDINATE_OPERATION", pszCoordinateOperation );
288
289 bool result = resampleSingleBandRasterStatic( hSrcDS, hDstDS, resampleAlg, papszOptions );
290 CSLDestroy( papszOptions );
291 return result;
292}
293
295 GDALDatasetH hDstDS,
296 GDALResampleAlg resampleAlg,
297 const QgsCoordinateReferenceSystem &sourceCrs,
298 const QgsCoordinateReferenceSystem &destinationCrs )
299{
300 char **papszOptions = nullptr;
301
302 papszOptions = CSLSetNameValue( papszOptions, "SRC_SRS", sourceCrs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toUtf8().constData() );
303 papszOptions = CSLSetNameValue( papszOptions, "DST_SRS", destinationCrs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toUtf8().constData() );
304
305 bool result = resampleSingleBandRasterStatic( hSrcDS, hDstDS, resampleAlg, papszOptions );
306 CSLDestroy( papszOptions );
307 return result;
308}
309
310QImage QgsGdalUtils::resampleImage( const QImage &image, QSize outputSize, GDALRIOResampleAlg resampleAlg )
311{
313 if ( !srcDS )
314 return QImage();
315
316 GDALRasterIOExtraArg extra;
317 INIT_RASTERIO_EXTRA_ARG( extra );
318 extra.eResampleAlg = resampleAlg;
319
320 QImage res( outputSize, image.format() );
321 if ( res.isNull() )
322 return QImage();
323
324 GByte *rgb = reinterpret_cast<GByte *>( res.bits() );
325
326 CPLErr err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 1 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 2, outputSize.width(),
327 outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
328 if ( err != CE_None )
329 {
330 QgsDebugError( QStringLiteral( "failed to read red band" ) );
331 return QImage();
332 }
333
334 err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 2 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 1, outputSize.width(),
335 outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
336 if ( err != CE_None )
337 {
338 QgsDebugError( QStringLiteral( "failed to read green band" ) );
339 return QImage();
340 }
341
342 err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 3 ), GF_Read, 0, 0, image.width(), image.height(), rgb, outputSize.width(),
343 outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
344 if ( err != CE_None )
345 {
346 QgsDebugError( QStringLiteral( "failed to read blue band" ) );
347 return QImage();
348 }
349
350 err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 4 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 3, outputSize.width(),
351 outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
352 if ( err != CE_None )
353 {
354 QgsDebugError( QStringLiteral( "failed to read alpha band" ) );
355 return QImage();
356 }
357
358 return res;
359}
360
361QString QgsGdalUtils::helpCreationOptionsFormat( const QString &format )
362{
363 QString message;
364 GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
365 if ( myGdalDriver )
366 {
367 // first report details and help page
368 char **GDALmetadata = GDALGetMetadata( myGdalDriver, nullptr );
369 message += QLatin1String( "Format Details:\n" );
370 message += QStringLiteral( " Extension: %1\n" ).arg( CSLFetchNameValue( GDALmetadata, GDAL_DMD_EXTENSION ) );
371 message += QStringLiteral( " Short Name: %1" ).arg( GDALGetDriverShortName( myGdalDriver ) );
372 message += QStringLiteral( " / Long Name: %1\n" ).arg( GDALGetDriverLongName( myGdalDriver ) );
373 message += QStringLiteral( " Help page: http://www.gdal.org/%1\n\n" ).arg( CSLFetchNameValue( GDALmetadata, GDAL_DMD_HELPTOPIC ) );
374
375 // next get creation options
376 // need to serialize xml to get newlines, should we make the basic xml prettier?
377 CPLXMLNode *psCOL = CPLParseXMLString( GDALGetMetadataItem( myGdalDriver,
378 GDAL_DMD_CREATIONOPTIONLIST, "" ) );
379 char *pszFormattedXML = CPLSerializeXMLTree( psCOL );
380 if ( pszFormattedXML )
381 message += QString( pszFormattedXML );
382 if ( psCOL )
383 CPLDestroyXMLNode( psCOL );
384 if ( pszFormattedXML )
385 CPLFree( pszFormattedXML );
386 }
387 return message;
388}
389
390char **QgsGdalUtils::papszFromStringList( const QStringList &list )
391{
392 char **papszRetList = nullptr;
393 const auto constList = list;
394 for ( const QString &elem : constList )
395 {
396 papszRetList = CSLAddString( papszRetList, elem.toLocal8Bit().constData() );
397 }
398 return papszRetList;
399}
400
401QString QgsGdalUtils::validateCreationOptionsFormat( const QStringList &createOptions, const QString &format )
402{
403 GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
404 if ( ! myGdalDriver )
405 return QStringLiteral( "invalid GDAL driver" );
406
407 char **papszOptions = papszFromStringList( createOptions );
408 // get error string?
409 const int ok = GDALValidateCreationOptions( myGdalDriver, papszOptions );
410 CSLDestroy( papszOptions );
411
412 if ( !ok )
413 return QStringLiteral( "Failed GDALValidateCreationOptions() test" );
414 return QString();
415}
416
418 GDALDatasetH hSrcDS,
419 const char *pszSrcWKT,
420 const char *pszDstWKT,
421 GDALResampleAlg eResampleAlg,
422 double dfMaxError,
423 const GDALWarpOptions *psOptionsIn )
424{
425 char **opts = nullptr;
426 if ( GDALGetMetadata( hSrcDS, "RPC" ) )
427 {
428 // well-behaved RPC should have height offset a good value for RPC_HEIGHT
429 const char *heightOffStr = GDALGetMetadataItem( hSrcDS, "HEIGHT_OFF", "RPC" );
430 if ( heightOffStr )
431 opts = CSLAddNameValue( opts, "RPC_HEIGHT", heightOffStr );
432 }
433
434 return GDALAutoCreateWarpedVRTEx( hSrcDS, pszSrcWKT, pszDstWKT, eResampleAlg, dfMaxError, psOptionsIn, opts );
435}
436
437void *QgsGdalUtils::rpcAwareCreateTransformer( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, char **papszOptions )
438{
439 char **opts = CSLDuplicate( papszOptions );
440 if ( GDALGetMetadata( hSrcDS, "RPC" ) )
441 {
442 // well-behaved RPC should have height offset a good value for RPC_HEIGHT
443 const char *heightOffStr = GDALGetMetadataItem( hSrcDS, "HEIGHT_OFF", "RPC" );
444 if ( heightOffStr )
445 opts = CSLAddNameValue( opts, "RPC_HEIGHT", heightOffStr );
446 }
447 void *transformer = GDALCreateGenImgProjTransformer2( hSrcDS, hDstDS, opts );
448 CSLDestroy( opts );
449 return transformer;
450}
451
453{
454 switch ( dataType )
455 {
457 return GDALDataType::GDT_Unknown;
458 break;
460 return GDALDataType::GDT_Byte;
461 break;
463#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
464 return GDALDataType::GDT_Int8;
465#else
466 return GDALDataType::GDT_Unknown;
467#endif
468 break;
470 return GDALDataType::GDT_UInt16;
471 break;
473 return GDALDataType::GDT_Int16;
474 break;
476 return GDALDataType::GDT_UInt32;
477 break;
479 return GDALDataType::GDT_Int32;
480 break;
482 return GDALDataType::GDT_Float32;
483 break;
485 return GDALDataType::GDT_Float64;
486 break;
488 return GDALDataType::GDT_CInt16;
489 break;
491 return GDALDataType::GDT_CInt32;
492 break;
494 return GDALDataType::GDT_CFloat32;
495 break;
497 return GDALDataType::GDT_CFloat64;
498 break;
501 return GDALDataType::GDT_Unknown;
502 break;
503 };
504
505 return GDALDataType::GDT_Unknown;
506}
507
509{
510 GDALResampleAlg eResampleAlg = GRA_NearestNeighbour;
511 switch ( method )
512 {
514 case QgsRasterDataProvider::ResamplingMethod::Gauss: // Gauss not available in GDALResampleAlg
515 eResampleAlg = GRA_NearestNeighbour;
516 break;
517
519 eResampleAlg = GRA_Bilinear;
520 break;
521
523 eResampleAlg = GRA_Cubic;
524 break;
525
527 eResampleAlg = GRA_CubicSpline;
528 break;
529
531 eResampleAlg = GRA_Lanczos;
532 break;
533
535 eResampleAlg = GRA_Average;
536 break;
537
539 eResampleAlg = GRA_Mode;
540 break;
541 }
542
543 return eResampleAlg;
544}
545
546#ifndef QT_NO_NETWORKPROXY
548{
549 // Check proxy configuration, they are application level but
550 // instead of adding an API and complex signal/slot connections
551 // given the limited cost of checking them on every provider instantiation
552 // we can do it here so that new settings are applied whenever a new layer
553 // is created.
554 const QgsSettings settings;
555 // Check that proxy is enabled
556 if ( settings.value( QStringLiteral( "proxy/proxyEnabled" ), false ).toBool() )
557 {
558 // Get the first configured proxy
559 QList<QNetworkProxy> proxies( QgsNetworkAccessManager::instance()->proxyFactory()->queryProxy( ) );
560 if ( ! proxies.isEmpty() )
561 {
562 const QNetworkProxy proxy( proxies.first() );
563 // TODO/FIXME: check excludes (the GDAL config options are global, we need a per-connection config option)
564 //QStringList excludes;
565 //excludes = settings.value( QStringLiteral( "proxy/proxyExcludedUrls" ), "" ).toStringList();
566
567 const QString proxyHost( proxy.hostName() );
568 const quint16 proxyPort( proxy.port() );
569
570 const QString proxyUser( proxy.user() );
571 const QString proxyPassword( proxy.password() );
572
573 if ( ! proxyHost.isEmpty() )
574 {
575 QString connection( proxyHost );
576 if ( proxyPort )
577 {
578 connection += ':' + QString::number( proxyPort );
579 }
580 CPLSetConfigOption( "GDAL_HTTP_PROXY", connection.toUtf8() );
581 if ( ! proxyUser.isEmpty( ) )
582 {
583 QString credentials( proxyUser );
584 if ( ! proxyPassword.isEmpty( ) )
585 {
586 credentials += ':' + proxyPassword;
587 }
588 CPLSetConfigOption( "GDAL_HTTP_PROXYUSERPWD", credentials.toUtf8() );
589 }
590 }
591 }
592 }
593}
594
595bool QgsGdalUtils::pathIsCheapToOpen( const QString &path, int smallFileSizeLimit )
596{
597 const QFileInfo info( path );
598 const long long size = info.size();
599
600 // if size could not be determined, safest to flag path as expensive
601 if ( size == 0 )
602 return false;
603
604 const QString suffix = info.suffix().toLower();
605 static const QStringList sFileSizeDependentExtensions
606 {
607 QStringLiteral( "xlsx" ),
608 QStringLiteral( "ods" ),
609 QStringLiteral( "csv" )
610 };
611 if ( sFileSizeDependentExtensions.contains( suffix ) )
612 {
613 // path corresponds to a file type which is only cheap to open for small files
614 return size < smallFileSizeLimit;
615 }
616
617 // treat all other formats as expensive.
618 // TODO -- flag formats which only require a quick header parse as cheap
619 return false;
620}
621
623{
624#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,4,0)
625 // get supported extensions
626 static std::once_flag initialized;
627 static QStringList SUPPORTED_DB_LAYERS_EXTENSIONS;
628 std::call_once( initialized, [ = ]
629 {
630 // iterate through all of the supported drivers, adding the corresponding file extensions for
631 // types which advertise multilayer support
632 GDALDriverH driver = nullptr;
633
634 QSet< QString > extensions;
635
636 for ( int i = 0; i < GDALGetDriverCount(); ++i )
637 {
638 driver = GDALGetDriver( i );
639 if ( !driver )
640 {
641 QgsLogger::warning( "unable to get driver " + QString::number( i ) );
642 continue;
643 }
644
645 bool isMultiLayer = false;
646 if ( QString( GDALGetMetadataItem( driver, GDAL_DCAP_RASTER, nullptr ) ) == QLatin1String( "YES" ) )
647 {
648 if ( GDALGetMetadataItem( driver, GDAL_DMD_SUBDATASETS, nullptr ) != nullptr )
649 {
650 isMultiLayer = true;
651 }
652 }
653 if ( !isMultiLayer && QString( GDALGetMetadataItem( driver, GDAL_DCAP_VECTOR, nullptr ) ) == QLatin1String( "YES" ) )
654 {
655 if ( GDALGetMetadataItem( driver, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, nullptr ) != nullptr )
656 {
657 isMultiLayer = true;
658 }
659 }
660
661 if ( !isMultiLayer )
662 continue;
663
664 const QString driverExtensions = GDALGetMetadataItem( driver, GDAL_DMD_EXTENSIONS, "" );
665 if ( driverExtensions.isEmpty() )
666 continue;
667
668 const QStringList splitExtensions = driverExtensions.split( ' ', Qt::SkipEmptyParts );
669
670 for ( const QString &ext : splitExtensions )
671 {
672 // maintain older behavior -- don't always expose tiff files as containers
673 if ( ext == QLatin1String( "tif" ) || ext == QLatin1String( "tiff" ) )
674 continue;
675
676 extensions.insert( ext );
677 }
678 }
679
680 SUPPORTED_DB_LAYERS_EXTENSIONS = QStringList( extensions.constBegin(), extensions.constEnd() );
681 } );
682 return SUPPORTED_DB_LAYERS_EXTENSIONS;
683
684#else
685 static const QStringList SUPPORTED_DB_LAYERS_EXTENSIONS
686 {
687 QStringLiteral( "gpkg" ),
688 QStringLiteral( "sqlite" ),
689 QStringLiteral( "db" ),
690 QStringLiteral( "gdb" ),
691 QStringLiteral( "kml" ),
692 QStringLiteral( "kmz" ),
693 QStringLiteral( "osm" ),
694 QStringLiteral( "mdb" ),
695 QStringLiteral( "accdb" ),
696 QStringLiteral( "xls" ),
697 QStringLiteral( "xlsx" ),
698 QStringLiteral( "ods" ),
699 QStringLiteral( "gpx" ),
700 QStringLiteral( "pdf" ),
701 QStringLiteral( "pbf" ),
702 QStringLiteral( "vrt" ),
703 QStringLiteral( "nc" ),
704 QStringLiteral( "dxf" ),
705 QStringLiteral( "shp.zip" ) };
706 return SUPPORTED_DB_LAYERS_EXTENSIONS;
707#endif
708}
709
710QString QgsGdalUtils::vsiPrefixForPath( const QString &path )
711{
712 const QStringList vsiPrefixes = QgsGdalUtils::vsiArchivePrefixes();
713
714 for ( const QString &vsiPrefix : vsiPrefixes )
715 {
716 if ( path.startsWith( vsiPrefix, Qt::CaseInsensitive ) )
717 return vsiPrefix;
718 }
719
720 if ( path.endsWith( QLatin1String( ".shp.zip" ), Qt::CaseInsensitive ) )
721 {
722 // GDAL 3.1 Shapefile driver directly handles .shp.zip files
723 if ( GDALIdentifyDriverEx( path.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr ) )
724 return QString();
725 return QStringLiteral( "/vsizip/" );
726 }
727 else if ( path.endsWith( QLatin1String( ".zip" ), Qt::CaseInsensitive ) )
728 return QStringLiteral( "/vsizip/" );
729 else if ( path.endsWith( QLatin1String( ".tar" ), Qt::CaseInsensitive ) ||
730 path.endsWith( QLatin1String( ".tar.gz" ), Qt::CaseInsensitive ) ||
731 path.endsWith( QLatin1String( ".tgz" ), Qt::CaseInsensitive ) )
732 return QStringLiteral( "/vsitar/" );
733 else if ( path.endsWith( QLatin1String( ".gz" ), Qt::CaseInsensitive ) )
734 return QStringLiteral( "/vsigzip/" );
735#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
736 else if ( vsiPrefixes.contains( QStringLiteral( "/vsi7z/" ) ) &&
737 ( path.endsWith( QLatin1String( ".7z" ), Qt::CaseInsensitive ) ||
738 path.endsWith( QLatin1String( ".lpk" ), Qt::CaseInsensitive ) ||
739 path.endsWith( QLatin1String( ".lpkx" ), Qt::CaseInsensitive ) ||
740 path.endsWith( QLatin1String( ".mpk" ), Qt::CaseInsensitive ) ||
741 path.endsWith( QLatin1String( ".mpkx" ), Qt::CaseInsensitive ) ) )
742 return QStringLiteral( "/vsi7z/" );
743 else if ( vsiPrefixes.contains( QStringLiteral( "/vsirar/" ) ) &&
744 path.endsWith( QLatin1String( ".rar" ), Qt::CaseInsensitive ) )
745 return QStringLiteral( "/vsirar/" );
746#endif
747
748 return QString();
749}
750
752{
753 QStringList res { QStringLiteral( "/vsizip/" ),
754 QStringLiteral( "/vsitar/" ),
755 QStringLiteral( "/vsigzip/" ),
756 };
757#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
758 res.append( QStringLiteral( "/vsi7z/" ) );
759 res.append( QStringLiteral( "/vsirar/" ) );
760#endif
761 return res;
762}
763
764bool QgsGdalUtils::isVsiArchivePrefix( const QString &prefix )
765{
766 return vsiArchivePrefixes().contains( prefix );
767}
768
770{
771 QStringList res { QStringLiteral( ".zip" ),
772 QStringLiteral( ".tar" ),
773 QStringLiteral( ".tar.gz" ),
774 QStringLiteral( ".tgz" ),
775 QStringLiteral( ".gz" ),
776 };
777#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
778 res.append( { QStringLiteral( ".7z" ),
779 QStringLiteral( ".lpk" ),
780 QStringLiteral( ".lpkx" ),
781 QStringLiteral( ".mpk" ),
782 QStringLiteral( ".mpkx" ),
783 QStringLiteral( ".rar" )
784 } );
785#endif
786 return res;
787}
788
789bool QgsGdalUtils::isVsiArchiveFileExtension( const QString &extension )
790{
791 const QString extWithDot = extension.startsWith( '.' ) ? extension : ( '.' + extension );
792 return vsiArchiveFileExtensions().contains( extWithDot.toLower() );
793}
794
795bool QgsGdalUtils::vrtMatchesLayerType( const QString &vrtPath, Qgis::LayerType type )
796{
797 CPLPushErrorHandler( CPLQuietErrorHandler );
798 CPLErrorReset();
799 GDALDriverH hDriver = nullptr;
800
801 switch ( type )
802 {
804 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr );
805 break;
806
808 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_RASTER, nullptr, nullptr );
809 break;
810
818 break;
819 }
820
821 CPLPopErrorHandler();
822 return static_cast< bool >( hDriver );
823}
824#endif
DataType
Raster data types.
Definition: qgis.h:269
@ CInt32
Complex Int32.
@ Float32
Thirty two bit floating point (float)
@ CFloat64
Complex Float64.
@ Int16
Sixteen bit signed integer (qint16)
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30)
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ UnknownDataType
Unknown or unspecified type.
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
@ Int32
Thirty two bit signed integer (qint32)
@ Float64
Sixty four bit floating point (double)
@ CFloat32
Complex Float32.
@ CInt16
Complex Int16.
@ UInt32
Thirty two bit unsigned integer (quint32)
LayerType
Types of layers that can be added to a map.
Definition: qgis.h:114
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
This class represents a coordinate reference system (CRS).
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
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 GDALResampleAlg gdalResamplingAlgorithm(QgsRasterDataProvider::ResamplingMethod method)
Returns the GDAL resampling method corresponding to the QGIS resampling method.
static bool resampleSingleBandRaster(GDALDatasetH hSrcDS, GDALDatasetH hDstDS, GDALResampleAlg resampleAlg, const char *pszCoordinateOperation)
Resamples a single band raster to the destination dataset with different resolution (and possibly wit...
static GDALDatasetH rpcAwareAutoCreateWarpedVrt(GDALDatasetH hSrcDS, const char *pszSrcWKT, const char *pszDstWKT, GDALResampleAlg eResampleAlg, double dfMaxError, const GDALWarpOptions *psOptionsIn)
This is a copy of GDALAutoCreateWarpedVRT optimized for imagery using RPC georeferencing that also se...
static bool supportsRasterCreate(GDALDriverH driver)
Reads whether a driver supports GDALCreate() for raster purposes.
static bool isVsiArchiveFileExtension(const QString &extension)
Returns true if a file extension is a supported archive style container (e.g.
static 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 gdal::dataset_unique_ptr createSingleBandMemoryDataset(GDALDataType dataType, const QgsRectangle &extent, int width, int height, const QgsCoordinateReferenceSystem &crs)
Creates a new single band memory dataset with given parameters.
static QString validateCreationOptionsFormat(const QStringList &createOptions, const QString &format)
Validates creation options for a given format, regardless of layer.
static gdal::dataset_unique_ptr blockToSingleBandMemoryDataset(int pixelWidth, int pixelHeight, const QgsRectangle &extent, void *block, GDALDataType dataType)
Converts a data block to a single band GDAL memory dataset.
static 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.
Definition: qgslogger.cpp:131
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
A class to represent a 2D point.
Definition: qgspointxy.h:60
double y
Definition: qgspointxy.h:64
Q_GADGET double x
Definition: qgspointxy.h:63
Raster data container.
int height() const
Returns the height (number of rows) of the raster block.
char * bits(int row, int column)
Returns a pointer to block data.
double noDataValue() const
Returns no data value.
Qgis::DataType dataType() const
Returns data type.
int width() const
Returns the width (number of columns) of the raster block.
ResamplingMethod
Resampling method for provider-level resampling.
@ Lanczos
Lanczos windowed sinc interpolation (6x6 kernel)
@ Nearest
Nearest-neighbour resampling.
@ Mode
Mode (selects the value which appears most often of all the sampled points)
@ Bilinear
Bilinear (2x2 kernel) resampling.
@ CubicSpline
Cubic B-Spline Approximation (4x4 kernel)
@ Cubic
Cubic Convolution Approximation (4x4 kernel) resampling.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:201
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:211
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:236
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:243
This class is a composition of two QSettings instances:
Definition: qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
Definition: qgsogrutils.h:157
std::unique_ptr< GDALWarpOptions, GDALWarpOptionsDeleter > warp_options_unique_ptr
Scoped GDAL warp options.
Definition: qgsogrutils.h:172
void * GDALDatasetH
#define QgsDebugError(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs