24 #include <QCoreApplication>
25 #include <QProgressDialog>
26 #include <QTextStream>
27 #include <QMessageBox>
30 mMode( Raw ), mOutputUrl( outputUrl ), mOutputProviderKey(
"gdal" ), mOutputFormat(
"GTiff" ),
31 mTiledMode( false ), mMaxTileWidth( 500 ), mMaxTileHeight( 500 ),
32 mBuildPyramidsFlag(
QgsRaster::PyramidsFlagNo ),
33 mPyramidsFormat(
QgsRaster::PyramidsGTiff ),
34 mProgressDialog( 0 ), mPipe( 0 ), mInput( 0 )
39 QgsRasterFileWriter::QgsRasterFileWriter()
41 , mOutputProviderKey(
"gdal" )
42 , mOutputFormat(
"GTiff" )
44 , mMaxTileWidth( 500 )
45 , mMaxTileHeight( 500 )
46 , mBuildPyramidsFlag(
QgsRaster::PyramidsFlagNo )
47 , mPyramidsFormat(
QgsRaster::PyramidsGTiff )
48 , mProgressDialog( 0 )
88 QgsDebugMsg( QString(
"reading from %1" ).arg(
typeid( *iface ).name() ) );
97 mProgressDialog = progressDialog;
104 QFileInfo fileInfo( mOutputUrl );
105 if ( !fileInfo.exists() )
107 QDir dir = fileInfo.dir();
108 if ( !dir.mkdir( fileInfo.fileName() ) )
110 QgsDebugMsg(
"Cannot create output VRT directory " + fileInfo.fileName() +
" in " + dir.absolutePath() );
116 if ( mMode ==
Image )
118 WriterError e = writeImageRaster( &iter, nCols, nRows, outputExtent, crs, progressDialog );
125 WriterError e = writeDataRaster( pipe, &iter, nCols, nRows, outputExtent, crs, progressDialog );
165 for (
int i = 2; i <= nBands; ++i )
177 QList<bool> destHasNoDataValueList;
178 QList<double> destNoDataValueList;
179 QList<QGis::DataType> destDataTypeList;
180 for (
int bandNo = 1; bandNo <= nBands; bandNo++ )
185 bool destHasNoDataValue =
false;
186 double destNoDataValue = std::numeric_limits<double>::quiet_NaN();
189 QgsDebugMsg( QString(
"srcHasNoDataValue = %1 srcNoDataValue = %2" ).arg( srcHasNoDataValue ).arg( srcProvider->
srcNoDataValue( bandNo ) ) );
190 if ( srcHasNoDataValue )
195 destHasNoDataValue =
true;
197 else if ( nuller && nuller->
noData( bandNo ).size() > 0 )
200 destNoDataValue = nuller->
noData( bandNo ).value( 0 ).min();
201 destHasNoDataValue =
true;
208 if ( projector && projector->
destCrs() != projector->
srcCrs() )
211 srcExtent = ct.transformBoundingBox( outputExtent );
224 destNoDataValue = typeMinValue;
228 destNoDataValue = typeMaxValue;
235 destHasNoDataValue =
true;
239 if ( nuller && destHasNoDataValue )
244 QgsDebugMsg( QString(
"bandNo = %1 destDataType = %2 destHasNoDataValue = %3 destNoDataValue = %4" ).arg( bandNo ).arg( destDataType ).arg( destHasNoDataValue ).arg( destNoDataValue ) );
245 destDataTypeList.append( destDataType );
246 destHasNoDataValueList.append( destHasNoDataValue );
247 destNoDataValueList.append( destNoDataValue );
252 for (
int i = 1; i < nBands; i++ )
254 if ( destDataTypeList.value( i ) > destDataType )
256 destDataType = destDataTypeList.value( i );
264 double geoTransform[6];
265 globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize );
268 destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList );
270 WriterError error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog );
287 for (
int i = 0; i < nBands; i++ )
289 double destNoDataValue;
291 destDataTypeList.replace( i, destDataType );
292 destNoDataValueList.replace( i, destNoDataValue );
294 destDataType = destDataTypeList.value( 0 );
297 destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList );
298 error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog );
310 int nCols,
int nRows,
314 QList<bool> destHasNoDataValueList,
315 QList<double> destNoDataValueList,
317 QProgressDialog* progressDialog )
320 Q_UNUSED( destHasNoDataValueList );
326 QgsDebugMsg( QString(
"nBands = %1" ).arg( nBands ) );
334 QList<QgsRasterBlock*> blockList;
335 for (
int i = 1; i <= nBands; ++i )
338 blockList.push_back( 0 );
341 destProvider->
setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
347 if ( progressDialog )
351 nParts = nPartsX * nPartsY;
352 progressDialog->setMaximum( nParts );
353 progressDialog->show();
354 progressDialog->setLabelText(
QObject::tr(
"Reading raster part %1 of %2" ).arg( fileIndex + 1 ).arg( nParts ) );
361 for (
int i = 1; i <= nBands; ++i )
363 if ( !iter->
readNextRasterPart( i, iterCols, iterRows, &( blockList[i - 1] ), iterLeft, iterTop ) )
368 QString vrtFilePath( mOutputUrl +
"/" + vrtFileName() );
369 writeVRT( vrtFilePath );
372 buildPyramids( vrtFilePath );
379 buildPyramids( mOutputUrl );
389 if ( progressDialog && fileIndex < ( nParts - 1 ) )
391 progressDialog->setValue( fileIndex + 1 );
392 progressDialog->setLabelText(
QObject::tr(
"Reading raster part %1 of %2" ).arg( fileIndex + 2 ).arg( nParts ) );
393 QCoreApplication::processEvents( QEventLoop::AllEvents, 1000 );
394 if ( progressDialog->wasCanceled() )
396 for (
int i = 0; i < nBands; ++i )
405 QList<QgsRasterBlock*> destBlockList;
406 for (
int i = 1; i <= nBands; ++i )
408 if ( srcProvider && srcProvider->
dataType( i ) == destDataType )
410 destBlockList.push_back( blockList[i-1] );
415 blockList[i-1]->convert( destDataType );
416 destBlockList.push_back( blockList[i-1] );
424 nCols, iterCols, iterRows,
425 iterLeft, iterTop, mOutputUrl,
426 fileIndex, nBands, destDataType, crs );
428 if ( partDestProvider )
431 for (
int i = 1; i <= nBands; ++i )
433 partDestProvider->
setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
434 partDestProvider->
write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, 0, 0 );
435 delete destBlockList[i - 1];
436 addToVRT( partFileName( fileIndex ), i, iterCols, iterRows, iterLeft, iterTop );
438 delete partDestProvider;
441 else if ( destProvider )
444 for (
int i = 1; i <= nBands; ++i )
446 destProvider->
write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, iterLeft, iterTop );
447 delete destBlockList[i - 1];
479 void* redData =
qgsMalloc( mMaxTileWidth * mMaxTileHeight );
480 void* greenData =
qgsMalloc( mMaxTileWidth * mMaxTileHeight );
481 void* blueData =
qgsMalloc( mMaxTileWidth * mMaxTileHeight );
482 void* alphaData =
qgsMalloc( mMaxTileWidth * mMaxTileHeight );
484 int iterLeft = 0, iterTop = 0, iterCols = 0, iterRows = 0;
490 double geoTransform[6];
491 globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize );
493 destProvider = initOutput( nCols, nRows, crs, geoTransform, 4,
QGis::Byte );
498 if ( progressDialog )
502 nParts = nPartsX * nPartsY;
503 progressDialog->setMaximum( nParts );
504 progressDialog->show();
505 progressDialog->setLabelText(
QObject::tr(
"Reading raster part %1 of %2" ).arg( fileIndex + 1 ).arg( nParts ) );
509 while ( iter->
readNextRasterPart( 1, iterCols, iterRows, &inputBlock, iterLeft, iterTop ) )
516 if ( progressDialog && fileIndex < ( nParts - 1 ) )
518 progressDialog->
setValue( fileIndex + 1 );
519 progressDialog->setLabelText(
QObject::tr(
"Reading raster part %1 of %2" ).arg( fileIndex + 2 ).arg( nParts ) );
520 QCoreApplication::processEvents( QEventLoop::AllEvents, 1000 );
521 if ( progressDialog->wasCanceled() )
535 for (
qgssize i = 0; i < nPixels; ++i )
537 QRgb c = inputBlock->
color( i );
539 red = qRed( c ); green = qGreen( c ); blue = qBlue( c );
543 double a = alpha / 255.;
544 QgsDebugMsgLevel( QString(
"red = %1 green = %2 blue = %3 alpha = %4 p = %5 a = %6" ).arg( red ).arg( green ).arg( blue ).arg( alpha ).arg((
int )c, 0, 16 ).arg( a ), 5 );
549 memcpy((
char* )redData + i, &red, 1 );
550 memcpy((
char* )greenData + i, &green, 1 );
551 memcpy((
char* )blueData + i, &blue, 1 );
552 memcpy((
char* )alphaData + i, &alpha, 1 );
561 nCols, iterCols, iterRows,
562 iterLeft, iterTop, mOutputUrl, fileIndex,
565 if ( partDestProvider )
568 partDestProvider->
write( redData, 1, iterCols, iterRows, 0, 0 );
569 partDestProvider->
write( greenData, 2, iterCols, iterRows, 0, 0 );
570 partDestProvider->
write( blueData, 3, iterCols, iterRows, 0, 0 );
571 partDestProvider->
write( alphaData, 4, iterCols, iterRows, 0, 0 );
573 addToVRT( partFileName( fileIndex ), 1, iterCols, iterRows, iterLeft, iterTop );
574 addToVRT( partFileName( fileIndex ), 2, iterCols, iterRows, iterLeft, iterTop );
575 addToVRT( partFileName( fileIndex ), 3, iterCols, iterRows, iterLeft, iterTop );
576 addToVRT( partFileName( fileIndex ), 4, iterCols, iterRows, iterLeft, iterTop );
577 delete partDestProvider;
580 else if ( destProvider )
582 destProvider->
write( redData, 1, iterCols, iterRows, iterLeft, iterTop );
583 destProvider->
write( greenData, 2, iterCols, iterRows, iterLeft, iterTop );
584 destProvider->
write( blueData, 3, iterCols, iterRows, iterLeft, iterTop );
585 destProvider->
write( alphaData, 4, iterCols, iterRows, iterLeft, iterTop );
596 if ( progressDialog )
598 progressDialog->setValue( progressDialog->maximum() );
603 QString vrtFilePath( mOutputUrl +
"/" + vrtFileName() );
604 writeVRT( vrtFilePath );
607 buildPyramids( vrtFilePath );
614 buildPyramids( mOutputUrl );
620 void QgsRasterFileWriter::addToVRT(
const QString& filename,
int band,
int xSize,
int ySize,
int xOffset,
int yOffset )
622 QDomElement bandElem = mVRTBands.value( band - 1 );
624 QDomElement simpleSourceElem = mVRTDocument.createElement(
"SimpleSource" );
627 QDomElement sourceFilenameElem = mVRTDocument.createElement(
"SourceFilename" );
628 sourceFilenameElem.setAttribute(
"relativeToVRT",
"1" );
629 QDomText sourceFilenameText = mVRTDocument.createTextNode( filename );
630 sourceFilenameElem.appendChild( sourceFilenameText );
631 simpleSourceElem.appendChild( sourceFilenameElem );
634 QDomElement sourceBandElem = mVRTDocument.createElement(
"SourceBand" );
635 QDomText sourceBandText = mVRTDocument.createTextNode( QString::number( band ) );
636 sourceBandElem.appendChild( sourceBandText );
637 simpleSourceElem.appendChild( sourceBandElem );
640 QDomElement sourcePropertiesElem = mVRTDocument.createElement(
"SourceProperties" );
641 sourcePropertiesElem.setAttribute(
"RasterXSize", xSize );
642 sourcePropertiesElem.setAttribute(
"RasterYSize", ySize );
643 sourcePropertiesElem.setAttribute(
"BlockXSize", xSize );
644 sourcePropertiesElem.setAttribute(
"BlockYSize", ySize );
645 sourcePropertiesElem.setAttribute(
"DataType",
"Byte" );
646 simpleSourceElem.appendChild( sourcePropertiesElem );
649 QDomElement srcRectElem = mVRTDocument.createElement(
"SrcRect" );
650 srcRectElem.setAttribute(
"xOff",
"0" );
651 srcRectElem.setAttribute(
"yOff",
"0" );
652 srcRectElem.setAttribute(
"xSize", xSize );
653 srcRectElem.setAttribute(
"ySize", ySize );
654 simpleSourceElem.appendChild( srcRectElem );
657 QDomElement dstRectElem = mVRTDocument.createElement(
"DstRect" );
658 dstRectElem.setAttribute(
"xOff", xOffset );
659 dstRectElem.setAttribute(
"yOff", yOffset );
660 dstRectElem.setAttribute(
"xSize", xSize );
661 dstRectElem.setAttribute(
"ySize", ySize );
662 simpleSourceElem.appendChild( dstRectElem );
664 bandElem.appendChild( simpleSourceElem );
668 void QgsRasterFileWriter::buildPyramids(
const QString& filename )
670 GDALDatasetH dataSet;
672 dataSet = GDALOpen( filename.toLocal8Bit().data(), GA_Update );
683 overviewList[3] = 16;
684 overviewList[4] = 32;
685 overviewList[5] = 64;
688 if ( mProgressDialog )
690 mProgressDialog->setLabelText(
QObject::tr(
"Building Pyramids..." ) );
691 mProgressDialog->setValue( 0 );
692 mProgressDialog->setWindowModality( Qt::WindowModal );
693 mProgressDialog->show();
696 GDALBuildOverviews( dataSet,
"AVERAGE", 6, overviewList, 0, 0, 0, 0 );
700 void QgsRasterFileWriter::buildPyramids(
const QString& filename )
713 QList< QgsRasterPyramid> myPyramidList;
714 if ( ! mPyramidsList.isEmpty() )
716 for (
int myCounterInt = 0; myCounterInt < myPyramidList.count(); myCounterInt++ )
718 myPyramidList[myCounterInt].build =
true;
721 QgsDebugMsg( QString(
"building pyramids : %1 pyramids, %2 resampling, %3 format, %4 options" ).arg( myPyramidList.count() ).arg( mPyramidsResampling ).arg( mPyramidsFormat ).arg( mPyramidsConfigOptions.count() ) );
723 QString res = destProvider->
buildPyramids( myPyramidList, mPyramidsResampling,
724 mPyramidsFormat, mPyramidsConfigOptions );
730 QString title, message;
731 if ( res ==
"ERROR_WRITE_ACCESS" )
733 title =
QObject::tr(
"Building pyramids failed - write access denied" );
734 message =
QObject::tr(
"Write access denied. Adjust the file permissions and try again." );
736 else if ( res ==
"ERROR_WRITE_FORMAT" )
738 title =
QObject::tr(
"Building pyramids failed." );
739 message =
QObject::tr(
"The file was not writable. Some formats do not "
740 "support pyramid overviews. Consult the GDAL documentation if in doubt." );
742 else if ( res ==
"FAILED_NOT_SUPPORTED" )
744 title =
QObject::tr(
"Building pyramids failed." );
745 message =
QObject::tr(
"Building pyramid overviews is not supported on this type of raster." );
747 else if ( res ==
"ERROR_JPEG_COMPRESSION" )
749 title =
QObject::tr(
"Building pyramids failed." );
750 message =
QObject::tr(
"Building internal pyramid overviews is not supported on raster layers with JPEG compression and your current libtiff library." );
752 else if ( res ==
"ERROR_VIRTUAL" )
754 title =
QObject::tr(
"Building pyramids failed." );
755 message =
QObject::tr(
"Building pyramid overviews is not supported on this type of raster." );
757 QMessageBox::warning( 0, title, message );
764 int QgsRasterFileWriter::pyramidsProgress(
double dfComplete,
const char *pszMessage,
void* pData )
766 Q_UNUSED( pszMessage );
767 GDALTermProgress( dfComplete, 0, 0 );
768 QProgressDialog* progressDialog =
static_cast<QProgressDialog*
>( pData );
769 if ( pData && progressDialog->wasCanceled() )
776 progressDialog->setRange( 0, 100 );
777 progressDialog->setValue( dfComplete * 100 );
783 void QgsRasterFileWriter::createVRT(
int xSize,
int ySize,
const QgsCoordinateReferenceSystem& crs,
double* geoTransform,
QGis::DataType type, QList<bool> destHasNoDataValueList, QList<double> destNoDataValueList )
785 mVRTDocument.clear();
786 QDomElement VRTDatasetElem = mVRTDocument.createElement(
"VRTDataset" );
789 VRTDatasetElem.setAttribute(
"rasterXSize", xSize );
790 VRTDatasetElem.setAttribute(
"rasterYSize", ySize );
791 mVRTDocument.appendChild( VRTDatasetElem );
794 QDomElement SRSElem = mVRTDocument.createElement(
"SRS" );
795 QDomText crsText = mVRTDocument.createTextNode( crs.
toWkt() );
796 SRSElem.appendChild( crsText );
797 VRTDatasetElem.appendChild( SRSElem );
802 QDomElement geoTransformElem = mVRTDocument.createElement(
"GeoTransform" );
803 QString geoTransformString = QString::number( geoTransform[0] ) +
", " + QString::number( geoTransform[1] ) +
", " + QString::number( geoTransform[2] ) +
804 ", " + QString::number( geoTransform[3] ) +
", " + QString::number( geoTransform[4] ) +
", " + QString::number( geoTransform[5] );
805 QDomText geoTransformText = mVRTDocument.createTextNode( geoTransformString );
806 geoTransformElem.appendChild( geoTransformText );
807 VRTDatasetElem.appendChild( geoTransformElem );
820 QStringList colorInterp;
821 colorInterp <<
"Red" <<
"Green" <<
"Blue" <<
"Alpha";
823 QMap<QGis::DataType, QString> dataTypes;
835 for (
int i = 1; i <= nBands; i++ )
837 QDomElement VRTBand = mVRTDocument.createElement(
"VRTRasterBand" );
839 VRTBand.setAttribute(
"band", QString::number( i ) );
840 QString dataType = dataTypes.value( type );
841 VRTBand.setAttribute(
"dataType", dataType );
843 if ( mMode ==
Image )
845 VRTBand.setAttribute(
"dataType",
"Byte" );
846 QDomElement colorInterpElement = mVRTDocument.createElement(
"ColorInterp" );
847 QDomText interpText = mVRTDocument.createTextNode( colorInterp.value( i - 1 ) );
848 colorInterpElement.appendChild( interpText );
849 VRTBand.appendChild( colorInterpElement );
852 if ( !destHasNoDataValueList.isEmpty() && destHasNoDataValueList.value( i - 1 ) )
854 VRTBand.setAttribute(
"NoDataValue", QString::number( destNoDataValueList.value( i - 1 ) ) );
857 mVRTBands.append( VRTBand );
858 VRTDatasetElem.appendChild( VRTBand );
862 bool QgsRasterFileWriter::writeVRT(
const QString&
file )
864 QFile outputFile( file );
865 if ( ! outputFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
870 QTextStream outStream( &outputFile );
871 mVRTDocument.save( outStream, 2 );
876 int iterRows,
int iterLeft,
int iterTop,
const QString& outputUrl,
int fileIndex,
int nBands,
QGis::DataType type,
879 double mup = extent.
width() / nCols;
880 double mapLeft = extent.
xMinimum() + iterLeft * mup;
881 double mapRight = mapLeft + mup * iterCols;
882 double mapTop = extent.
yMaximum() - iterTop * mup;
883 double mapBottom = mapTop - iterRows * mup;
884 QgsRectangle mapRect( mapLeft, mapBottom, mapRight, mapTop );
886 QString outputFile = outputUrl +
"/" + partFileName( fileIndex );
889 double geoTransform[6];
890 geoTransform[0] = mapRect.
xMinimum();
891 geoTransform[1] = mup;
892 geoTransform[2] = 0.0;
893 geoTransform[3] = mapRect.
yMaximum();
894 geoTransform[4] = 0.0;
895 geoTransform[5] = -mup;
907 QList<bool> destHasNoDataValueList, QList<double> destNoDataValueList )
911 createVRT( nCols, nRows, crs, geoTransform, type, destHasNoDataValueList, destNoDataValueList );
919 if ( mBuildPyramidsFlag == -4 && mOutputProviderKey ==
"gdal" && mOutputFormat.toLower() ==
"gtiff" )
920 mCreateOptions <<
"COPY_SRC_OVERVIEWS=YES";
934 void QgsRasterFileWriter::globalOutputParameters(
const QgsRectangle& extent,
int nCols,
int& nRows,
935 double* geoTransform,
double& pixelSize )
937 pixelSize = extent.
width() / nCols;
942 nRows = ( double )nCols / extent.
width() * extent.
height() + 0.5;
944 geoTransform[0] = extent.
xMinimum();
945 geoTransform[1] = pixelSize;
946 geoTransform[2] = 0.0;
947 geoTransform[3] = extent.
yMaximum();
948 geoTransform[4] = 0.0;
949 geoTransform[5] = -( extent.
height() / nRows );
952 QString QgsRasterFileWriter::partFileName(
int fileIndex )
955 QFileInfo outputInfo( mOutputUrl );
956 return QString(
"%1.%2.tif" ).arg( outputInfo.fileName() ).arg( fileIndex );
959 QString QgsRasterFileWriter::vrtFileName()
961 QFileInfo outputInfo( mOutputUrl );
962 return QString(
"%1.vrt" ).arg( outputInfo.fileName() );