QGIS API Documentation  master-3f58142
src/core/raster/qgsrasterfilewriter.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgsrasterfilewriter.cpp
00003     ---------------------
00004     begin                : July 2012
00005     copyright            : (C) 2012 by Marco Hugentobler
00006     email                : marco dot hugentobler at sourcepole dot ch
00007  ***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 #include <typeinfo>
00016 
00017 #include "qgsrasterfilewriter.h"
00018 #include "qgsproviderregistry.h"
00019 #include "qgsrasterinterface.h"
00020 #include "qgsrasteriterator.h"
00021 #include "qgsrasterlayer.h"
00022 #include "qgsrasterprojector.h"
00023 
00024 #include <QCoreApplication>
00025 #include <QProgressDialog>
00026 #include <QTextStream>
00027 #include <QMessageBox>
00028 
00029 QgsRasterFileWriter::QgsRasterFileWriter( const QString& outputUrl ):
00030     mMode( Raw ), mOutputUrl( outputUrl ), mOutputProviderKey( "gdal" ), mOutputFormat( "GTiff" ),
00031     mTiledMode( false ), mMaxTileWidth( 500 ), mMaxTileHeight( 500 ),
00032     mBuildPyramidsFlag( QgsRaster::PyramidsFlagNo ),
00033     mPyramidsFormat( QgsRaster::PyramidsGTiff ),
00034     mProgressDialog( 0 ), mPipe( 0 ), mInput( 0 )
00035 {
00036 
00037 }
00038 
00039 QgsRasterFileWriter::QgsRasterFileWriter()
00040 {
00041 
00042 }
00043 
00044 QgsRasterFileWriter::~QgsRasterFileWriter()
00045 {
00046 
00047 }
00048 
00049 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeRaster( const QgsRasterPipe* pipe, int nCols, int nRows, QgsRectangle outputExtent,
00050     const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog )
00051 {
00052   QgsDebugMsg( "Entered" );
00053 
00054   if ( !pipe )
00055   {
00056     return SourceProviderError;
00057   }
00058   mPipe = pipe;
00059 
00060   //const QgsRasterInterface* iface = iter->input();
00061   const QgsRasterInterface* iface = pipe->last();
00062   if ( !iface )
00063   {
00064     return SourceProviderError;
00065   }
00066   mInput = iface;
00067 
00068   if ( QgsRasterBlock::typeIsColor( iface->dataType( 1 ) ) )
00069   {
00070     mMode = Image;
00071   }
00072   else
00073   {
00074     mMode = Raw;
00075   }
00076 
00077   QgsDebugMsg( QString( "reading from %1" ).arg( typeid( *iface ).name() ) );
00078 
00079   if ( !iface->srcInput() )
00080   {
00081     QgsDebugMsg( "iface->srcInput() == 0" );
00082     return SourceProviderError;
00083   }
00084   QgsDebugMsg( QString( "srcInput = %1" ).arg( typeid( *( iface->srcInput() ) ).name() ) );
00085 
00086   mProgressDialog = progressDialog;
00087 
00088   QgsRasterIterator iter( pipe->last() );
00089 
00090   //create directory for output files
00091   if ( mTiledMode )
00092   {
00093     QFileInfo fileInfo( mOutputUrl );
00094     if ( !fileInfo.exists() )
00095     {
00096       QDir dir = fileInfo.dir();
00097       if ( !dir.mkdir( fileInfo.fileName() ) )
00098       {
00099         QgsDebugMsg( "Cannot create output VRT directory " + fileInfo.fileName() + " in " + dir.absolutePath() );
00100         return CreateDatasourceError;
00101       }
00102     }
00103   }
00104 
00105   if ( mMode == Image )
00106   {
00107     WriterError e = writeImageRaster( &iter, nCols, nRows, outputExtent, crs, progressDialog );
00108     mProgressDialog = 0;
00109     return e;
00110   }
00111   else
00112   {
00113     mProgressDialog = 0;
00114     WriterError e = writeDataRaster( pipe, &iter, nCols, nRows, outputExtent, crs, progressDialog );
00115     return e;
00116   }
00117 }
00118 
00119 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const QgsRasterPipe* pipe, QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent,
00120     const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog )
00121 {
00122   QgsDebugMsg( "Entered" );
00123   if ( !iter )
00124   {
00125     return SourceProviderError;
00126   }
00127 
00128   const QgsRasterInterface* iface = pipe->last();
00129   if ( !iface )
00130   {
00131     return SourceProviderError;
00132   }
00133 
00134   QgsRasterDataProvider* srcProvider = const_cast<QgsRasterDataProvider*>( dynamic_cast<const QgsRasterDataProvider*>( iface->srcInput() ) );
00135   if ( !srcProvider )
00136   {
00137     QgsDebugMsg( "Cannot get source data provider" );
00138     return SourceProviderError;
00139   }
00140 
00141   iter->setMaximumTileWidth( mMaxTileWidth );
00142   iter->setMaximumTileHeight( mMaxTileHeight );
00143 
00144   int nBands = iface->bandCount();
00145   if ( nBands < 1 )
00146   {
00147     return SourceProviderError;
00148   }
00149 
00150 
00151   //check if all the bands have the same data type size, otherwise we cannot write it to the provider
00152   //(at least not with the current interface)
00153   int dataTypeSize = QgsRasterBlock::typeSize( srcProvider->srcDataType( 1 ) );
00154   for ( int i = 2; i <= nBands; ++i )
00155   {
00156     if ( QgsRasterBlock::typeSize( srcProvider->srcDataType( 1 ) ) != dataTypeSize )
00157     {
00158       return DestProviderError;
00159     }
00160   }
00161 
00162   // Output data type - source data type is preferred but it may happen that we need
00163   // to set 'no data' value (which was not set on source data) if output extent
00164   // is larger than source extent (with or without reprojection) and there is no 'free'
00165   // (not used) value available
00166   QList<bool> destHasNoDataValueList;
00167   QList<double> destNoDataValueList;
00168   QList<QGis::DataType> destDataTypeList;
00169   for ( int bandNo = 1; bandNo <= nBands; bandNo++ )
00170   {
00171     QgsRasterNuller *nuller = pipe->nuller();
00172 
00173     bool srcHasNoDataValue = srcProvider->srcHasNoDataValue( bandNo );
00174     bool destHasNoDataValue = false;
00175     double destNoDataValue = std::numeric_limits<double>::quiet_NaN();
00176     QGis::DataType destDataType = srcProvider->srcDataType( bandNo );
00177     // TODO: verify what happens/should happen if srcNoDataValue is disabled by setUseSrcNoDataValue
00178     QgsDebugMsg( QString( "srcHasNoDataValue = %1 srcNoDataValue = %2" ).arg( srcHasNoDataValue ).arg( srcProvider->srcNoDataValue( bandNo ) ) );
00179     if ( srcHasNoDataValue )
00180     {
00181 
00182       // If source has no data value, it is used by provider
00183       destNoDataValue = srcProvider->srcNoDataValue( bandNo );
00184       destHasNoDataValue = true;
00185     }
00186     else if ( nuller && nuller->noData( bandNo ).size() > 0 )
00187     {
00188       // Use one user defined no data value
00189       destNoDataValue = nuller->noData( bandNo ).value( 0 ).min();
00190       destHasNoDataValue = true;
00191     }
00192     else
00193     {
00194       // Verify if we realy need no data value, i.e.
00195       QgsRectangle srcExtent = outputExtent;
00196       QgsRasterProjector *projector = pipe->projector();
00197       if ( projector && projector->destCrs() != projector->srcCrs() )
00198       {
00199         QgsCoordinateTransform ct( projector->destCrs(), projector->srcCrs() );
00200         srcExtent = ct.transformBoundingBox( outputExtent );
00201       }
00202       if ( !srcProvider->extent().contains( srcExtent ) )
00203       {
00204         // Destination extent is larger than source extent, we need destination no data values
00205         // Get src sample statistics (estimation from sample)
00206         QgsRasterBandStats stats = srcProvider->bandStatistics( bandNo, QgsRasterBandStats::Min | QgsRasterBandStats::Max, srcExtent, 250000 );
00207 
00208         // Test if we have free (not used) values
00209         double typeMinValue = QgsContrastEnhancement::maximumValuePossible(( QGis::DataType )srcProvider->srcDataType( bandNo ) );
00210         double typeMaxValue = QgsContrastEnhancement::maximumValuePossible(( QGis::DataType )srcProvider->srcDataType( bandNo ) );
00211         if ( stats.minimumValue > typeMinValue )
00212         {
00213           destNoDataValue = typeMinValue;
00214         }
00215         else if ( stats.maximumValue < typeMaxValue )
00216         {
00217           destNoDataValue = typeMaxValue;
00218         }
00219         else
00220         {
00221           // We have to use wider type
00222           destDataType = QgsRasterBlock::typeWithNoDataValue( destDataType, &destNoDataValue );
00223         }
00224         destHasNoDataValue = true;
00225       }
00226     }
00227 
00228     if ( nuller && destHasNoDataValue )
00229     {
00230       nuller->setOutputNoDataValue( bandNo, destNoDataValue );
00231     }
00232 
00233     QgsDebugMsg( QString( "bandNo = %1 destDataType = %2 destHasNoDataValue = %3 destNoDataValue = %4" ).arg( bandNo ).arg( destDataType ).arg( destHasNoDataValue ).arg( destNoDataValue ) );
00234     destDataTypeList.append( destDataType );
00235     destHasNoDataValueList.append( destHasNoDataValue );
00236     destNoDataValueList.append( destNoDataValue );
00237   }
00238 
00239   QGis::DataType destDataType = destDataTypeList.value( 0 );
00240   // Currently write API supports one output type for dataset only -> find the widest
00241   for ( int i = 1; i < nBands; i++ )
00242   {
00243     if ( destDataTypeList.value( i ) > destDataType )
00244     {
00245       destDataType = destDataTypeList.value( i );
00246       // no data value may be left per band (for future)
00247     }
00248   }
00249 
00250   //create destProvider for whole dataset here
00251   QgsRasterDataProvider* destProvider = 0;
00252   double pixelSize;
00253   double geoTransform[6];
00254   globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize );
00255 
00256   // initOutput() returns 0 in tile mode!
00257   destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands,  destDataType, destHasNoDataValueList, destNoDataValueList );
00258 
00259   WriterError error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog );
00260 
00261   if ( error == NoDataConflict )
00262   {
00263     // The value used for no data was found in source data, we must use wider data type
00264     if ( destProvider ) // no tiles
00265     {
00266       destProvider->remove();
00267       delete destProvider;
00268       destProvider = 0;
00269     }
00270     else // VRT
00271     {
00272       // TODO: remove created VRT
00273     }
00274 
00275     // But we don't know which band -> wider all
00276     for ( int i = 0; i < nBands; i++ )
00277     {
00278       double destNoDataValue;
00279       QGis::DataType destDataType = QgsRasterBlock::typeWithNoDataValue( destDataTypeList.value( i ), &destNoDataValue );
00280       destDataTypeList.replace( i, destDataType );
00281       destNoDataValueList.replace( i, destNoDataValue );
00282     }
00283     destDataType =  destDataTypeList.value( 0 );
00284 
00285     // Try again
00286     destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands,  destDataType , destHasNoDataValueList, destNoDataValueList );
00287     error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog );
00288   }
00289 
00290   if ( destProvider )
00291     delete destProvider;
00292 
00293   return error;
00294 }
00295 
00296 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster(
00297   const QgsRasterPipe* pipe,
00298   QgsRasterIterator* iter,
00299   int nCols, int nRows,
00300   const QgsRectangle& outputExtent,
00301   const QgsCoordinateReferenceSystem& crs,
00302   QGis::DataType destDataType,
00303   QList<bool> destHasNoDataValueList,
00304   QList<double> destNoDataValueList,
00305   QgsRasterDataProvider* destProvider,
00306   QProgressDialog* progressDialog )
00307 {
00308   Q_UNUSED( pipe );
00309   Q_UNUSED( destHasNoDataValueList );
00310   QgsDebugMsg( "Entered" );
00311 
00312   const QgsRasterInterface* iface = iter->input();
00313   const QgsRasterDataProvider* srcProvider = dynamic_cast<const QgsRasterDataProvider*>( iface->srcInput() );
00314   int nBands = iface->bandCount();
00315   QgsDebugMsg( QString( "nBands = %1" ).arg( nBands ) );
00316 
00317   //Get output map units per pixel
00318   int iterLeft = 0;
00319   int iterTop = 0;
00320   int iterCols = 0;
00321   int iterRows = 0;
00322 
00323   QList<QgsRasterBlock*> blockList;
00324   for ( int i = 1; i <= nBands; ++i )
00325   {
00326     iter->startRasterRead( i, nCols, nRows, outputExtent );
00327     blockList.push_back( 0 );
00328     if ( destProvider ) // no tiles
00329     {
00330       destProvider->setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
00331     }
00332   }
00333 
00334   int nParts = 0;
00335   int fileIndex = 0;
00336   if ( progressDialog )
00337   {
00338     int nPartsX = nCols / iter->maximumTileWidth() + 1;
00339     int nPartsY = nRows / iter->maximumTileHeight() + 1;
00340     nParts = nPartsX * nPartsY;
00341     progressDialog->setMaximum( nParts );
00342     progressDialog->show();
00343     progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 1 ).arg( nParts ) );
00344   }
00345 
00346   // hmm why is there a for(;;) here ..
00347   // not good coding practice IMHO, it might be better to use [ for() and break ] or  [ while (test) ]
00348   for ( ;; )
00349   {
00350     for ( int i = 1; i <= nBands; ++i )
00351     {
00352       if ( !iter->readNextRasterPart( i, iterCols, iterRows, &( blockList[i - 1] ), iterLeft, iterTop ) )
00353       {
00354         // No more parts, create VRT and return
00355         if ( mTiledMode )
00356         {
00357           QString vrtFilePath( mOutputUrl + "/" + vrtFileName() );
00358           writeVRT( vrtFilePath );
00359           if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
00360           {
00361             buildPyramids( vrtFilePath );
00362           }
00363         }
00364         else
00365         {
00366           if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
00367           {
00368             buildPyramids( mOutputUrl );
00369           }
00370         }
00371 
00372         QgsDebugMsg( "Done" );
00373         return NoError; //reached last tile, bail out
00374       }
00375       // TODO: verify if NoDataConflict happened, to do that we need the whole pipe or nuller interface
00376     }
00377 
00378     if ( progressDialog && fileIndex < ( nParts - 1 ) )
00379     {
00380       progressDialog->setValue( fileIndex + 1 );
00381       progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 2 ).arg( nParts ) );
00382       QCoreApplication::processEvents( QEventLoop::AllEvents, 1000 );
00383       if ( progressDialog->wasCanceled() )
00384       {
00385         for ( int i = 0; i < nBands; ++i )
00386         {
00387           delete blockList[i];
00388         }
00389         break;
00390       }
00391     }
00392 
00393     // It may happen that internal data type (dataType) is wider than destDataType
00394     QList<QgsRasterBlock*> destBlockList;
00395     for ( int i = 1; i <= nBands; ++i )
00396     {
00397       if ( srcProvider->dataType( i ) == destDataType )
00398       {
00399         destBlockList.push_back( blockList[i-1] );
00400       }
00401       else
00402       {
00403         // TODO: this conversion should go to QgsRasterDataProvider::write with additional input data type param
00404         blockList[i-1]->convert( destDataType );
00405         destBlockList.push_back( blockList[i-1] );
00406       }
00407       blockList[i-1] = 0;
00408     }
00409 
00410     if ( mTiledMode ) //write to file
00411     {
00412       QgsRasterDataProvider* partDestProvider = createPartProvider( outputExtent,
00413           nCols, iterCols, iterRows,
00414           iterLeft, iterTop, mOutputUrl,
00415           fileIndex, nBands, destDataType, crs );
00416 
00417       if ( partDestProvider )
00418       {
00419         //write data to output file. todo: loop over the data list
00420         for ( int i = 1; i <= nBands; ++i )
00421         {
00422           partDestProvider->setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
00423           partDestProvider->write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, 0, 0 );
00424           delete destBlockList[i - 1];
00425           addToVRT( partFileName( fileIndex ), i, iterCols, iterRows, iterLeft, iterTop );
00426         }
00427         delete partDestProvider;
00428       }
00429     }
00430     else if ( destProvider )
00431     {
00432       //loop over data
00433       for ( int i = 1; i <= nBands; ++i )
00434       {
00435         destProvider->write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, iterLeft, iterTop );
00436         delete destBlockList[i - 1];
00437       }
00438     }
00439     ++fileIndex;
00440   }
00441 
00442   QgsDebugMsg( "Done" );
00443   return NoError;
00444 }
00445 
00446 QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent,
00447     const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog )
00448 {
00449   QgsDebugMsg( "Entered" );
00450   if ( !iter )
00451   {
00452     return SourceProviderError;
00453   }
00454 
00455   const QgsRasterInterface* iface = iter->input();
00456   QGis::DataType inputDataType = iface->dataType( 1 );
00457   if ( !iface || ( inputDataType != QGis::ARGB32 &&
00458                    inputDataType != QGis::ARGB32_Premultiplied ) )
00459   {
00460     return SourceProviderError;
00461   }
00462 
00463   iter->setMaximumTileWidth( mMaxTileWidth );
00464   iter->setMaximumTileHeight( mMaxTileHeight );
00465 
00466   void* redData = qgsMalloc( mMaxTileWidth * mMaxTileHeight );
00467   void* greenData = qgsMalloc( mMaxTileWidth * mMaxTileHeight );
00468   void* blueData = qgsMalloc( mMaxTileWidth * mMaxTileHeight );
00469   void* alphaData = qgsMalloc( mMaxTileWidth * mMaxTileHeight );
00470   QgsRectangle mapRect;
00471   int iterLeft = 0, iterTop = 0, iterCols = 0, iterRows = 0;
00472   int fileIndex = 0;
00473 
00474   //create destProvider for whole dataset here
00475   QgsRasterDataProvider* destProvider = 0;
00476   double pixelSize;
00477   double geoTransform[6];
00478   globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize );
00479 
00480   destProvider = initOutput( nCols, nRows, crs, geoTransform, 4, QGis::Byte );
00481 
00482   iter->startRasterRead( 1, nCols, nRows, outputExtent );
00483 
00484   int nParts = 0;
00485   if ( progressDialog )
00486   {
00487     int nPartsX = nCols / iter->maximumTileWidth() + 1;
00488     int nPartsY = nRows / iter->maximumTileHeight() + 1;
00489     nParts = nPartsX * nPartsY;
00490     progressDialog->setMaximum( nParts );
00491     progressDialog->show();
00492     progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 1 ).arg( nParts ) );
00493   }
00494 
00495   QgsRasterBlock *inputBlock = 0;
00496   while ( iter->readNextRasterPart( 1, iterCols, iterRows, &inputBlock, iterLeft, iterTop ) )
00497   {
00498     if ( iterCols <= 5 || iterRows <= 5 ) //some wms servers don't like small values
00499     {
00500       delete &inputBlock;
00501       continue;
00502     }
00503 
00504     if ( progressDialog && fileIndex < ( nParts - 1 ) )
00505     {
00506       progressDialog->setValue( fileIndex + 1 );
00507       progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 2 ).arg( nParts ) );
00508       QCoreApplication::processEvents( QEventLoop::AllEvents, 1000 );
00509       if ( progressDialog->wasCanceled() )
00510       {
00511         delete inputBlock;
00512         break;
00513       }
00514     }
00515 
00516     //fill into red/green/blue/alpha channels
00517     size_t nPixels = ( size_t )iterCols * iterRows;
00518     // TODO: should be char not int? we are then copying 1 byte
00519     int red = 0;
00520     int green = 0;
00521     int blue = 0;
00522     int alpha = 255;
00523     for ( size_t i = 0; i < nPixels; ++i )
00524     {
00525       QRgb c = inputBlock->color( i );
00526       alpha = qAlpha( c );
00527       red = qRed( c ); green = qGreen( c ); blue = qBlue( c );
00528 
00529       if ( inputDataType == QGis::ARGB32_Premultiplied )
00530       {
00531         double a = alpha / 255.;
00532         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 );
00533         red /= a;
00534         green /= a;
00535         blue /= a;
00536       }
00537       memcpy(( char* )redData + i, &red, 1 );
00538       memcpy(( char* )greenData + i, &green, 1 );
00539       memcpy(( char* )blueData + i, &blue, 1 );
00540       memcpy(( char* )alphaData + i, &alpha, 1 );
00541     }
00542     delete inputBlock;
00543 
00544     //create output file
00545     if ( mTiledMode )
00546     {
00547       //delete destProvider;
00548       QgsRasterDataProvider* partDestProvider = createPartProvider( outputExtent,
00549           nCols, iterCols, iterRows,
00550           iterLeft, iterTop, mOutputUrl, fileIndex,
00551           4, QGis::Byte, crs );
00552 
00553       if ( partDestProvider )
00554       {
00555         //write data to output file
00556         partDestProvider->write( redData, 1, iterCols, iterRows, 0, 0 );
00557         partDestProvider->write( greenData, 2, iterCols, iterRows, 0, 0 );
00558         partDestProvider->write( blueData, 3, iterCols, iterRows, 0, 0 );
00559         partDestProvider->write( alphaData, 4, iterCols, iterRows, 0, 0 );
00560 
00561         addToVRT( partFileName( fileIndex ), 1, iterCols, iterRows, iterLeft, iterTop );
00562         addToVRT( partFileName( fileIndex ), 2, iterCols, iterRows, iterLeft, iterTop );
00563         addToVRT( partFileName( fileIndex ), 3, iterCols, iterRows, iterLeft, iterTop );
00564         addToVRT( partFileName( fileIndex ), 4, iterCols, iterRows, iterLeft, iterTop );
00565         delete partDestProvider;
00566       }
00567     }
00568     else if ( destProvider )
00569     {
00570       destProvider->write( redData, 1, iterCols, iterRows, iterLeft, iterTop );
00571       destProvider->write( greenData, 2, iterCols, iterRows, iterLeft, iterTop );
00572       destProvider->write( blueData, 3, iterCols, iterRows, iterLeft, iterTop );
00573       destProvider->write( alphaData, 4, iterCols, iterRows, iterLeft, iterTop );
00574     }
00575 
00576     ++fileIndex;
00577   }
00578 
00579   if ( destProvider )
00580     delete destProvider;
00581 
00582   qgsFree( redData ); qgsFree( greenData ); qgsFree( blueData ); qgsFree( alphaData );
00583 
00584   if ( progressDialog )
00585   {
00586     progressDialog->setValue( progressDialog->maximum() );
00587   }
00588 
00589   if ( mTiledMode )
00590   {
00591     QString vrtFilePath( mOutputUrl + "/" + vrtFileName() );
00592     writeVRT( vrtFilePath );
00593     if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
00594     {
00595       buildPyramids( vrtFilePath );
00596     }
00597   }
00598   else
00599   {
00600     if ( mBuildPyramidsFlag == QgsRaster::PyramidsFlagYes )
00601     {
00602       buildPyramids( mOutputUrl );
00603     }
00604   }
00605   return NoError;
00606 }
00607 
00608 void QgsRasterFileWriter::addToVRT( const QString& filename, int band, int xSize, int ySize, int xOffset, int yOffset )
00609 {
00610   QDomElement bandElem = mVRTBands.value( band - 1 );
00611 
00612   QDomElement simpleSourceElem = mVRTDocument.createElement( "SimpleSource" );
00613 
00614   //SourceFilename
00615   QDomElement sourceFilenameElem = mVRTDocument.createElement( "SourceFilename" );
00616   sourceFilenameElem.setAttribute( "relativeToVRT", "1" );
00617   QDomText sourceFilenameText = mVRTDocument.createTextNode( filename );
00618   sourceFilenameElem.appendChild( sourceFilenameText );
00619   simpleSourceElem.appendChild( sourceFilenameElem );
00620 
00621   //SourceBand
00622   QDomElement sourceBandElem = mVRTDocument.createElement( "SourceBand" );
00623   QDomText sourceBandText = mVRTDocument.createTextNode( QString::number( band ) );
00624   sourceBandElem.appendChild( sourceBandText );
00625   simpleSourceElem.appendChild( sourceBandElem );
00626 
00627   //SourceProperties
00628   QDomElement sourcePropertiesElem = mVRTDocument.createElement( "SourceProperties" );
00629   sourcePropertiesElem.setAttribute( "RasterXSize", xSize );
00630   sourcePropertiesElem.setAttribute( "RasterYSize", ySize );
00631   sourcePropertiesElem.setAttribute( "BlockXSize", xSize );
00632   sourcePropertiesElem.setAttribute( "BlockYSize", ySize );
00633   sourcePropertiesElem.setAttribute( "DataType", "Byte" );
00634   simpleSourceElem.appendChild( sourcePropertiesElem );
00635 
00636   //SrcRect
00637   QDomElement srcRectElem = mVRTDocument.createElement( "SrcRect" );
00638   srcRectElem.setAttribute( "xOff", "0" );
00639   srcRectElem.setAttribute( "yOff", "0" );
00640   srcRectElem.setAttribute( "xSize", xSize );
00641   srcRectElem.setAttribute( "ySize", ySize );
00642   simpleSourceElem.appendChild( srcRectElem );
00643 
00644   //DstRect
00645   QDomElement dstRectElem = mVRTDocument.createElement( "DstRect" );
00646   dstRectElem.setAttribute( "xOff", xOffset );
00647   dstRectElem.setAttribute( "yOff", yOffset );
00648   dstRectElem.setAttribute( "xSize", xSize );
00649   dstRectElem.setAttribute( "ySize", ySize );
00650   simpleSourceElem.appendChild( dstRectElem );
00651 
00652   bandElem.appendChild( simpleSourceElem );
00653 }
00654 
00655 #if 0
00656 void QgsRasterFileWriter::buildPyramids( const QString& filename )
00657 {
00658   GDALDatasetH dataSet;
00659   GDALAllRegister();
00660   dataSet = GDALOpen( filename.toLocal8Bit().data(), GA_Update );
00661   if ( !dataSet )
00662   {
00663     return;
00664   }
00665 
00666   //2,4,8,16,32,64
00667   int overviewList[6];
00668   overviewList[0] = 2;
00669   overviewList[1] = 4;
00670   overviewList[2] = 8;
00671   overviewList[3] = 16;
00672   overviewList[4] = 32;
00673   overviewList[5] = 64;
00674 
00675 #if 0
00676   if ( mProgressDialog )
00677   {
00678     mProgressDialog->setLabelText( QObject::tr( "Building Pyramids..." ) );
00679     mProgressDialog->setValue( 0 );
00680     mProgressDialog->setWindowModality( Qt::WindowModal );
00681     mProgressDialog->show();
00682   }
00683 #endif
00684   GDALBuildOverviews( dataSet, "AVERAGE", 6, overviewList, 0, 0, /*pyramidsProgress*/ 0, /*mProgressDialog*/ 0 );
00685 }
00686 #endif
00687 
00688 void QgsRasterFileWriter::buildPyramids( const QString& filename )
00689 {
00690   QgsDebugMsg( "filename = " + filename );
00691   // open new dataProvider so we can build pyramids with it
00692   QgsRasterDataProvider* destProvider = ( QgsRasterDataProvider* ) QgsProviderRegistry::instance()->provider( mOutputProviderKey, filename );
00693   if ( !destProvider )
00694   {
00695     return;
00696   }
00697 
00698   // TODO progress report
00699   // TODO test mTiledMode - not tested b/c segfault at line # 289
00700   // connect( provider, SIGNAL( progressUpdate( int ) ), mPyramidProgress, SLOT( setValue( int ) ) );
00701   QList< QgsRasterPyramid> myPyramidList;
00702   if ( ! mPyramidsList.isEmpty() )
00703     myPyramidList = destProvider->buildPyramidList( mPyramidsList );
00704   for ( int myCounterInt = 0; myCounterInt < myPyramidList.count(); myCounterInt++ )
00705   {
00706     myPyramidList[myCounterInt].build = true;
00707   }
00708 
00709   QgsDebugMsg( QString( "building pyramids : %1 pyramids, %2 resampling, %3 format, %4 options" ).arg( myPyramidList.count() ).arg( mPyramidsResampling ).arg( mPyramidsFormat ).arg( mPyramidsConfigOptions.count() ) );
00710   // QApplication::setOverrideCursor( Qt::WaitCursor );
00711   QString res = destProvider->buildPyramids( myPyramidList, mPyramidsResampling,
00712                 mPyramidsFormat, mPyramidsConfigOptions );
00713   // QApplication::restoreOverrideCursor();
00714 
00715   // TODO put this in provider or elsewhere
00716   if ( !res.isNull() )
00717   {
00718     QString title, message;
00719     if ( res == "ERROR_WRITE_ACCESS" )
00720     {
00721       title = QObject::tr( "Building pyramids failed - write access denied" );
00722       message = QObject::tr( "Write access denied. Adjust the file permissions and try again." );
00723     }
00724     else if ( res == "ERROR_WRITE_FORMAT" )
00725     {
00726       title = QObject::tr( "Building pyramids failed." );
00727       message = QObject::tr( "The file was not writable. Some formats do not "
00728                              "support pyramid overviews. Consult the GDAL documentation if in doubt." );
00729     }
00730     else if ( res == "FAILED_NOT_SUPPORTED" )
00731     {
00732       title = QObject::tr( "Building pyramids failed." );
00733       message = QObject::tr( "Building pyramid overviews is not supported on this type of raster." );
00734     }
00735     else if ( res == "ERROR_JPEG_COMPRESSION" )
00736     {
00737       title = QObject::tr( "Building pyramids failed." );
00738       message = QObject::tr( "Building internal pyramid overviews is not supported on raster layers with JPEG compression and your current libtiff library." );
00739     }
00740     else if ( res == "ERROR_VIRTUAL" )
00741     {
00742       title = QObject::tr( "Building pyramids failed." );
00743       message = QObject::tr( "Building pyramid overviews is not supported on this type of raster." );
00744     }
00745     QMessageBox::warning( 0, title, message );
00746     QgsDebugMsg( res + " - " + message );
00747   }
00748   delete destProvider;
00749 }
00750 
00751 #if 0
00752 int QgsRasterFileWriter::pyramidsProgress( double dfComplete, const char *pszMessage, void* pData )
00753 {
00754   Q_UNUSED( pszMessage );
00755   GDALTermProgress( dfComplete, 0, 0 );
00756   QProgressDialog* progressDialog = static_cast<QProgressDialog*>( pData );
00757   if ( pData && progressDialog->wasCanceled() )
00758   {
00759     return 0;
00760   }
00761 
00762   if ( pData )
00763   {
00764     progressDialog->setRange( 0, 100 );
00765     progressDialog->setValue( dfComplete * 100 );
00766   }
00767   return 1;
00768 }
00769 #endif
00770 
00771 void QgsRasterFileWriter::createVRT( int xSize, int ySize, const QgsCoordinateReferenceSystem& crs, double* geoTransform, QGis::DataType type, QList<bool> destHasNoDataValueList, QList<double> destNoDataValueList )
00772 {
00773   mVRTDocument.clear();
00774   QDomElement VRTDatasetElem = mVRTDocument.createElement( "VRTDataset" );
00775 
00776   //xsize / ysize
00777   VRTDatasetElem.setAttribute( "rasterXSize", xSize );
00778   VRTDatasetElem.setAttribute( "rasterYSize", ySize );
00779   mVRTDocument.appendChild( VRTDatasetElem );
00780 
00781   //CRS
00782   QDomElement SRSElem = mVRTDocument.createElement( "SRS" );
00783   QDomText crsText = mVRTDocument.createTextNode( crs.toWkt() );
00784   SRSElem.appendChild( crsText );
00785   VRTDatasetElem.appendChild( SRSElem );
00786 
00787   //geotransform
00788   if ( geoTransform )
00789   {
00790     QDomElement geoTransformElem = mVRTDocument.createElement( "GeoTransform" );
00791     QString geoTransformString = QString::number( geoTransform[0] ) + ", " + QString::number( geoTransform[1] ) + ", " + QString::number( geoTransform[2] ) +
00792                                  ", "  + QString::number( geoTransform[3] ) + ", " + QString::number( geoTransform[4] ) + ", " + QString::number( geoTransform[5] );
00793     QDomText geoTransformText = mVRTDocument.createTextNode( geoTransformString );
00794     geoTransformElem.appendChild( geoTransformText );
00795     VRTDatasetElem.appendChild( geoTransformElem );
00796   }
00797 
00798   int nBands;
00799   if ( mMode == Raw )
00800   {
00801     nBands = mInput->bandCount();
00802   }
00803   else
00804   {
00805     nBands = 4;
00806   }
00807 
00808   QStringList colorInterp;
00809   colorInterp << "Red" << "Green" << "Blue" << "Alpha";
00810 
00811   QMap<QGis::DataType, QString> dataTypes;
00812   dataTypes.insert( QGis::Byte, "Byte" );
00813   dataTypes.insert( QGis::UInt16, "UInt16" );
00814   dataTypes.insert( QGis::Int16, "Int16" );
00815   dataTypes.insert( QGis::UInt32, "Int32" );
00816   dataTypes.insert( QGis::Float32, "Float32" );
00817   dataTypes.insert( QGis::Float64, "Float64" );
00818   dataTypes.insert( QGis::CInt16, "CInt16" );
00819   dataTypes.insert( QGis::CInt32, "CInt32" );
00820   dataTypes.insert( QGis::CFloat32, "CFloat32" );
00821   dataTypes.insert( QGis::CFloat64, "CFloat64" );
00822 
00823   for ( int i = 1; i <= nBands; i++ )
00824   {
00825     QDomElement VRTBand = mVRTDocument.createElement( "VRTRasterBand" );
00826 
00827     VRTBand.setAttribute( "band", QString::number( i ) );
00828     QString dataType = dataTypes.value( type );
00829     VRTBand.setAttribute( "dataType", dataType );
00830 
00831     if ( mMode == Image )
00832     {
00833       VRTBand.setAttribute( "dataType", "Byte" );
00834       QDomElement colorInterpElement = mVRTDocument.createElement( "ColorInterp" );
00835       QDomText interpText = mVRTDocument.createTextNode( colorInterp.value( i - 1 ) );
00836       colorInterpElement.appendChild( interpText );
00837       VRTBand.appendChild( colorInterpElement );
00838     }
00839 
00840     if ( !destHasNoDataValueList.isEmpty() && destHasNoDataValueList.value( i - 1 ) )
00841     {
00842       VRTBand.setAttribute( "NoDataValue", QString::number( destNoDataValueList.value( i - 1 ) ) );
00843     }
00844 
00845     mVRTBands.append( VRTBand );
00846     VRTDatasetElem.appendChild( VRTBand );
00847   }
00848 }
00849 
00850 bool QgsRasterFileWriter::writeVRT( const QString& file )
00851 {
00852   QFile outputFile( file );
00853   if ( ! outputFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
00854   {
00855     return false;
00856   }
00857 
00858   QTextStream outStream( &outputFile );
00859   mVRTDocument.save( outStream, 2 );
00860   return true;
00861 }
00862 
00863 QgsRasterDataProvider* QgsRasterFileWriter::createPartProvider( const QgsRectangle& extent, int nCols, int iterCols,
00864     int iterRows, int iterLeft, int iterTop, const QString& outputUrl, int fileIndex, int nBands, QGis::DataType type,
00865     const QgsCoordinateReferenceSystem& crs )
00866 {
00867   double mup = extent.width() / nCols;
00868   double mapLeft = extent.xMinimum() + iterLeft * mup;
00869   double mapRight = mapLeft + mup * iterCols;
00870   double mapTop = extent.yMaximum() - iterTop * mup;
00871   double mapBottom = mapTop - iterRows * mup;
00872   QgsRectangle mapRect( mapLeft, mapBottom, mapRight, mapTop );
00873 
00874   QString outputFile = outputUrl + "/" + partFileName( fileIndex );
00875 
00876   //geotransform
00877   double geoTransform[6];
00878   geoTransform[0] = mapRect.xMinimum();
00879   geoTransform[1] = mup;
00880   geoTransform[2] = 0.0;
00881   geoTransform[3] = mapRect.yMaximum();
00882   geoTransform[4] = 0.0;
00883   geoTransform[5] = -mup;
00884 
00885   // perhaps we need a separate createOptions for tiles ?
00886 
00887   QgsRasterDataProvider* destProvider = QgsRasterDataProvider::create( mOutputProviderKey, outputFile, mOutputFormat, nBands, type, iterCols, iterRows, geoTransform, crs, mCreateOptions ) ;
00888 
00889   // TODO: return provider and report error
00890   return destProvider;
00891 }
00892 
00893 QgsRasterDataProvider* QgsRasterFileWriter::initOutput( int nCols, int nRows, const QgsCoordinateReferenceSystem& crs,
00894     double* geoTransform, int nBands, QGis::DataType type,
00895     QList<bool> destHasNoDataValueList, QList<double> destNoDataValueList )
00896 {
00897   if ( mTiledMode )
00898   {
00899     createVRT( nCols, nRows, crs, geoTransform, type, destHasNoDataValueList, destNoDataValueList );
00900     return 0;
00901   }
00902   else
00903   {
00904 #if 0
00905     // TODO enable "use existing", has no effect for now, because using Create() in gdal provider
00906     // should this belong in provider? should also test that source provider is gdal
00907     if ( mBuildPyramidsFlag == -4 && mOutputProviderKey == "gdal" && mOutputFormat.toLower() == "gtiff" )
00908       mCreateOptions << "COPY_SRC_OVERVIEWS=YES";
00909 #endif
00910 
00911     QgsRasterDataProvider* destProvider = QgsRasterDataProvider::create( mOutputProviderKey, mOutputUrl, mOutputFormat, nBands, type, nCols, nRows, geoTransform, crs, mCreateOptions ) ;
00912 
00913     if ( !destProvider )
00914     {
00915       QgsDebugMsg( "No provider created" );
00916     }
00917 
00918     return destProvider;
00919   }
00920 }
00921 
00922 void QgsRasterFileWriter::globalOutputParameters( const QgsRectangle& extent, int nCols, int& nRows,
00923     double* geoTransform, double& pixelSize )
00924 {
00925   pixelSize = extent.width() / nCols;
00926 
00927   //calculate nRows automatically for providers without exact resolution
00928   if ( nRows < 0 )
00929   {
00930     nRows = ( double )nCols / extent.width() * extent.height() + 0.5;
00931   }
00932   geoTransform[0] = extent.xMinimum();
00933   geoTransform[1] = pixelSize;
00934   geoTransform[2] = 0.0;
00935   geoTransform[3] = extent.yMaximum();
00936   geoTransform[4] = 0.0;
00937   geoTransform[5] = -( extent.height() / nRows );
00938 }
00939 
00940 QString QgsRasterFileWriter::partFileName( int fileIndex )
00941 {
00942   // .tif for now
00943   QFileInfo outputInfo( mOutputUrl );
00944   return QString( "%1.%2.tif" ).arg( outputInfo.fileName() ).arg( fileIndex );
00945 }
00946 
00947 QString QgsRasterFileWriter::vrtFileName()
00948 {
00949   QFileInfo outputInfo( mOutputUrl );
00950   return QString( "%1.vrt" ).arg( outputInfo.fileName() );
00951 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines