30 #include <QCoreApplication> 
   31 #include <QProgressDialog> 
   32 #include <QTextStream> 
   33 #include <QMessageBox> 
   34 #include <QRegularExpression> 
   39 #include <cpl_string.h> 
   48   double geoTransform[6];
 
   49   globalOutputParameters( extent, width, height, geoTransform, pixelSize );
 
   51   return initOutput( width, height, 
crs, geoTransform, 1, dataType, QList<bool>(), QList<double>() );
 
   60   double geoTransform[6];
 
   61   globalOutputParameters( extent, width, height, geoTransform, pixelSize );
 
   63   return initOutput( width, height, 
crs, geoTransform, nBands, dataType, QList<bool>(), QList<double>() );
 
   67   : mOutputUrl( outputUrl )
 
   72 QgsRasterFileWriter::QgsRasterFileWriter()
 
  114   QgsDebugMsgLevel( QStringLiteral( 
"reading from %1" ).arg( 
typeid( *iface ).name() ), 4 );
 
  118     QgsDebugMsg( QStringLiteral( 
"iface->srcInput() == 0" ) );
 
  123   QgsDebugMsgLevel( QStringLiteral( 
"srcInput = %1" ).arg( 
typeid( srcInput ).name() ), 4 );
 
  126   mFeedback = feedback;
 
  133     const QFileInfo fileInfo( mOutputUrl );
 
  134     if ( !fileInfo.exists() )
 
  136       const QDir dir = fileInfo.dir();
 
  137       if ( !dir.mkdir( fileInfo.fileName() ) )
 
  139         QgsDebugMsg( 
"Cannot create output VRT directory " + fileInfo.fileName() + 
" in " + dir.absolutePath() );
 
  146   QFile pyramidFile( mOutputUrl + ( mTiledMode ? 
".vrt.ovr" : 
".ovr" ) );
 
  147   if ( pyramidFile.exists() )
 
  148     pyramidFile.remove();
 
  149   pyramidFile.setFileName( mOutputUrl + ( mTiledMode ? 
".vrt.rrd" : 
".rrd" ) );
 
  150   if ( pyramidFile.exists() )
 
  151     pyramidFile.remove();
 
  153   if ( mMode == 
Image )
 
  155     const WriterError e = writeImageRaster( &iter, nCols, nRows, outputExtent, 
crs, feedback );
 
  160     const WriterError e = writeDataRaster( pipe, &iter, nCols, nRows, outputExtent, 
crs, transformContext, feedback );
 
  183     QgsDebugMsg( QStringLiteral( 
"Cannot get source data provider" ) );
 
  200   for ( 
int i = 2; i <= nBands; ++i )
 
  212   QList<bool> destHasNoDataValueList;
 
  213   QList<double> destNoDataValueList;
 
  214   QList<Qgis::DataType> destDataTypeList;
 
  215   destDataTypeList.reserve( nBands );
 
  216   destHasNoDataValueList.reserve( nBands );
 
  217   destNoDataValueList.reserve( nBands );
 
  219   const bool isGpkgOutput = mOutputProviderKey == 
"gdal" &&
 
  220                             mOutputFormat.compare( QLatin1String( 
"gpkg" ), Qt::CaseInsensitive ) == 0;
 
  222   double geoTransform[6];
 
  223   globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize );
 
  224   const auto srcProviderExtent( srcProvider->
extent() );
 
  226   for ( 
int bandNo = 1; bandNo <= nBands; bandNo++ )
 
  231     bool destHasNoDataValue = 
false;
 
  232     double destNoDataValue = std::numeric_limits<double>::quiet_NaN();
 
  237     if ( srcHasNoDataValue )
 
  242       destHasNoDataValue = 
true;
 
  244     else if ( nuller && !nuller->
noData( bandNo ).isEmpty() )
 
  247       destNoDataValue = nuller->
noData( bandNo ).value( 0 ).min();
 
  248       destHasNoDataValue = 
