QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
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#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 char **driverMetadata = GDALGetMetadata( driver, nullptr );
43 return CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) &&
44 CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, false );
45}
46
48{
49 return createMultiBandMemoryDataset( dataType, 1, extent, width, height, crs );
50}
51
52gdal::dataset_unique_ptr QgsGdalUtils::createMultiBandMemoryDataset( GDALDataType dataType, int bands, const QgsRectangle &extent, int width, int height, const QgsCoordinateReferenceSystem &crs )
53{
54 GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
55 if ( !hDriverMem )
56 {
58 }
59
60 gdal::dataset_unique_ptr hSrcDS( GDALCreate( hDriverMem, "", width, height, bands, dataType, nullptr ) );
61
62 const double cellSizeX = extent.width() / width;
63 const double cellSizeY = extent.height() / height;
64 double geoTransform[6];
65 geoTransform[0] = extent.xMinimum();
66 geoTransform[1] = cellSizeX;
67 geoTransform[2] = 0;
68 geoTransform[3] = extent.yMinimum() + ( cellSizeY * height );
69 geoTransform[4] = 0;
70 geoTransform[5] = -cellSizeY;
71
72 GDALSetProjection( hSrcDS.get(), crs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).toLatin1().constData() );
73 GDALSetGeoTransform( hSrcDS.get(), geoTransform );
74 return hSrcDS;
75}
76
77gdal::dataset_unique_ptr QgsGdalUtils::createSingleBandTiffDataset( const QString &filename, GDALDataType dataType, const QgsRectangle &extent, int width, int height, const QgsCoordinateReferenceSystem &crs )
78{
79 const double cellSizeX = extent.width() / width;
80 const double cellSizeY = extent.height() / height;
81 double geoTransform[6];
82 geoTransform[0] = extent.xMinimum();
83 geoTransform[1] = cellSizeX;
84 geoTransform[2] = 0;
85 geoTransform[3] = extent.yMinimum() + ( cellSizeY * height );
86 geoTransform[4] = 0;
87 geoTransform[5] = -cellSizeY;
88
89 GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
90 if ( !hDriver )
91 {
93 }
94
95 // Create the output file.
96 gdal::dataset_unique_ptr hDstDS( GDALCreate( hDriver, filename.toUtf8().constData(), width, height, 1, dataType, nullptr ) );
97 if ( !hDstDS )
98 {
100 }
101
102 // Write out the projection definition.
103 GDALSetProjection( hDstDS.get(), crs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).toLatin1().constData() );
104 GDALSetGeoTransform( hDstDS.get(), geoTransform );
105 return hDstDS;
106}
107
109{
110 if ( image.isNull() )
111 return nullptr;
112
113 const QRgb *rgb = reinterpret_cast<const QRgb *>( image.constBits() );
114 GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
115 if ( !hDriverMem )
116 {
117 return nullptr;
118 }
119 gdal::dataset_unique_ptr hSrcDS( GDALCreate( hDriverMem, "", image.width(), image.height(), 0, GDT_Byte, nullptr ) );
120
121 char **papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
122 << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
123 << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
124 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) + 2 ) );
125 GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
126 CSLDestroy( papszOptions );
127
128 papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
129 << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
130 << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
131 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) + 1 ) );
132 GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
133 CSLDestroy( papszOptions );
134
135 papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
136 << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
137 << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
138 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) ) );
139 GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
140 CSLDestroy( papszOptions );
141
142 papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
143 << QStringLiteral( "PIXELOFFSET=%1" ).arg( sizeof( QRgb ) )
144 << QStringLiteral( "LINEOFFSET=%1" ).arg( image.bytesPerLine() )
145 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( rgb ) + 3 ) );
146 GDALAddBand( hSrcDS.get(), GDT_Byte, papszOptions );
147 CSLDestroy( papszOptions );
148
149 return hSrcDS;
150}
151
152gdal::dataset_unique_ptr QgsGdalUtils::blockToSingleBandMemoryDataset( int pixelWidth, int pixelHeight, const QgsRectangle &extent, void *block, GDALDataType dataType )
153{
154 if ( !block )
155 return nullptr;
156
157 GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
158 if ( !hDriverMem )
159 return nullptr;
160
161 const double cellSizeX = extent.width() / pixelWidth;
162 const double cellSizeY = extent.height() / pixelHeight;
163 double geoTransform[6];
164 geoTransform[0] = extent.xMinimum();
165 geoTransform[1] = cellSizeX;
166 geoTransform[2] = 0;
167 geoTransform[3] = extent.yMinimum() + ( cellSizeY * pixelHeight );
168 geoTransform[4] = 0;
169 geoTransform[5] = -cellSizeY;
170
171 gdal::dataset_unique_ptr hDstDS( GDALCreate( hDriverMem, "", pixelWidth, pixelHeight, 0, dataType, nullptr ) );
172
173 int dataTypeSize = GDALGetDataTypeSizeBytes( dataType );
174 char **papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
175 << QStringLiteral( "PIXELOFFSET=%1" ).arg( dataTypeSize )
176 << QStringLiteral( "LINEOFFSET=%1" ).arg( pixelWidth * dataTypeSize )
177 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( block ) ) );
178 GDALAddBand( hDstDS.get(), dataType, papszOptions );
179 CSLDestroy( papszOptions );
180
181 GDALSetGeoTransform( hDstDS.get(), geoTransform );
182
183 return hDstDS;
184}
185
187{
188 if ( !block )
189 return nullptr;
190
191 gdal::dataset_unique_ptr ret = blockToSingleBandMemoryDataset( block->width(), block->height(), extent, block->bits(), gdalDataTypeFromQgisDataType( block->dataType() ) );
192 if ( ret )
193 {
194 GDALRasterBandH band = GDALGetRasterBand( ret.get(), 1 );
195 if ( band )
196 GDALSetRasterNoDataValue( band, block->noDataValue() );
197 }
198
199 return ret;
200}
201
202
203
205 const QgsPointXY &origin,
206 double gridXSize,
207 double gridYSize,
208 QgsRasterBlock *block )
209{
210 if ( !block )
211 return nullptr;
212
213 GDALDriverH hDriverMem = GDALGetDriverByName( "MEM" );
214 if ( !hDriverMem )
215 return nullptr;
216
217 const double cellSizeX = gridXSize / block->width();
218 const double cellSizeY = gridYSize / block->height();
219 double geoTransform[6];
220 geoTransform[0] = origin.x();
221 geoTransform[1] = cellSizeX * std::cos( rotation );
222 geoTransform[2] = cellSizeY * std::sin( rotation );
223 geoTransform[3] = origin.y();
224 geoTransform[4] = cellSizeX * std::sin( rotation );
225 geoTransform[5] = -cellSizeY * std::cos( rotation );
226
227 GDALDataType dataType = gdalDataTypeFromQgisDataType( block->dataType() );
228 gdal::dataset_unique_ptr hDstDS( GDALCreate( hDriverMem, "", block->width(), block->height(), 0, dataType, nullptr ) );
229
230 int dataTypeSize = GDALGetDataTypeSizeBytes( dataType );
231 char **papszOptions = QgsGdalUtils::papszFromStringList( QStringList()
232 << QStringLiteral( "PIXELOFFSET=%1" ).arg( dataTypeSize )
233 << QStringLiteral( "LINEOFFSET=%1" ).arg( block->width() * dataTypeSize )
234 << QStringLiteral( "DATAPOINTER=%1" ).arg( reinterpret_cast< qulonglong >( block->bits() ) ) );
235 GDALAddBand( hDstDS.get(), dataType, papszOptions );
236 CSLDestroy( papszOptions );
237
238 GDALSetGeoTransform( hDstDS.get(), geoTransform );
239
240 GDALRasterBandH band = GDALGetRasterBand( hDstDS.get(), 1 );
241 if ( band )
242 GDALSetRasterNoDataValue( band, block->noDataValue() );
243
244 return hDstDS;
245}
246
247static bool resampleSingleBandRasterStatic( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, GDALResampleAlg resampleAlg, char **papszOptions )
248{
249 gdal::warp_options_unique_ptr psWarpOptions( GDALCreateWarpOptions() );
250 psWarpOptions->hSrcDS = hSrcDS;
251 psWarpOptions->hDstDS = hDstDS;
252
253 psWarpOptions->nBandCount = 1;
254 psWarpOptions->panSrcBands = reinterpret_cast< int * >( CPLMalloc( sizeof( int ) * 1 ) );
255 psWarpOptions->panDstBands = reinterpret_cast< int * >( CPLMalloc( sizeof( int ) * 1 ) );
256 psWarpOptions->panSrcBands[0] = 1;
257 psWarpOptions->panDstBands[0] = 1;
258 double noDataValue = GDALGetRasterNoDataValue( GDALGetRasterBand( hDstDS, 1 ), nullptr );
259 psWarpOptions->padfDstNoDataReal = reinterpret_cast< double * >( CPLMalloc( sizeof( double ) * 1 ) );
260 psWarpOptions->padfDstNoDataReal[0] = noDataValue;
261 psWarpOptions->eResampleAlg = resampleAlg;
262
263 // Establish reprojection transformer.
264 psWarpOptions->pTransformerArg = GDALCreateGenImgProjTransformer2( hSrcDS, hDstDS, papszOptions );
265
266 if ( ! psWarpOptions->pTransformerArg )
267 {
268 return false;
269 }
270
271 psWarpOptions->pfnTransformer = GDALGenImgProjTransform;
272 psWarpOptions->papszWarpOptions = CSLSetNameValue( psWarpOptions-> papszWarpOptions, "INIT_DEST", "NO_DATA" );
273
274 // Initialize and execute the warp operation.
275 bool retVal = false;
276 GDALWarpOperation oOperation;
277 CPLErr initResult = oOperation.Initialize( psWarpOptions.get() );
278 if ( initResult != CE_Failure )
279 retVal = oOperation.ChunkAndWarpImage( 0, 0, GDALGetRasterXSize( hDstDS ), GDALGetRasterYSize( hDstDS ) ) == CE_None;
280 GDALDestroyGenImgProjTransformer( psWarpOptions->pTransformerArg );
281 return retVal;
282}
283
284bool QgsGdalUtils::resampleSingleBandRaster( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, GDALResampleAlg resampleAlg, const char *pszCoordinateOperation )
285{
286 char **papszOptions = nullptr;
287 if ( pszCoordinateOperation != nullptr )
288 papszOptions = CSLSetNameValue( papszOptions, "COORDINATE_OPERATION", pszCoordinateOperation );
289
290 bool result = resampleSingleBandRasterStatic( hSrcDS, hDstDS, resampleAlg, papszOptions );
291 CSLDestroy( papszOptions );
292 return result;
293}
294
296 GDALDatasetH hDstDS,
297 GDALResampleAlg resampleAlg,
298 const QgsCoordinateReferenceSystem &sourceCrs,
299 const QgsCoordinateReferenceSystem &destinationCrs )
300{
301 char **papszOptions = nullptr;
302
303 papszOptions = CSLSetNameValue( papszOptions, "SRC_SRS", sourceCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).toUtf8().constData() );
304 papszOptions = CSLSetNameValue( papszOptions, "DST_SRS", destinationCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).toUtf8().constData() );
305
306 bool result = resampleSingleBandRasterStatic( hSrcDS, hDstDS, resampleAlg, papszOptions );
307 CSLDestroy( papszOptions );
308 return result;
309}
310
311QImage QgsGdalUtils::resampleImage( const QImage &image, QSize outputSize, GDALRIOResampleAlg resampleAlg )
312{
314 if ( !srcDS )
315 return QImage();
316
317 GDALRasterIOExtraArg extra;
318 INIT_RASTERIO_EXTRA_ARG( extra );
319 extra.eResampleAlg = resampleAlg;
320
321 QImage res( outputSize, image.format() );
322 if ( res.isNull() )
323 return QImage();
324
325 GByte *rgb = reinterpret_cast<GByte *>( res.bits() );
326
327 CPLErr err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 1 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 2, outputSize.width(),
328 outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
329 if ( err != CE_None )
330 {
331 QgsDebugError( QStringLiteral( "failed to read red band" ) );
332 return QImage();
333 }
334
335 err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 2 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 1, outputSize.width(),
336 outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
337 if ( err != CE_None )
338 {
339 QgsDebugError( QStringLiteral( "failed to read green band" ) );
340 return QImage();
341 }
342
343 err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 3 ), GF_Read, 0, 0, image.width(), image.height(), rgb, outputSize.width(),
344 outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
345 if ( err != CE_None )
346 {
347 QgsDebugError( QStringLiteral( "failed to read blue band" ) );
348 return QImage();
349 }
350
351 err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 4 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 3, outputSize.width(),
352 outputSize.height(), GDT_Byte, sizeof( QRgb ), res.bytesPerLine(), &extra );
353 if ( err != CE_None )
354 {
355 QgsDebugError( QStringLiteral( "failed to read alpha band" ) );
356 return QImage();
357 }
358
359 return res;
360}
361
362QString QgsGdalUtils::helpCreationOptionsFormat( const QString &format )
363{
364 QString message;
365 GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
366 if ( myGdalDriver )
367 {
368 // first report details and help page
369 char **GDALmetadata = GDALGetMetadata( myGdalDriver, nullptr );
370 message += QLatin1String( "Format Details:\n" );
371 message += QStringLiteral( " Extension: %1\n" ).arg( CSLFetchNameValue( GDALmetadata, GDAL_DMD_EXTENSION ) );
372 message += QStringLiteral( " Short Name: %1" ).arg( GDALGetDriverShortName( myGdalDriver ) );
373 message += QStringLiteral( " / Long Name: %1\n" ).arg( GDALGetDriverLongName( myGdalDriver ) );
374 message += QStringLiteral( " Help page: http://www.gdal.org/%1\n\n" ).arg( CSLFetchNameValue( GDALmetadata, GDAL_DMD_HELPTOPIC ) );
375
376 // next get creation options
377 // need to serialize xml to get newlines, should we make the basic xml prettier?
378 CPLXMLNode *psCOL = CPLParseXMLString( GDALGetMetadataItem( myGdalDriver,
379 GDAL_DMD_CREATIONOPTIONLIST, "" ) );
380 char *pszFormattedXML = CPLSerializeXMLTree( psCOL );
381 if ( pszFormattedXML )
382 message += QString( pszFormattedXML );
383 if ( psCOL )
384 CPLDestroyXMLNode( psCOL );
385 if ( pszFormattedXML )
386 CPLFree( pszFormattedXML );
387 }
388 return message;
389}
390
391char **QgsGdalUtils::papszFromStringList( const QStringList &list )
392{
393 char **papszRetList = nullptr;
394 const auto constList = list;
395 for ( const QString &elem : constList )
396 {
397 papszRetList = CSLAddString( papszRetList, elem.toLocal8Bit().constData() );
398 }
399 return papszRetList;
400}
401
402QString QgsGdalUtils::validateCreationOptionsFormat( const QStringList &createOptions, const QString &format )
403{
404 GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
405 if ( ! myGdalDriver )
406 return QStringLiteral( "invalid GDAL driver" );
407
408 char **papszOptions = papszFromStringList( createOptions );
409 // get error string?
410 const int ok = GDALValidateCreationOptions( myGdalDriver, papszOptions );
411 CSLDestroy( papszOptions );
412
413 if ( !ok )
414 return QStringLiteral( "Failed GDALValidateCreationOptions() test" );
415 return QString();
416}
417
419 GDALDatasetH hSrcDS,
420 const char *pszSrcWKT,
421 const char *pszDstWKT,
422 GDALResampleAlg eResampleAlg,
423 double dfMaxError,
424 const GDALWarpOptions *psOptionsIn )
425{
426 char **opts = nullptr;
427 if ( GDALGetMetadata( hSrcDS, "RPC" ) )
428 {
429 // well-behaved RPC should have height offset a good value for RPC_HEIGHT
430 const char *heightOffStr = GDALGetMetadataItem( hSrcDS, "HEIGHT_OFF", "RPC" );
431 if ( heightOffStr )
432 opts = CSLAddNameValue( opts, "RPC_HEIGHT", heightOffStr );
433 }
434
435 return GDALAutoCreateWarpedVRTEx( hSrcDS, pszSrcWKT, pszDstWKT, eResampleAlg, dfMaxError, psOptionsIn, opts );
436}
437
438void *QgsGdalUtils::rpcAwareCreateTransformer( GDALDatasetH hSrcDS, GDALDatasetH hDstDS, char **papszOptions )
439{
440 char **opts = CSLDuplicate( papszOptions );
441 if ( GDALGetMetadata( hSrcDS, "RPC" ) )
442 {
443 // well-behaved RPC should have height offset a good value for RPC_HEIGHT
444 const char *heightOffStr = GDALGetMetadataItem( hSrcDS, "HEIGHT_OFF", "RPC" );
445 if ( heightOffStr )
446 opts = CSLAddNameValue( opts, "RPC_HEIGHT", heightOffStr );
447 }
448 void *transformer = GDALCreateGenImgProjTransformer2( hSrcDS, hDstDS, opts );
449 CSLDestroy( opts );
450 return transformer;
451}
452
454{
455 switch ( dataType )
456 {
458 return GDALDataType::GDT_Unknown;
459 break;
461 return GDALDataType::GDT_Byte;
462 break;
464#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
465 return GDALDataType::GDT_Int8;
466#else
467 return GDALDataType::GDT_Unknown;
468#endif
469 break;
471 return GDALDataType::GDT_UInt16;
472 break;
474 return GDALDataType::GDT_Int16;
475 break;
477 return GDALDataType::GDT_UInt32;
478 break;
480 return GDALDataType::GDT_Int32;
481 break;
483 return GDALDataType::GDT_Float32;
484 break;
486 return GDALDataType::GDT_Float64;
487 break;
489 return GDALDataType::GDT_CInt16;
490 break;
492 return GDALDataType::GDT_CInt32;
493 break;
495 return GDALDataType::GDT_CFloat32;
496 break;
498 return GDALDataType::GDT_CFloat64;
499 break;
502 return GDALDataType::GDT_Unknown;
503 break;
504 };
505
506 return GDALDataType::GDT_Unknown;
507}
508
510{
511 GDALResampleAlg eResampleAlg = GRA_NearestNeighbour;
512 switch ( method )
513 {
515 case QgsRasterDataProvider::ResamplingMethod::Gauss: // Gauss not available in GDALResampleAlg
516 eResampleAlg = GRA_NearestNeighbour;
517 break;
518
520 eResampleAlg = GRA_Bilinear;
521 break;
522
524 eResampleAlg = GRA_Cubic;
525 break;
526
528 eResampleAlg = GRA_CubicSpline;
529 break;
530
532 eResampleAlg = GRA_Lanczos;
533 break;
534
536 eResampleAlg = GRA_Average;
537 break;
538
540 eResampleAlg = GRA_Mode;
541 break;
542 }
543
544 return eResampleAlg;
545}
546
547#ifndef QT_NO_NETWORKPROXY
549{
550 // Check proxy configuration, they are application level but
551 // instead of adding an API and complex signal/slot connections
552 // given the limited cost of checking them on every provider instantiation
553 // we can do it here so that new settings are applied whenever a new layer
554 // is created.
555 const QgsSettings settings;
556 // Check that proxy is enabled
557 if ( settings.value( QStringLiteral( "proxy/proxyEnabled" ), false ).toBool() )
558 {
559 // Get the first configured proxy
560 QList<QNetworkProxy> proxies( QgsNetworkAccessManager::instance()->proxyFactory()->queryProxy( ) );
561 if ( ! proxies.isEmpty() )
562 {
563 const QNetworkProxy proxy( proxies.first() );
564 // TODO/FIXME: check excludes (the GDAL config options are global, we need a per-connection config option)
565 //QStringList excludes;
566 //excludes = settings.value( QStringLiteral( "proxy/proxyExcludedUrls" ), "" ).toStringList();
567
568 const QString proxyHost( proxy.hostName() );
569 const quint16 proxyPort( proxy.port() );
570
571 const QString proxyUser( proxy.user() );
572 const QString proxyPassword( proxy.password() );
573
574 if ( ! proxyHost.isEmpty() )
575 {
576 QString connection( proxyHost );
577 if ( proxyPort )
578 {
579 connection += ':' + QString::number( proxyPort );
580 }
581 CPLSetConfigOption( "GDAL_HTTP_PROXY", connection.toUtf8() );
582 if ( ! proxyUser.isEmpty( ) )
583 {
584 QString credentials( proxyUser );
585 if ( ! proxyPassword.isEmpty( ) )
586 {
587 credentials += ':' + proxyPassword;
588 }
589 CPLSetConfigOption( "GDAL_HTTP_PROXYUSERPWD", credentials.toUtf8() );
590 }
591 }
592 }
593 }
594}
595
596bool QgsGdalUtils::pathIsCheapToOpen( const QString &path, int smallFileSizeLimit )
597{
598 const QFileInfo info( path );
599 const long long size = info.size();
600
601 // if size could not be determined, safest to flag path as expensive
602 if ( size == 0 )
603 return false;
604
605 const QString suffix = info.suffix().toLower();
606 static const QStringList sFileSizeDependentExtensions
607 {
608 QStringLiteral( "xlsx" ),
609 QStringLiteral( "ods" ),
610 QStringLiteral( "csv" )
611 };
612 if ( sFileSizeDependentExtensions.contains( suffix ) )
613 {
614 // path corresponds to a file type which is only cheap to open for small files
615 return size < smallFileSizeLimit;
616 }
617
618 // treat all other formats as expensive.
619 // TODO -- flag formats which only require a quick header parse as cheap
620 return false;
621}
622
624{
625#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,4,0)
626 // get supported extensions
627 static std::once_flag initialized;
628 static QStringList SUPPORTED_DB_LAYERS_EXTENSIONS;
629 std::call_once( initialized, [ = ]
630 {
631 // iterate through all of the supported drivers, adding the corresponding file extensions for
632 // types which advertise multilayer support
633 GDALDriverH driver = nullptr;
634
635 QSet< QString > extensions;
636
637 for ( int i = 0; i < GDALGetDriverCount(); ++i )
638 {
639 driver = GDALGetDriver( i );
640 if ( !driver )
641 {
642 QgsLogger::warning( "unable to get driver " + QString::number( i ) );
643 continue;
644 }
645
646 bool isMultiLayer = false;
647 if ( QString( GDALGetMetadataItem( driver, GDAL_DCAP_RASTER, nullptr ) ) == QLatin1String( "YES" ) )
648 {
649 if ( GDALGetMetadataItem( driver, GDAL_DMD_SUBDATASETS, nullptr ) != nullptr )
650 {
651 isMultiLayer = true;
652 }
653 }
654 if ( !isMultiLayer && QString( GDALGetMetadataItem( driver, GDAL_DCAP_VECTOR, nullptr ) ) == QLatin1String( "YES" ) )
655 {
656 if ( GDALGetMetadataItem( driver, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, nullptr ) != nullptr )
657 {
658 isMultiLayer = true;
659 }
660 }
661
662 if ( !isMultiLayer )
663 continue;
664
665 const QString driverExtensions = GDALGetMetadataItem( driver, GDAL_DMD_EXTENSIONS, "" );
666 if ( driverExtensions.isEmpty() )
667 continue;
668
669 const QStringList splitExtensions = driverExtensions.split( ' ', Qt::SkipEmptyParts );
670
671 for ( const QString &ext : splitExtensions )
672 {
673 // maintain older behavior -- don't always expose tiff files as containers
674 if ( ext == QLatin1String( "tif" ) || ext == QLatin1String( "tiff" ) )
675 continue;
676
677 extensions.insert( ext );
678 }
679 }
680
681 SUPPORTED_DB_LAYERS_EXTENSIONS = QStringList( extensions.constBegin(), extensions.constEnd() );
682 } );
683 return SUPPORTED_DB_LAYERS_EXTENSIONS;
684
685#else
686 static const QStringList SUPPORTED_DB_LAYERS_EXTENSIONS
687 {
688 QStringLiteral( "gpkg" ),
689 QStringLiteral( "sqlite" ),
690 QStringLiteral( "db" ),
691 QStringLiteral( "gdb" ),
692 QStringLiteral( "kml" ),
693 QStringLiteral( "kmz" ),
694 QStringLiteral( "osm" ),
695 QStringLiteral( "mdb" ),
696 QStringLiteral( "accdb" ),
697 QStringLiteral( "xls" ),
698 QStringLiteral( "xlsx" ),
699 QStringLiteral( "ods" ),
700 QStringLiteral( "gpx" ),
701 QStringLiteral( "pdf" ),
702 QStringLiteral( "pbf" ),
703 QStringLiteral( "vrt" ),
704 QStringLiteral( "nc" ),
705 QStringLiteral( "dxf" ),
706 QStringLiteral( "shp.zip" ) };
707 return SUPPORTED_DB_LAYERS_EXTENSIONS;
708#endif
709}
710
711QString QgsGdalUtils::vsiPrefixForPath( const QString &path )
712{
713 const QStringList vsiPrefixes = QgsGdalUtils::vsiArchivePrefixes();
714
715 for ( const QString &vsiPrefix : vsiPrefixes )
716 {
717 if ( path.startsWith( vsiPrefix, Qt::CaseInsensitive ) )
718 return vsiPrefix;
719 }
720
721 if ( path.endsWith( QLatin1String( ".shp.zip" ), Qt::CaseInsensitive ) )
722 {
723 // GDAL 3.1 Shapefile driver directly handles .shp.zip files
724 if ( GDALIdentifyDriverEx( path.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr ) )
725 return QString();
726 return QStringLiteral( "/vsizip/" );
727 }
728 else if ( path.endsWith( QLatin1String( ".zip" ), Qt::CaseInsensitive ) )
729 return QStringLiteral( "/vsizip/" );
730 else if ( path.endsWith( QLatin1String( ".tar" ), Qt::CaseInsensitive ) ||
731 path.endsWith( QLatin1String( ".tar.gz" ), Qt::CaseInsensitive ) ||
732 path.endsWith( QLatin1String( ".tgz" ), Qt::CaseInsensitive ) )
733 return QStringLiteral( "/vsitar/" );
734 else if ( path.endsWith( QLatin1String( ".gz" ), Qt::CaseInsensitive ) )
735 return QStringLiteral( "/vsigzip/" );
736#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
737 else if ( vsiPrefixes.contains( QStringLiteral( "/vsi7z/" ) ) &&
738 ( path.endsWith( QLatin1String( ".7z" ), Qt::CaseInsensitive ) ||
739 path.endsWith( QLatin1String( ".lpk" ), Qt::CaseInsensitive ) ||
740 path.endsWith( QLatin1String( ".lpkx" ), Qt::CaseInsensitive ) ||
741 path.endsWith( QLatin1String( ".mpk" ), Qt::CaseInsensitive ) ||
742 path.endsWith( QLatin1String( ".mpkx" ), Qt::CaseInsensitive ) ) )
743 return QStringLiteral( "/vsi7z/" );
744 else if ( vsiPrefixes.contains( QStringLiteral( "/vsirar/" ) ) &&
745 path.endsWith( QLatin1String( ".rar" ), Qt::CaseInsensitive ) )
746 return QStringLiteral( "/vsirar/" );
747#endif
748
749 return QString();
750}
751
753{
754 QStringList res { QStringLiteral( "/vsizip/" ),
755 QStringLiteral( "/vsitar/" ),
756 QStringLiteral( "/vsigzip/" ),
757 };
758#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
759 res.append( QStringLiteral( "/vsi7z/" ) );
760 res.append( QStringLiteral( "/vsirar/" ) );
761#endif
762 return res;
763}
764
765bool QgsGdalUtils::isVsiArchivePrefix( const QString &prefix )
766{
767 return vsiArchivePrefixes().contains( prefix );
768}
769
771{
772 QStringList res { QStringLiteral( ".zip" ),
773 QStringLiteral( ".tar" ),
774 QStringLiteral( ".tar.gz" ),
775 QStringLiteral( ".tgz" ),
776 QStringLiteral( ".gz" ),
777 };
778#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,7,0)
779 res.append( { QStringLiteral( ".7z" ),
780 QStringLiteral( ".lpk" ),
781 QStringLiteral( ".lpkx" ),
782 QStringLiteral( ".mpk" ),
783 QStringLiteral( ".mpkx" ),
784 QStringLiteral( ".rar" )
785 } );
786#endif
787 return res;
788}
789
790bool QgsGdalUtils::isVsiArchiveFileExtension( const QString &extension )
791{
792 const QString extWithDot = extension.startsWith( '.' ) ? extension : ( '.' + extension );
793 return vsiArchiveFileExtensions().contains( extWithDot.toLower() );
794}
795
796bool QgsGdalUtils::vrtMatchesLayerType( const QString &vrtPath, Qgis::LayerType type )
797{
798 CPLPushErrorHandler( CPLQuietErrorHandler );
799 CPLErrorReset();
800 GDALDriverH hDriver = nullptr;
801
802 switch ( type )
803 {
805 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr );
806 break;
807
809 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_RASTER, nullptr, nullptr );
810 break;
811
819 break;
820 }
821
822 CPLPopErrorHandler();
823 return static_cast< bool >( hDriver );
824}
825#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.
This class represents a coordinate reference system (CRS).
@ WKT_PREFERRED_GDAL
Preferred format for conversion of CRS to WKT for use with the GDAL library.
QString toWkt(WktVariant variant=WKT1_GDAL, 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.
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:59
double y
Definition qgspointxy.h:63
double x
Definition qgspointxy.h:62
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.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double width() const
Returns the width of the rectangle.
double height() const
Returns the height of the rectangle.
This class is a composition of two QSettings instances:
Definition qgssettings.h:63
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.
std::unique_ptr< GDALWarpOptions, GDALWarpOptionsDeleter > warp_options_unique_ptr
Scoped GDAL warp options.
void * GDALDatasetH
#define QgsDebugError(str)
Definition qgslogger.h:38
const QgsCoordinateReferenceSystem & crs