22 #define CPL_SUPRESS_CPLUSPLUS
24 #include "gdalwarper.h"
25 #include "cpl_string.h"
28 #include <QNetworkProxy>
36 const QString driverShortName = GDALGetDriverShortName( driver );
37 if ( driverShortName == QLatin1String(
"SQLite" ) )
42 char **driverMetadata = GDALGetMetadata( driver,
nullptr );
43 return CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE,
false ) &&
44 CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER,
false );
54 GDALDriverH hDriverMem = GDALGetDriverByName(
"MEM" );
62 const double cellSizeX = extent.
width() / width;
63 const double cellSizeY = extent.
height() / height;
64 double geoTransform[6];
66 geoTransform[1] = cellSizeX;
68 geoTransform[3] = extent.
yMinimum() + ( cellSizeY * height );
70 geoTransform[5] = -cellSizeY;
73 GDALSetGeoTransform( hSrcDS.get(), geoTransform );
79 const double cellSizeX = extent.
width() / width;
80 const double cellSizeY = extent.
height() / height;
81 double geoTransform[6];
83 geoTransform[1] = cellSizeX;
85 geoTransform[3] = extent.
yMinimum() + ( cellSizeY * height );
87 geoTransform[5] = -cellSizeY;
89 GDALDriverH hDriver = GDALGetDriverByName(
"GTiff" );
96 gdal::dataset_unique_ptr hDstDS( GDALCreate( hDriver, filename.toLocal8Bit().constData(), width, height, 1, dataType,
nullptr ) );
104 GDALSetGeoTransform( hDstDS.get(), geoTransform );
110 if ( image.isNull() )
113 const QRgb *rgb =
reinterpret_cast<const QRgb *
>( image.constBits() );
114 GDALDriverH hDriverMem = GDALGetDriverByName(
"MEM" );
119 gdal::dataset_unique_ptr hSrcDS( GDALCreate( hDriverMem,
"", image.width(), image.height(), 0, GDT_Byte,
nullptr ) );
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 );
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 );
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 );
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 );
155 psWarpOptions->hSrcDS = hSrcDS;
156 psWarpOptions->hDstDS = hDstDS;
158 psWarpOptions->nBandCount = 1;
159 psWarpOptions->panSrcBands =
reinterpret_cast< int *
>( CPLMalloc(
sizeof(
int ) * 1 ) );
160 psWarpOptions->panDstBands =
reinterpret_cast< int *
>( CPLMalloc(
sizeof(
int ) * 1 ) );
161 psWarpOptions->panSrcBands[0] = 1;
162 psWarpOptions->panDstBands[0] = 1;
164 psWarpOptions->eResampleAlg = resampleAlg;
167 char **papszOptions =
nullptr;
168 if ( pszCoordinateOperation !=
nullptr )
169 papszOptions = CSLSetNameValue( papszOptions,
"COORDINATE_OPERATION", pszCoordinateOperation );
170 psWarpOptions->pTransformerArg = GDALCreateGenImgProjTransformer2( hSrcDS, hDstDS, papszOptions );
171 CSLDestroy( papszOptions );
173 if ( ! psWarpOptions->pTransformerArg )
178 psWarpOptions->pfnTransformer = GDALGenImgProjTransform;
181 GDALWarpOperation oOperation;
182 oOperation.Initialize( psWarpOptions.get() );
184 const bool retVal { oOperation.ChunkAndWarpImage( 0, 0, GDALGetRasterXSize( hDstDS ), GDALGetRasterYSize( hDstDS ) ) == CE_None };
185 GDALDestroyGenImgProjTransformer( psWarpOptions->pTransformerArg );
195 GDALRasterIOExtraArg extra;
196 INIT_RASTERIO_EXTRA_ARG( extra );
197 extra.eResampleAlg = resampleAlg;
199 QImage res( outputSize, image.format() );
203 GByte *rgb =
reinterpret_cast<GByte *
>( res.bits() );
205 CPLErr err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 1 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 2, outputSize.width(),
206 outputSize.height(), GDT_Byte,
sizeof( QRgb ), res.bytesPerLine(), &extra );
207 if ( err != CE_None )
209 QgsDebugMsg( QStringLiteral(
"failed to read red band" ) );
213 err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 2 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 1, outputSize.width(),
214 outputSize.height(), GDT_Byte,
sizeof( QRgb ), res.bytesPerLine(), &extra );
215 if ( err != CE_None )
217 QgsDebugMsg( QStringLiteral(
"failed to read green band" ) );
221 err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 3 ), GF_Read, 0, 0, image.width(), image.height(), rgb, outputSize.width(),
222 outputSize.height(), GDT_Byte,
sizeof( QRgb ), res.bytesPerLine(), &extra );
223 if ( err != CE_None )
225 QgsDebugMsg( QStringLiteral(
"failed to read blue band" ) );
229 err = GDALRasterIOEx( GDALGetRasterBand( srcDS.get(), 4 ), GF_Read, 0, 0, image.width(), image.height(), rgb + 3, outputSize.width(),
230 outputSize.height(), GDT_Byte,
sizeof( QRgb ), res.bytesPerLine(), &extra );
231 if ( err != CE_None )
233 QgsDebugMsg( QStringLiteral(
"failed to read alpha band" ) );
243 GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
247 char **GDALmetadata = GDALGetMetadata( myGdalDriver,
nullptr );
248 message += QLatin1String(
"Format Details:\n" );
249 message += QStringLiteral(
" Extension: %1\n" ).arg( CSLFetchNameValue( GDALmetadata, GDAL_DMD_EXTENSION ) );
250 message += QStringLiteral(
" Short Name: %1" ).arg( GDALGetDriverShortName( myGdalDriver ) );
251 message += QStringLiteral(
" / Long Name: %1\n" ).arg( GDALGetDriverLongName( myGdalDriver ) );
252 message += QStringLiteral(
" Help page: http://www.gdal.org/%1\n\n" ).arg( CSLFetchNameValue( GDALmetadata, GDAL_DMD_HELPTOPIC ) );
256 CPLXMLNode *psCOL = CPLParseXMLString( GDALGetMetadataItem( myGdalDriver,
257 GDAL_DMD_CREATIONOPTIONLIST,
"" ) );
258 char *pszFormattedXML = CPLSerializeXMLTree( psCOL );
259 if ( pszFormattedXML )
260 message += QString( pszFormattedXML );
262 CPLDestroyXMLNode( psCOL );
263 if ( pszFormattedXML )
264 CPLFree( pszFormattedXML );
271 char **papszRetList =
nullptr;
272 const auto constList = list;
273 for (
const QString &elem : constList )
275 papszRetList = CSLAddString( papszRetList, elem.toLocal8Bit().constData() );
282 GDALDriverH myGdalDriver = GDALGetDriverByName( format.toLocal8Bit().constData() );
283 if ( ! myGdalDriver )
284 return QStringLiteral(
"invalid GDAL driver" );
288 const int ok = GDALValidateCreationOptions( myGdalDriver, papszOptions );
289 CSLDestroy( papszOptions );
292 return QStringLiteral(
"Failed GDALValidateCreationOptions() test" );
296 #if GDAL_VERSION_NUM < GDAL_COMPUTE_VERSION(3,2,0)
298 GDALDatasetH GDALAutoCreateWarpedVRTEx(
GDALDatasetH hSrcDS,
const char *pszSrcWKT,
const char *pszDstWKT, GDALResampleAlg eResampleAlg,
299 double dfMaxError,
const GDALWarpOptions *psOptionsIn,
char **papszTransformerOptions )
301 VALIDATE_POINTER1( hSrcDS,
"GDALAutoCreateWarpedVRT",
nullptr );
306 GDALWarpOptions *psWO =
nullptr;
307 if ( psOptionsIn !=
nullptr )
308 psWO = GDALCloneWarpOptions( psOptionsIn );
310 psWO = GDALCreateWarpOptions();
312 psWO->eResampleAlg = eResampleAlg;
314 psWO->hSrcDS = hSrcDS;
316 GDALWarpInitDefaultBandMapping( psWO, GDALGetRasterCount( hSrcDS ) );
321 for (
int i = 0; i < psWO->nBandCount; i++ )
323 GDALRasterBandH rasterBand = GDALGetRasterBand( psWO->hSrcDS, psWO->panSrcBands[i] );
326 double noDataValue = GDALGetRasterNoDataValue( rasterBand, &hasNoDataValue );
328 if ( hasNoDataValue )
331 int bClamped = FALSE;
332 int bRounded = FALSE;
334 GDALAdjustValueToDataType( GDALGetRasterDataType( rasterBand ),
335 noDataValue, &bClamped, &bRounded ) );
338 GDALWarpInitNoDataReal( psWO, -1e10 );
340 psWO->padfSrcNoDataReal[i] = noDataValue;
341 psWO->padfDstNoDataReal[i] = noDataValue;
346 if ( psWO->padfDstNoDataReal !=
nullptr )
348 if ( CSLFetchNameValue( psWO->papszWarpOptions,
"INIT_DEST" ) ==
nullptr )
350 psWO->papszWarpOptions =
351 CSLSetNameValue( psWO->papszWarpOptions,
"INIT_DEST",
"NO_DATA" );
358 psWO->pfnTransformer = GDALGenImgProjTransform;
360 char **papszOptions =
nullptr;
361 if ( pszSrcWKT !=
nullptr )
362 papszOptions = CSLSetNameValue( papszOptions,
"SRC_SRS", pszSrcWKT );
363 if ( pszDstWKT !=
nullptr )
364 papszOptions = CSLSetNameValue( papszOptions,
"DST_SRS", pszDstWKT );
365 papszOptions = CSLMerge( papszOptions, papszTransformerOptions );
366 psWO->pTransformerArg =
367 GDALCreateGenImgProjTransformer2( psWO->hSrcDS,
nullptr,
369 CSLDestroy( papszOptions );
371 if ( psWO->pTransformerArg ==
nullptr )
373 GDALDestroyWarpOptions( psWO );
380 double adfDstGeoTransform[6] = { 0.0 };
384 GDALSuggestedWarpOutput( hSrcDS, psWO->pfnTransformer,
385 psWO->pTransformerArg,
386 adfDstGeoTransform, &nDstPixels, &nDstLines );
387 if ( eErr != CE_None )
389 GDALDestroyTransformer( psWO->pTransformerArg );
390 GDALDestroyWarpOptions( psWO );
399 GDALSetGenImgProjTransformerDstGeoTransform(
400 psWO->pTransformerArg, adfDstGeoTransform );
405 if ( dfMaxError > 0.0 )
407 psWO->pTransformerArg =
408 GDALCreateApproxTransformer( psWO->pfnTransformer,
409 psWO->pTransformerArg,
411 psWO->pfnTransformer = GDALApproxTransform;
412 GDALApproxTransformerOwnsSubtransformer( psWO->pTransformerArg, TRUE );
419 = GDALCreateWarpedVRT( hSrcDS, nDstPixels, nDstLines,
420 adfDstGeoTransform, psWO );
422 GDALDestroyWarpOptions( psWO );
424 if ( pszDstWKT !=
nullptr )
425 GDALSetProjection( hDstDS, pszDstWKT );
426 else if ( pszSrcWKT !=
nullptr )
427 GDALSetProjection( hDstDS, pszSrcWKT );
428 else if ( GDALGetGCPCount( hSrcDS ) > 0 )
429 GDALSetProjection( hDstDS, GDALGetGCPProjection( hSrcDS ) );
431 GDALSetProjection( hDstDS, GDALGetProjectionRef( hSrcDS ) );
440 const char *pszSrcWKT,
441 const char *pszDstWKT,
442 GDALResampleAlg eResampleAlg,
444 const GDALWarpOptions *psOptionsIn )
446 char **opts =
nullptr;
447 if ( GDALGetMetadata( hSrcDS,
"RPC" ) )
450 const char *heightOffStr = GDALGetMetadataItem( hSrcDS,
"HEIGHT_OFF",
"RPC" );
452 opts = CSLAddNameValue( opts,
"RPC_HEIGHT", heightOffStr );
455 return GDALAutoCreateWarpedVRTEx( hSrcDS, pszSrcWKT, pszDstWKT, eResampleAlg, dfMaxError, psOptionsIn, opts );
460 char **opts = CSLDuplicate( papszOptions );
461 if ( GDALGetMetadata( hSrcDS,
"RPC" ) )
464 const char *heightOffStr = GDALGetMetadataItem( hSrcDS,
"HEIGHT_OFF",
"RPC" );
466 opts = CSLAddNameValue( opts,
"RPC_HEIGHT", heightOffStr );
468 void *transformer = GDALCreateGenImgProjTransformer2( hSrcDS, hDstDS, opts );
473 #ifndef QT_NO_NETWORKPROXY
483 if ( settings.
value( QStringLiteral(
"proxy/proxyEnabled" ),
false ).toBool() )
487 if ( ! proxies.isEmpty() )
489 const QNetworkProxy proxy( proxies.first() );
494 const QString proxyHost( proxy.hostName() );
495 const qint16 proxyPort( proxy.port() );
497 const QString proxyUser( proxy.user() );
498 const QString proxyPassword( proxy.password() );
500 if ( ! proxyHost.isEmpty() )
502 QString connection( proxyHost );
505 connection +=
':' + QString::number( proxyPort );
507 CPLSetConfigOption(
"GDAL_HTTP_PROXY", connection.toUtf8() );
508 if ( ! proxyUser.isEmpty( ) )
510 QString credentials( proxyUser );
511 if ( ! proxyPassword.isEmpty( ) )
513 credentials +=
':' + proxyPassword;
515 CPLSetConfigOption(
"GDAL_HTTP_PROXYUSERPWD", credentials.toUtf8() );
524 const QFileInfo info( path );
525 const long long size = info.size();
531 const QString suffix = info.suffix().toLower();
532 static const QStringList sFileSizeDependentExtensions
534 QStringLiteral(
"xlsx" ),
535 QStringLiteral(
"ods" ),
536 QStringLiteral(
"csv" )
538 if ( sFileSizeDependentExtensions.contains( suffix ) )
541 return size < smallFileSizeLimit;
551 #if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(3,4,0)
553 static std::once_flag initialized;
554 static QStringList SUPPORTED_DB_LAYERS_EXTENSIONS;
555 std::call_once( initialized, [ = ]
559 GDALDriverH driver =
nullptr;
561 QSet< QString > extensions;
563 for (
int i = 0; i < GDALGetDriverCount(); ++i )
565 driver = GDALGetDriver( i );
572 bool isMultiLayer =
false;
573 if ( QString( GDALGetMetadataItem( driver, GDAL_DCAP_RASTER,
nullptr ) ) == QLatin1String(
"YES" ) )
575 if ( GDALGetMetadataItem( driver, GDAL_DMD_SUBDATASETS,
nullptr ) !=
nullptr )
580 if ( !isMultiLayer && QString( GDALGetMetadataItem( driver, GDAL_DCAP_VECTOR,
nullptr ) ) == QLatin1String(
"YES" ) )
582 if ( GDALGetMetadataItem( driver, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS,
nullptr ) !=
nullptr )
591 const QString driverExtensions = GDALGetMetadataItem( driver, GDAL_DMD_EXTENSIONS,
"" );
592 if ( driverExtensions.isEmpty() )
595 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
596 const QStringList splitExtensions = driverExtensions.split(
' ', QString::SkipEmptyParts );
598 const QStringList splitExtensions = driverExtensions.split(
' ', Qt::SkipEmptyParts );
601 for (
const QString &ext : splitExtensions )
602 extensions.insert( ext );
605 SUPPORTED_DB_LAYERS_EXTENSIONS = qgis::setToList( extensions );
607 return SUPPORTED_DB_LAYERS_EXTENSIONS;
610 static const QStringList SUPPORTED_DB_LAYERS_EXTENSIONS
612 QStringLiteral(
"gpkg" ),
613 QStringLiteral(
"sqlite" ),
614 QStringLiteral(
"db" ),
615 QStringLiteral(
"gdb" ),
616 QStringLiteral(
"kml" ),
617 QStringLiteral(
"kmz" ),
618 QStringLiteral(
"osm" ),
619 QStringLiteral(
"mdb" ),
620 QStringLiteral(
"accdb" ),
621 QStringLiteral(
"xls" ),
622 QStringLiteral(
"xlsx" ),
623 QStringLiteral(
"ods" ),
624 QStringLiteral(
"gpx" ),
625 QStringLiteral(
"pdf" ),
626 QStringLiteral(
"pbf" ),
627 QStringLiteral(
"vrt" ),
628 QStringLiteral(
"nc" ),
629 QStringLiteral(
"shp.zip" ) };
630 return SUPPORTED_DB_LAYERS_EXTENSIONS;
636 CPLPushErrorHandler( CPLQuietErrorHandler );
638 GDALDriverH hDriver =
nullptr;
643 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_VECTOR,
nullptr,
nullptr );
647 hDriver = GDALIdentifyDriverEx( vrtPath.toUtf8().constData(), GDAL_OF_RASTER,
nullptr,
nullptr );
659 CPLPopErrorHandler();
660 return static_cast< bool >( hDriver );
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 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 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 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 bool vrtMatchesLayerType(const QString &vrtPath, QgsMapLayerType type)
Returns true if the VRT file at the specified path is a VRT matching the given layer type.
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.
static QgsNetworkAccessManager * instance(Qt::ConnectionType connectionType=Qt::BlockingQueuedConnection)
Returns a pointer to the active QgsNetworkAccessManager for the current thread.
A rectangle specified with double values.
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
QgsMapLayerType
Types of layers that can be added to a map.
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
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.
const QgsCoordinateReferenceSystem & crs