true;
 
  261         ct.setBallparkTransformsAreAppropriate( 
true );
 
  262         outputExtentInSrcCrs = ct.transformBoundingBox( outputExtent );
 
  264       if ( !srcProviderExtent.contains( outputExtentInSrcCrs ) &&
 
  265            ( std::fabs( srcProviderExtent.xMinimum() - outputExtentInSrcCrs.
xMinimum() ) > geoTransform[1] / 2 ||
 
  266              std::fabs( srcProviderExtent.xMaximum() - outputExtentInSrcCrs.
xMaximum() ) > geoTransform[1] / 2 ||
 
  267              std::fabs( srcProviderExtent.yMinimum() - outputExtentInSrcCrs.
yMinimum() ) > std::fabs( geoTransform[5] ) / 2 ||
 
  268              std::fabs( srcProviderExtent.yMaximum() - outputExtentInSrcCrs.
yMaximum() ) > std::fabs( geoTransform[5] ) / 2 ) )
 
  279           destNoDataValue = typeMinValue;
 
  283           destNoDataValue = typeMaxValue;
 
  290         destHasNoDataValue = 
true;
 
  294     if ( nuller && destHasNoDataValue )
 
  299     QgsDebugMsgLevel( QStringLiteral( 
"bandNo = %1 destDataType = %2 destHasNoDataValue = %3 destNoDataValue = %4" ).arg( bandNo ).arg( 
qgsEnumValueToKey( destDataType ) ).arg( destHasNoDataValue ).arg( destNoDataValue ), 4 );
 
  300     destDataTypeList.append( destDataType );
 
  301     destHasNoDataValueList.append( destHasNoDataValue );
 
  302     destNoDataValueList.append( destNoDataValue );
 
  307   for ( 
int i = 1; i < nBands; i++ )
 
  309     if ( destDataTypeList.value( i ) > destDataType )
 
  311       destDataType = destDataTypeList.value( i );
 
  317   for ( 
int attempt = 0; attempt < 2; attempt ++ )
 
  321     std::unique_ptr<QgsRasterDataProvider> destProvider(
 
  322       initOutput( nCols, nRows, 
crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList ) );
 
  329       if ( !destProvider->isValid() )
 
  331         if ( feedback && !destProvider->error().isEmpty() )
 
  333           feedback->
appendError( destProvider->error().summary() );
 
  337       if ( nCols != destProvider->xSize() || nRows != destProvider->ySize() )
 
  339         QgsDebugMsg( QStringLiteral( 
"Created raster does not have requested dimensions" ) );
 
  342           feedback->
appendError( QObject::tr( 
"Created raster does not have requested dimensions" ) );
 
  346       if ( nBands != destProvider->bandCount() )
 
  348         QgsDebugMsg( QStringLiteral( 
"Created raster does not have requested band count" ) );
 
  351           feedback->
appendError( QObject::tr( 
"Created raster does not have requested band count" ) );
 
  359         destDataType = destProvider->dataType( 1 );
 
  363     error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, 
crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider.get(), feedback );
 
  370         destProvider->remove();
 
  371         destProvider.reset();
 
  379       for ( 
int i = 0; i < nBands; i++ )
 
  381         double destNoDataValue;
 
  383         destDataTypeList.replace( i, destDataType );
 
  384         destNoDataValueList.replace( i, destNoDataValue );
 
  386       destDataType = destDataTypeList.value( 0 );
 
  399 static int qgsDivRoundUp( 
int a, 
int b )
 
  401   return a / b + ( ( ( a % b ) != 0 ) ? 1 : 0 );
 
  406     int nCols, 
int nRows,
 
  410     const QList<bool> &destHasNoDataValueList,
 
  411     const QList<double> &destNoDataValueList,
 
  416   Q_UNUSED( destHasNoDataValueList )
 
  430   std::vector< std::unique_ptr<QgsRasterBlock> > blockList;
 
  431   std::vector< std::unique_ptr<QgsRasterBlock> > destBlockList;
 
  433   blockList.resize( nBands );
 
  434   destBlockList.resize( nBands );
 
  436   for ( 
int i = 1; i <= nBands; ++i )
 
  439     if ( destProvider && destHasNoDataValueList.value( i - 1 ) ) 
 
  441       destProvider->
setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
 
  451     nParts = nPartsX * nPartsY;
 
  458     for ( 
int i = 1; i <= nBands; ++i )
 
  466           const QString vrtFilePath( mOutputUrl + 
'/' + vrtFileName() );
 
  467           writeVRT( vrtFilePath );
 
  470             buildPyramids( vrtFilePath );
 
  477             buildPyramids( mOutputUrl, destProvider );
 
  484       blockList[i - 1].reset( block );
 
  488     if ( feedback && fileIndex < ( nParts - 1 ) )
 
  490       feedback->
setProgress( 100.0 * fileIndex / 
static_cast< double >( nParts ) );
 
  498     for ( 
int i = 1; i <= nBands; ++i )
 
  500       if ( srcProvider && srcProvider->
dataType( i ) == destDataType )
 
  507         blockList[i - 1]->convert( destDataType );
 
  509       destBlockList[i - 1] = std::move( blockList[i - 1] );
 
  514       std::unique_ptr< QgsRasterDataProvider > partDestProvider( createPartProvider( outputExtent,
 
  515           nCols, iterCols, iterRows,
 
  516           iterLeft, iterTop, mOutputUrl,
 
  517           fileIndex, nBands, destDataType, 
crs ) );
 
  519       if ( !partDestProvider || !partDestProvider->isValid() )
 
  525       for ( 
int i = 1; i <= nBands; ++i )
 
  527         if ( destHasNoDataValueList.value( i - 1 ) )
 
  529           partDestProvider->setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
 
  531         if ( destBlockList[ i - 1 ]->isEmpty() )
 
  534         if ( !partDestProvider->write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, 0, 0 ) )
 
  538         addToVRT( partFileName( fileIndex ), i, iterCols, iterRows, iterLeft, iterTop );
 
  542     else if ( destProvider )
 
  545       for ( 
int i = 1; i <= nBands; ++i )
 
  547         if ( destBlockList[ i - 1 ]->isEmpty() )
 
  550         if ( !destProvider->
write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, iterLeft, iterTop ) )
 
  586   const size_t nMaxPixels = 
static_cast<size_t>( mMaxTileWidth ) * mMaxTileHeight;
 
  587   std::vector<unsigned char> redData( nMaxPixels );
 
  588   std::vector<unsigned char> greenData( nMaxPixels );
 
  589   std::vector<unsigned char> blueData( nMaxPixels );
 
  590   std::vector<unsigned char> alphaData( nMaxPixels );
 
  591   int iterLeft = 0, iterTop = 0, iterCols = 0, iterRows = 0;
 
  596   double geoTransform[6];
 
  597   globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize );
 
  599   const int nOutputBands = 4;
 
  600   std::unique_ptr< QgsRasterDataProvider > destProvider( initOutput( nCols, nRows, 
crs, geoTransform, nOutputBands, 
Qgis::DataType::Byte ) );
 
  607     if ( !destProvider->
isValid() )
 
  615     if ( nCols != destProvider->
xSize() || nRows != destProvider->
ySize() )
 
  617       QgsDebugMsg( QStringLiteral( 
"Created raster does not have requested dimensions" ) );
 
  620         feedback->
appendError( QObject::tr( 
"Created raster does not have requested dimensions" ) );
 
  624     if ( nOutputBands != destProvider->
bandCount() )
 
  626       QgsDebugMsg( QStringLiteral( 
"Created raster does not have requested band count" ) );
 
  629         feedback->
appendError( QObject::tr( 
"Created raster does not have requested band count" ) );
 
  635       QgsDebugMsg( QStringLiteral( 
"Created raster does not have requested data type" ) );
 
  638         feedback->
appendError( QObject::tr( 
"Created raster does not have requested data type" ) );
 
  651     nParts = nPartsX * nPartsY;
 
  654   std::unique_ptr< QgsRasterBlock > inputBlock;
 
  655   while ( iter->
readNextRasterPart( 1, iterCols, iterRows, inputBlock, iterLeft, iterTop ) )
 
  657     if ( !inputBlock || inputBlock->isEmpty() )
 
  662     if ( feedback && fileIndex < ( nParts - 1 ) )
 
  664       feedback->
