QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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 GDALWarpOperation oOperation;
276 oOperation.Initialize( psWarpOptions.get() );
277
278 const bool 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( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).toUtf8().constData() );
303 papszOptions = CSLSetNameValue( papszOptions, "DST_SRS", destinationCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).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 QgsDebugMsg( 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 QgsDebugMsg( 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 QgsDebugMsg( 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 QgsDebugMsg( 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 extensions.insert( ext );
672 }
673
674 SUPPORTED_DB_LAYERS_EXTENSIONS = QStringList( extensions.constBegin(), extensions.constEnd() );
675 } );
676 return SUPPORTED_DB_LAYERS_EXTENSIONS;
677
678#else
679 static const QStringList SUPPORTED_DB_LAYERS_EXTENSIONS
680 {
681 QStringLiteral( "gpkg" ),
682 QStringLiteral( "sqlite" ),
683 QStringLiteral( "db" ),
684 QStringLiteral( "gdb" ),
685 QStringLiteral( "kml" ),
686 QStringLiteral( "kmz" ),
687 QStringLiteral( "osm" ),
688 QStringLiteral( "mdb" ),
689 QStringLiteral( "accdb" ),
690 QStringLiteral( "xls" ),
691 QStringLiteral( "xlsx" ),
692 QStringLiteral( "ods" ),
693 QStringLiteral( "gpx" ),
694 QStringLiteral( "pdf" ),
695 QStringLiteral( "pbf" ),
696 QStringLiteral( "vrt" ),
697 QStringLiteral( "nc" ),
698 QStringLiteral( "shp.zip" ) };
699 return SUPPORTED_DB_LAYERS_EXTENSIONS;
700#endif
701}
702
703bool QgsGdalUtils::vrtMatchesLayerType( const QString &vrtPath, Qgis::LayerType type )
704{
705 CPLPushErrorHandler( CPLQuietErrorHandler );
706 CPLErrorReset();
707 GDALDriverH hDriver = nullptr;
708
709 switch ( type )
710 {
711 case Qgis::LayerType::Vector:
712 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_VECTOR, nullptr, nullptr );
713 break;
714
715 case Qgis::LayerType::Raster:
716 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_RASTER, nullptr, nullptr );
717 break;
718
719 case Qgis::LayerType::Plugin:
720 case Qgis::LayerType::Mesh:
721 case Qgis::LayerType::VectorTile:
722 case Qgis::LayerType::Annotation:
723 case Qgis::LayerType::PointCloud:
724 case Qgis::LayerType::Group:
725 break;
726 }
727
728 CPLPopErrorHandler();
729 return static_cast< bool >( hDriver );
730}
731#endif
DataType
Raster data types.
Definition: qgis.h:242
@ 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:115
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 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 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 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 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 void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
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
Q_GADGET double x
Definition: qgspointxy.h:62
Raster data container.
int width() const SIP_HOLDGIL
Returns the width (number of columns) of the raster block.
char * bits(int row, int column)
Returns a pointer to block data.
Qgis::DataType dataType() const SIP_HOLDGIL
Returns data type.
double noDataValue() const SIP_HOLDGIL
Returns no data value.
int height() const SIP_HOLDGIL
Returns the height (number of rows) 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 SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
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.
Definition: qgsogrutils.h:157
std::unique_ptr< GDALWarpOptions, GDALWarpOptionsDeleter > warp_options_unique_ptr
Scoped GDAL warp options.
Definition: qgsogrutils.h:172
void * GDALDatasetH
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs