|
QGIS API Documentation
master-6227475
|
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 }