setProgress( 100.0 * fileIndex / 
static_cast< double >( nParts ) );
 
  672     const qgssize nPixels = 
static_cast< qgssize >( iterCols ) * iterRows;
 
  673     for ( 
qgssize i = 0; i < nPixels; ++i )
 
  675       QRgb 
c = inputBlock->color( i );
 
  676       if ( isPremultiplied )
 
  678         c = qUnpremultiply( 
c );
 
  680       redData[i] = 
static_cast<unsigned char>( qRed( 
c ) );
 
  681       greenData[i] = 
static_cast<unsigned char>( qGreen( 
c ) );
 
  682       blueData[i] = 
static_cast<unsigned char>( qBlue( 
c ) );
 
  683       alphaData[i] = 
static_cast<unsigned char>( qAlpha( 
c ) );
 
  689       std::unique_ptr< QgsRasterDataProvider > partDestProvider( createPartProvider( outputExtent,
 
  690           nCols, iterCols, iterRows,
 
  691           iterLeft, iterTop, mOutputUrl, fileIndex,
 
  694       if ( !partDestProvider || partDestProvider->isValid() )
 
  700       if ( !partDestProvider->write( &redData[0], 1, iterCols, iterRows, 0, 0 ) ||
 
  701            !partDestProvider->write( &greenData[0], 2, iterCols, iterRows, 0, 0 ) ||
 
  702            !partDestProvider->write( &blueData[0], 3, iterCols, iterRows, 0, 0 ) ||
 
  703            !partDestProvider->write( &alphaData[0], 4, iterCols, iterRows, 0, 0 ) )
 
  708       addToVRT( partFileName( fileIndex ), 1, iterCols, iterRows, iterLeft, iterTop );
 
  709       addToVRT( partFileName( fileIndex ), 2, iterCols, iterRows, iterLeft, iterTop );
 
  710       addToVRT( partFileName( fileIndex ), 3, iterCols, iterRows, iterLeft, iterTop );
 
  711       addToVRT( partFileName( fileIndex ), 4, iterCols, iterRows, iterLeft, iterTop );
 
  713     else if ( destProvider )
 
  715       if ( !destProvider->
write( &redData[0], 1, iterCols, iterRows, iterLeft, iterTop ) ||
 
  716            !destProvider->
write( &greenData[0], 2, iterCols, iterRows, iterLeft, iterTop ) ||
 
  717            !destProvider->
write( &blueData[0], 3, iterCols, iterRows, iterLeft, iterTop ) ||
 
  718            !destProvider->
write( &alphaData[0], 4, iterCols, iterRows, iterLeft, iterTop ) )
 
  726   destProvider.reset();
 
  735     const QString vrtFilePath( mOutputUrl + 
'/' + vrtFileName() );
 
  736     writeVRT( vrtFilePath );
 
  739       buildPyramids( vrtFilePath );
 
  746       buildPyramids( mOutputUrl );
 
  752 void QgsRasterFileWriter::addToVRT( 
const QString &filename, 
int band, 
int xSize, 
int ySize, 
int xOffset, 
int yOffset )
 
  754   QDomElement bandElem = mVRTBands.value( band - 1 );
 
  756   QDomElement simpleSourceElem = mVRTDocument.createElement( QStringLiteral( 
"SimpleSource" ) );
 
  759   QDomElement sourceFilenameElem = mVRTDocument.createElement( QStringLiteral( 
"SourceFilename" ) );
 
  760   sourceFilenameElem.setAttribute( QStringLiteral( 
"relativeToVRT" ), QStringLiteral( 
"1" ) );
 
  761   const QDomText sourceFilenameText = mVRTDocument.createTextNode( filename );
 
  762   sourceFilenameElem.appendChild( sourceFilenameText );
 
  763   simpleSourceElem.appendChild( sourceFilenameElem );
 
  766   QDomElement sourceBandElem = mVRTDocument.createElement( QStringLiteral( 
"SourceBand" ) );
 
  767   const QDomText sourceBandText = mVRTDocument.createTextNode( QString::number( band ) );
 
  768   sourceBandElem.appendChild( sourceBandText );
 
  769   simpleSourceElem.appendChild( sourceBandElem );
 
  772   QDomElement sourcePropertiesElem = mVRTDocument.createElement( QStringLiteral( 
"SourceProperties" ) );
 
  773   sourcePropertiesElem.setAttribute( QStringLiteral( 
"RasterXSize" ), xSize );
 
  774   sourcePropertiesElem.setAttribute( QStringLiteral( 
"RasterYSize" ), ySize );
 
  775   sourcePropertiesElem.setAttribute( QStringLiteral( 
"BlockXSize" ), xSize );
 
  776   sourcePropertiesElem.setAttribute( QStringLiteral( 
"BlockYSize" ), ySize );
 
  777   sourcePropertiesElem.setAttribute( QStringLiteral( 
"DataType" ), QStringLiteral( 
"Byte" ) );
 
  778   simpleSourceElem.appendChild( sourcePropertiesElem );
 
  781   QDomElement srcRectElem = mVRTDocument.createElement( QStringLiteral( 
"SrcRect" ) );
 
  782   srcRectElem.setAttribute( QStringLiteral( 
"xOff" ), QStringLiteral( 
"0" ) );
 
  783   srcRectElem.setAttribute( QStringLiteral( 
"yOff" ), QStringLiteral( 
"0" ) );
 
  784   srcRectElem.setAttribute( QStringLiteral( 
"xSize" ), xSize );
 
  785   srcRectElem.setAttribute( QStringLiteral( 
"ySize" ), ySize );
 
  786   simpleSourceElem.appendChild( srcRectElem );
 
  789   QDomElement dstRectElem = mVRTDocument.createElement( QStringLiteral( 
"DstRect" ) );
 
  790   dstRectElem.setAttribute( QStringLiteral( 
"xOff" ), xOffset );
 
  791   dstRectElem.setAttribute( QStringLiteral( 
"yOff" ), yOffset );
 
  792   dstRectElem.setAttribute( QStringLiteral( 
"xSize" ), xSize );
 
  793   dstRectElem.setAttribute( QStringLiteral( 
"ySize" ), ySize );
 
  794   simpleSourceElem.appendChild( dstRectElem );
 
  796   bandElem.appendChild( simpleSourceElem );
 
  799 void QgsRasterFileWriter::buildPyramids( 
const QString &filename, 
QgsRasterDataProvider *destProviderIn )
 
  807     destProvider = qobject_cast< QgsRasterDataProvider * >( 
QgsProviderRegistry::instance()->createProvider( mOutputProviderKey, filename, providerOptions ) );
 
  808     if ( !destProvider || !destProvider->
isValid() )
 
  818   QList< QgsRasterPyramid> myPyramidList;
 
  819   if ( ! mPyramidsList.isEmpty() )
 
  821   for ( 
int myCounterInt = 0; myCounterInt < myPyramidList.count(); myCounterInt++ )
 
  823     myPyramidList[myCounterInt].setBuild( 
true );
 
  826   QgsDebugMsgLevel( QStringLiteral( 
"building pyramids : %1 pyramids, %2 resampling, %3 format, %4 options" ).arg( myPyramidList.count() ).arg( mPyramidsResampling ).arg( mPyramidsFormat ).arg( mPyramidsConfigOptions.count() ), 4 );
 
  828   const QString res = destProvider->
buildPyramids( myPyramidList, mPyramidsResampling,
 
  829                       mPyramidsFormat, mPyramidsConfigOptions );
 
  835     QString title, message;
 
  836     if ( res == QLatin1String( 
"ERROR_WRITE_ACCESS" ) )
 
  838       title = QObject::tr( 
"Building Pyramids" );
 
  839       message = QObject::tr( 
"Write access denied. Adjust the file permissions and try again." );
 
  841     else if ( res == QLatin1String( 
"ERROR_WRITE_FORMAT" ) )
 
  843       title = QObject::tr( 
"Building Pyramids" );
 
  844       message = QObject::tr( 
"The file was not writable. Some formats do not " 
  845                              "support pyramid overviews. Consult the GDAL documentation if in doubt." );
 
  847     else if ( res == QLatin1String( 
"FAILED_NOT_SUPPORTED" ) )
 
  849       title = QObject::tr( 
"Building Pyramids" );
 
  850       message = QObject::tr( 
"Building pyramid overviews is not supported on this type of raster." );
 
  852     else if ( res == QLatin1String( 
"ERROR_VIRTUAL" ) )
 
  854       title = QObject::tr( 
"Building Pyramids" );
 
  855       message = QObject::tr( 
"Building pyramid overviews is not supported on this type of raster." );
 
  857     QMessageBox::warning( 
nullptr, title, message );
 
  860   if ( !destProviderIn )
 
  865 int QgsRasterFileWriter::pyramidsProgress( 
double dfComplete, 
const char *pszMessage, 
void *pData )
 
  867   Q_UNUSED( pszMessage )
 
  868   GDALTermProgress( dfComplete, 0, 0 );
 
  869   QProgressDialog *progressDialog = 
static_cast<QProgressDialog *
>( pData );
 
  870   if ( pData && progressDialog->wasCanceled() )
 
  877     progressDialog->setRange( 0, 100 );
 
  878     progressDialog->setValue( dfComplete * 100 );
 
  884 void QgsRasterFileWriter::createVRT( 
int xSize, 
int ySize, 
const QgsCoordinateReferenceSystem &
crs, 
double *geoTransform, 
Qgis::DataType type, 
const QList<bool> &destHasNoDataValueList, 
const QList<double> &destNoDataValueList )
 
  886   mVRTDocument.clear();
 
  887   QDomElement VRTDatasetElem = mVRTDocument.createElement( QStringLiteral( 
"VRTDataset" ) );
 
  890   VRTDatasetElem.setAttribute( QStringLiteral( 
"rasterXSize" ), xSize );
 
  891   VRTDatasetElem.setAttribute( QStringLiteral( 
"rasterYSize" ), ySize );
 
  892   mVRTDocument.appendChild( VRTDatasetElem );
 
  895   QDomElement SRSElem = mVRTDocument.createElement( QStringLiteral( 
"SRS" ) );
 
  896   const QDomText crsText = mVRTDocument.createTextNode( 
crs.
toWkt() );
 
  897   SRSElem.appendChild( crsText );
 
  898   VRTDatasetElem.appendChild( SRSElem );
 
  903     QDomElement geoTransformElem = mVRTDocument.createElement( QStringLiteral( 
"GeoTransform" ) );
 
  904     const QString geoTransformString = QString::number( geoTransform[0], 
'f', 6 ) + 
", " + QString::number( geoTransform[1] ) + 
", " + QString::number( geoTransform[2] ) +
 
  905                                        ", "  + QString::number( geoTransform[3], 
'f', 6 ) + 
", " + QString::number( geoTransform[4] ) + 
", " + QString::number( geoTransform[5] );
 
  906     const QDomText geoTransformText = mVRTDocument.createTextNode( geoTransformString );
 
  907     geoTransformElem.appendChild( geoTransformText );
 
  908     VRTDatasetElem.appendChild( geoTransformElem );
 
  921   QStringList colorInterp;
 
  922   colorInterp << QStringLiteral( 
"Red" ) << QStringLiteral( 
"Green" ) << QStringLiteral( 
"Blue" ) << QStringLiteral( 
"Alpha" );
 
  924   QMap<Qgis::DataType, QString> dataTypes;
 
  936   for ( 
int i = 1; i <= nBands; i++ )
 
  938     QDomElement VRTBand = mVRTDocument.createElement( QStringLiteral( 
"VRTRasterBand" ) );
 
  940     VRTBand.setAttribute( QStringLiteral( 
"band" ), QString::number( i ) );
 
  941     const QString dataType = dataTypes.value( type );
 
  942     VRTBand.setAttribute( QStringLiteral( 
"dataType" ), dataType );
 
  944     if ( mMode == 
Image )
 
  946       VRTBand.setAttribute( QStringLiteral( 
"dataType" ), QStringLiteral( 
"Byte" ) );
 
  947       QDomElement colorInterpElement = mVRTDocument.createElement( QStringLiteral( 
"ColorInterp" ) );
 
  948       const QDomText interpText = mVRTDocument.createTextNode( colorInterp.value( i - 1 ) );
 
  949       colorInterpElement.appendChild( interpText );
 
  950       VRTBand.appendChild( colorInterpElement );
 
  953     if ( !destHasNoDataValueList.isEmpty() && destHasNoDataValueList.value( i - 1 ) )
 
  955       VRTBand.setAttribute( QStringLiteral( 
"NoDataValue" ), QString::number( destNoDataValueList.value( i - 1 ) ) );
 
  958     mVRTBands.append( VRTBand );
 
  959     VRTDatasetElem.appendChild( VRTBand );
 
  963 bool QgsRasterFileWriter::writeVRT( 
const QString &file )
 
  965   QFile outputFile( file );
 
  966   if ( ! outputFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
 
  971   QTextStream outStream( &outputFile );
 
  972   mVRTDocument.save( outStream, 2 );
 
  977     int iterRows, 
int iterLeft, 
int iterTop, 
const QString &outputUrl, 
int fileIndex, 
int nBands, 
Qgis::DataType type,
 
  980   const double mup = extent.
width() / nCols;
 
  981   const double mapLeft = extent.
xMinimum() + iterLeft * mup;
 
  982   const double mapRight = mapLeft + mup * iterCols;
 
  983   const double mapTop = extent.
yMaximum() - iterTop * mup;
 
  984   const double mapBottom = mapTop - iterRows * mup;
 
  985   const QgsRectangle mapRect( mapLeft, mapBottom, mapRight, mapTop );
 
  987   const QString outputFile = 
outputUrl + 
'/' + partFileName( fileIndex );
 
  990   double geoTransform[6];
 
  991   geoTransform[0] = mapRect.xMinimum();
 
  992   geoTransform[1] = mup;
 
  993   geoTransform[2] = 0.0;
 
  994   geoTransform[3] = mapRect.yMaximum();
 
  995   geoTransform[4] = 0.0;
 
  996   geoTransform[5] = -mup;
 
 1003   return destProvider;
 
 1008     const QList<bool> &destHasNoDataValueList, 
const QList<double> &destNoDataValueList )
 
 1012     createVRT( nCols, nRows, 
crs, geoTransform, type, destHasNoDataValueList, destNoDataValueList );
 
 1020     if ( mBuildPyramidsFlag == -4 && mOutputProviderKey == 
"gdal" && mOutputFormat.compare( QLatin1String( 
"gtiff" ), Qt::CaseInsensitive ) == 0 )
 
 1021       mCreateOptions << 
"COPY_SRC_OVERVIEWS=YES";
 
 1026     if ( !destProvider )
 
 1028       QgsDebugMsg( QStringLiteral( 
"No provider created" ) );
 
 1031     return destProvider;
 
 1035 void QgsRasterFileWriter::globalOutputParameters( 
const QgsRectangle &extent, 
int nCols, 
int &nRows,
 
 1036     double *geoTransform, 
double &pixelSize )
 
 1038   pixelSize = extent.
width() / nCols;
 
 1043     nRows = 
static_cast< double >( nCols ) / extent.
width() * extent.
height() + 0.5; 
 
 1045   geoTransform[0] = extent.
xMinimum();
 
 1046   geoTransform[1] = pixelSize;
 
 1047   geoTransform[2] = 0.0;
 
 1048   geoTransform[3] = extent.
yMaximum();
 
 1049   geoTransform[4] = 0.0;
 
 1050   geoTransform[5] = -( extent.
height() / nRows );
 
 1053 QString QgsRasterFileWriter::partFileName( 
int fileIndex )
 
 1056   const QFileInfo outputInfo( mOutputUrl );
 
 1057   return QStringLiteral( 
"%1.%2.tif" ).arg( outputInfo.fileName() ).arg( fileIndex );
 
 1060 QString QgsRasterFileWriter::vrtFileName()
 
 1062   const QFileInfo outputInfo( mOutputUrl );
 
 1063   return QStringLiteral( 
"%1.vrt" ).arg( outputInfo.fileName() );
 
 1068   QString ext = extension.trimmed();
 
 1069   if ( ext.isEmpty() )
 
 1072   if ( ext.startsWith( 
'.' ) )
 
 1076   int const drvCount = GDALGetDriverCount();
 
 1078   for ( 
int i = 0; i < drvCount; ++i )
 
 1080     GDALDriverH drv = GDALGetDriver( i );
 
 1083       char **driverMetadata = GDALGetMetadata( drv, 
nullptr );
 
 1084       if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, 
false ) )
 
 1086         QString drvName = GDALGetDriverShortName( drv );
 
 1087         const QStringList driverExtensions = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, 
