QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( const QgsRasterPipe* pipe, QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent, const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog ) { QgsDebugMsg( "Entered" ); if ( !iter ) { return SourceProviderError; } const QgsRasterInterface* iface = pipe->last(); if ( !iface ) { return SourceProviderError; } QgsRasterDataProvider* srcProvider = const_cast<QgsRasterDataProvider*>( dynamic_cast<const QgsRasterDataProvider*>( iface->srcInput() ) ); if ( !srcProvider ) { QgsDebugMsg( "Cannot get source data provider" ); return SourceProviderError; } iter->setMaximumTileWidth( mMaxTileWidth ); iter->setMaximumTileHeight( mMaxTileHeight ); int nBands = iface->bandCount(); if ( nBands < 1 ) { return SourceProviderError; } //check if all the bands have the same data type size, otherwise we cannot write it to the provider //(at least not with the current interface) int dataTypeSize = QgsRasterBlock::typeSize( srcProvider->srcDataType( 1 ) ); for ( int i = 2; i <= nBands; ++i ) { if ( QgsRasterBlock::typeSize( srcProvider->srcDataType( 1 ) ) != dataTypeSize ) { return DestProviderError; } } // Output data type - source data type is preferred but it may happen that we need // to set 'no data' value (which was not set on source data) if output extent // is larger than source extent (with or without reprojection) and there is no 'free' // (not used) value available QList<bool> destHasNoDataValueList; QList<double> destNoDataValueList; QList<QGis::DataType> destDataTypeList; for ( int bandNo = 1; bandNo <= nBands; bandNo++ ) { QgsRasterNuller *nuller = pipe->nuller(); bool srcHasNoDataValue = srcProvider->srcHasNoDataValue( bandNo ); bool destHasNoDataValue = false; double destNoDataValue = std::numeric_limits<double>::quiet_NaN(); QGis::DataType destDataType = srcProvider->srcDataType( bandNo ); // TODO: verify what happens/should happen if srcNoDataValue is disabled by setUseSrcNoDataValue QgsDebugMsg( QString( "srcHasNoDataValue = %1 srcNoDataValue = %2" ).arg( srcHasNoDataValue ).arg( srcProvider->srcNoDataValue( bandNo ) ) ); if ( srcHasNoDataValue ) { // If source has no data value, it is used by provider destNoDataValue = srcProvider->srcNoDataValue( bandNo ); destHasNoDataValue = true; } else if ( nuller && nuller->noData( bandNo ).size() > 0 ) { // Use one user defined no data value destNoDataValue = nuller->noData( bandNo ).value( 0 ).min(); destHasNoDataValue = true; } else { // Verify if we realy need no data value, i.e. QgsRectangle srcExtent = outputExtent; QgsRasterProjector *projector = pipe->projector(); if ( projector && projector->destCrs() != projector->srcCrs() ) { QgsCoordinateTransform ct( projector->destCrs(), projector->srcCrs() ); srcExtent = ct.transformBoundingBox( outputExtent ); } if ( !srcProvider->extent().contains( srcExtent ) ) { // Destination extent is larger than source extent, we need destination no data values // Get src sample statistics (estimation from sample) QgsRasterBandStats stats = srcProvider->bandStatistics( bandNo, QgsRasterBandStats::Min | QgsRasterBandStats::Max, srcExtent, 250000 ); // Test if we have free (not used) values double typeMinValue = QgsContrastEnhancement::maximumValuePossible(( QGis::DataType )srcProvider->srcDataType( bandNo ) ); double typeMaxValue = QgsContrastEnhancement::maximumValuePossible(( QGis::DataType )srcProvider->srcDataType( bandNo ) ); if ( stats.minimumValue > typeMinValue ) { destNoDataValue = typeMinValue; } else if ( stats.maximumValue < typeMaxValue ) { destNoDataValue = typeMaxValue; } else { // We have to use wider type destDataType = QgsRasterBlock::typeWithNoDataValue( destDataType, &destNoDataValue ); } destHasNoDataValue = true; } } if ( nuller && destHasNoDataValue ) { nuller->setOutputNoDataValue( bandNo, destNoDataValue ); } QgsDebugMsg( QString( "bandNo = %1 destDataType = %2 destHasNoDataValue = %3 destNoDataValue = %4" ).arg( bandNo ).arg( destDataType ).arg( destHasNoDataValue ).arg( destNoDataValue ) ); destDataTypeList.append( destDataType ); destHasNoDataValueList.append( destHasNoDataValue ); destNoDataValueList.append( destNoDataValue ); } QGis::DataType destDataType = destDataTypeList.value( 0 ); // Currently write API supports one output type for dataset only -> find the widest for ( int i = 1; i < nBands; i++ ) { if ( destDataTypeList.value( i ) > destDataType ) { destDataType = destDataTypeList.value( i ); // no data value may be left per band (for future) } } //create destProvider for whole dataset here QgsRasterDataProvider* destProvider = 0; double pixelSize; double geoTransform[6]; globalOutputParameters( outputExtent, nCols, nRows, geoTransform, pixelSize ); // initOutput() returns 0 in tile mode! destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList ); WriterError error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog ); if ( error == NoDataConflict ) { // The value used for no data was found in source data, we must use wider data type if ( destProvider ) // no tiles { destProvider->remove(); delete destProvider; destProvider = 0; } else // VRT { // TODO: remove created VRT } // But we don't know which band -> wider all for ( int i = 0; i < nBands; i++ ) { double destNoDataValue; QGis::DataType destDataType = QgsRasterBlock::typeWithNoDataValue( destDataTypeList.value( i ), &destNoDataValue ); destDataTypeList.replace( i, destDataType ); destNoDataValueList.replace( i, destNoDataValue ); } destDataType = destDataTypeList.value( 0 ); // Try again destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType, destHasNoDataValueList, destNoDataValueList ); error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog ); } if ( destProvider ) delete destProvider; return error; }
QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeRaster( const QgsRasterPipe* pipe, int nCols, int nRows, QgsRectangle outputExtent, const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog ) { QgsDebugMsg( "Entered" ); if ( !pipe ) { return SourceProviderError; } mPipe = pipe; //const QgsRasterInterface* iface = iter->input(); const QgsRasterInterface* iface = pipe->last(); if ( !iface ) { return SourceProviderError; } mInput = iface; if ( QgsRasterBlock::typeIsColor( iface->dataType( 1 ) ) ) { mMode = Image; } else { mMode = Raw; } QgsDebugMsg( QString( "reading from %1" ).arg( typeid( *iface ).name() ) ); if ( !iface->srcInput() ) { QgsDebugMsg( "iface->srcInput() == 0" ); return SourceProviderError; } QgsDebugMsg( QString( "srcInput = %1" ).arg( typeid( *( iface->srcInput() ) ).name() ) ); mProgressDialog = progressDialog; QgsRasterIterator iter( pipe->last() ); //create directory for output files if ( mTiledMode ) { QFileInfo fileInfo( mOutputUrl ); if ( !fileInfo.exists() ) { QDir dir = fileInfo.dir(); if ( !dir.mkdir( fileInfo.fileName() ) ) { QgsDebugMsg( "Cannot create output VRT directory " + fileInfo.fileName() + " in " + dir.absolutePath() ); return CreateDatasourceError; } } } if ( mMode == Image ) { WriterError e = writeImageRaster( &iter, nCols, nRows, outputExtent, crs, progressDialog ); mProgressDialog = 0; return e; } else { mProgressDialog = 0; WriterError e = writeDataRaster( pipe, &iter, nCols, nRows, outputExtent, crs, progressDialog ); return e; } }
QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeRaster( const QgsRasterPipe *pipe, int nCols, int nRows, const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &crs, QgsRasterBlockFeedback *feedback ) { QgsDebugMsgLevel( "Entered", 4 ); if ( !pipe ) { return SourceProviderError; } mPipe = pipe; //const QgsRasterInterface* iface = iter->input(); const QgsRasterInterface *iface = pipe->last(); if ( !iface ) { return SourceProviderError; } mInput = iface; if ( QgsRasterBlock::typeIsColor( iface->dataType( 1 ) ) ) { mMode = Image; } else { mMode = Raw; } QgsDebugMsgLevel( QString( "reading from %1" ).arg( typeid( *iface ).name() ), 4 ); if ( !iface->sourceInput() ) { QgsDebugMsg( "iface->srcInput() == 0" ); return SourceProviderError; } #ifdef QGISDEBUG const QgsRasterInterface &srcInput = *iface->sourceInput(); QgsDebugMsgLevel( QString( "srcInput = %1" ).arg( typeid( srcInput ).name() ), 4 ); #endif mFeedback = feedback; QgsRasterIterator iter( pipe->last() ); //create directory for output files if ( mTiledMode ) { QFileInfo fileInfo( mOutputUrl ); if ( !fileInfo.exists() ) { QDir dir = fileInfo.dir(); if ( !dir.mkdir( fileInfo.fileName() ) ) { QgsDebugMsg( "Cannot create output VRT directory " + fileInfo.fileName() + " in " + dir.absolutePath() ); return CreateDatasourceError; } } } // Remove pre-existing overview files to avoid using those with new raster QFile pyramidFile( mOutputUrl + ( mTiledMode ? ".vrt.ovr" : ".ovr" ) ); if ( pyramidFile.exists() ) pyramidFile.remove(); pyramidFile.setFileName( mOutputUrl + ( mTiledMode ? ".vrt.rrd" : ".rrd" ) ); if ( pyramidFile.exists() ) pyramidFile.remove(); if ( mMode == Image ) { WriterError e = writeImageRaster( &iter, nCols, nRows, outputExtent, crs, feedback ); return e; } else { WriterError e = writeDataRaster( pipe, &iter, nCols, nRows, outputExtent, crs, feedback ); return e; } }