nullptr ) ).split( 
' ' );
 
 1089         const auto constDriverExtensions = driverExtensions;
 
 1090         for ( 
const QString &driver : constDriverExtensions )
 
 1092           if ( driver.compare( ext, Qt::CaseInsensitive ) == 0 )
 
 1103   GDALDriverH drv = GDALGetDriverByName( format.toLocal8Bit().data() );
 
 1106     char **driverMetadata = GDALGetMetadata( drv, 
nullptr );
 
 1107     if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_RASTER, 
false ) )
 
 1109       return QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, 
nullptr ) ).split( 
' ' );
 
 1112   return QStringList();
 
 1117   GDALDriverH drv = GDALGetDriverByName( driverName.toLocal8Bit().data() );
 
 1120     const QString drvName = GDALGetDriverLongName( drv );
 
 1121     const QString extensionsString = QString( GDALGetMetadataItem( drv, GDAL_DMD_EXTENSIONS, 
nullptr ) );
 
 1122     if ( extensionsString.isEmpty() )
 
 1126     const QStringList extensions = extensionsString.split( 
' ' );
 
 1127     QString filter = drvName + 
" (";
 
 1128     for ( 
const QString &ext : extensions )
 
 1130       filter.append( QStringLiteral( 
"*.%1 *.%2 " ).arg( ext.toLower(), ext.toUpper() ) );
 
 1132     filter = filter.trimmed().append( QStringLiteral( 
")" ) );
 
 1141   static QReadWriteLock sFilterLock;
 
 1142   static QMap< RasterFormatOptions, QList< QgsRasterFileWriter::FilterFormatDetails > > sFilters;
 
 1146   const auto it = sFilters.constFind( options );
 
 1147   if ( it != sFilters.constEnd() )
 
 1151   int const drvCount = GDALGetDriverCount();
 
 1154   QList< QgsRasterFileWriter::FilterFormatDetails > results;
 
 1158   for ( 
int i = 0; i < drvCount; ++i )
 
 1160     GDALDriverH drv = GDALGetDriver( i );
 
 1165         const QString drvName = GDALGetDriverShortName( drv );
 
 1167         if ( filterString.isEmpty() )
 
 1176           if ( drvName == QLatin1String( 
"GTiff" ) )
 
 1178             tifFormat = details;
 
 1190     return a.driverName < b.driverName;
 
 1197       results.insert( 0, tifFormat );
 
 1201   sFilters.insert( options, results );
 
 1209   QStringList extensions;
 
 1211   const QRegularExpression rx( QStringLiteral( 
"\\*\\.([a-zA-Z0-9]*)" ) );
 
 1215     const QString ext = format.filterString;
 
 1216     const QRegularExpressionMatch match = rx.match( ext );
 
 1217     if ( !match.hasMatch() )
 
 1220     const QString matched = match.captured( 1 );
 
 1221     extensions << matched;