451 changes: 242 additions & 209 deletions src/app/qgsrasterlayerproperties.cpp

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions src/app/qgsrasterlayerproperties.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ class QgsRasterLayerProperties : public QDialog, private Ui::QgsRasterLayerPrope
/** Update items in pipe list */
void pipeItemClicked( QTreeWidgetItem * item, int column );

/** Transparency cell changed */
void transparencyCellTextEdited( const QString & text );

signals:
/** emitted when changes to layer were saved to update legend */
void refreshLegend( QString layerID, bool expandItem );
Expand Down Expand Up @@ -137,9 +140,16 @@ class QgsRasterLayerProperties : public QDialog, private Ui::QgsRasterLayerPrope

QgsRasterRendererWidget* mRendererWidget;

void setupTransparencyTable( int nBands );

/** \brief Clear the current transparency table and populate the table with the correct types for current drawing mode and data type*/
void populateTransparencyTable( QgsRasterRenderer* renderer );

void setTransparencyCell( int row, int column, double value );
void setTransparencyCellValue( int row, int column, double value );
double transparencyCellValue( int row, int column );
void setTransparencyToEdited( int row );

void setRendererWidget( const QString& rendererName );

//@TODO we should move these gradient generators somewhere more generic
Expand All @@ -162,5 +172,7 @@ class QgsRasterLayerProperties : public QDialog, private Ui::QgsRasterLayerPrope
QgsMapToolEmitPoint* mPixelSelectorTool;

QgsRasterHistogramWidget* mHistogramWidget;

QVector<bool> mTransparencyToEdited;
};
#endif
6 changes: 6 additions & 0 deletions src/core/qgsrasterdataprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
/* Read a value from a data block at a given index. */
virtual double readValue( void *data, int type, int index );

/* Return true if source band has no data value */
virtual bool srcHasNoDataValue( int bandNo ) const { Q_UNUSED( bandNo ); return false; }

/** value representing null data */
virtual double noDataValue() const { return 0; }

Expand Down Expand Up @@ -522,6 +525,9 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
return false;
}

/** Set no data value on created dataset */
virtual bool setNoDataValue( int bandNo, double noDataValue ) { return false; }

/**Returns the formats supported by create()*/
virtual QStringList createFormats() const { return QStringList(); }

Expand Down
6 changes: 6 additions & 0 deletions src/core/qgsrasterprojector.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ class CORE_EXPORT QgsRasterProjector : public QgsRasterInterface
/** \brief set source and destination CRS */
void setCRS( QgsCoordinateReferenceSystem theSrcCRS, QgsCoordinateReferenceSystem theDestCRS );

/** \brief Get source CRS */
QgsCoordinateReferenceSystem srcCrs() const { return mSrcCRS; }

/** \brief Get destination CRS */
QgsCoordinateReferenceSystem destCrs() const { return mDestCRS; }

/** \brief set maximum source resolution */
void setMaxSrcRes( double theMaxSrcXRes, double theMaxSrcYRes )
{
Expand Down
226 changes: 180 additions & 46 deletions src/core/raster/qgsrasterfilewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "qgsrasterinterface.h"
#include "qgsrasteriterator.h"
#include "qgsrasterlayer.h"
#include "qgsrasterprojector.h"
#include <QCoreApplication>
#include <QProgressDialog>
#include <QTextStream>
Expand All @@ -38,16 +39,20 @@ QgsRasterFileWriter::~QgsRasterFileWriter()

}

QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeRaster( QgsRasterIterator* iter, int nCols, int nRows, QgsRectangle outputExtent,
const QgsCoordinateReferenceSystem& crs, QProgressDialog* p )
//QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeRaster( QgsRasterIterator* iter, int nCols, int nRows, QgsRectangle outputExtent,
QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeRaster( const QgsRasterPipe* pipe, int nCols, int nRows, QgsRectangle outputExtent,
const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog )
{
QgsDebugMsg( "Entered" );
if ( !iter )

//if ( !iter )
if ( !pipe )
{
return SourceProviderError;
}

const QgsRasterInterface* iface = iter->input();
//const QgsRasterInterface* iface = iter->input();
const QgsRasterInterface* iface = pipe->last();
if ( !iface )
{
return SourceProviderError;
Expand All @@ -59,39 +64,44 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeRaster( QgsRasterIter
return SourceProviderError;
}

mProgressDialog = p;
mProgressDialog = progressDialog;

QgsRasterIterator iter( pipe->last() );

if ( iface->dataType( 1 ) == QgsRasterInterface::ARGB32 ||
iface->dataType( 1 ) == QgsRasterInterface::ARGB32_Premultiplied )
{
WriterError e = writeImageRaster( iter, nCols, nRows, outputExtent, crs, p );
WriterError e = writeImageRaster( &iter, nCols, nRows, outputExtent, crs, progressDialog );
mProgressDialog = 0;
return e;
}
else
{
mProgressDialog = 0;
WriterError e = writeDataRaster( iter, nCols, nRows, outputExtent, crs, p );
WriterError e = writeDataRaster( pipe, &iter, nCols, nRows, outputExtent, crs, progressDialog );
return e;
}
}

QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent,
const QgsCoordinateReferenceSystem& crs, QProgressDialog* p )
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 = iter->input();
//const QgsRasterInterface* iface = iter->input();
const QgsRasterInterface* iface = pipe->last();
if ( !iface )
{
return SourceProviderError;
}

const QgsRasterDataProvider* srcProvider = dynamic_cast<const QgsRasterDataProvider*>( iface->srcInput() );
//const QgsRasterDataProvider* srcProvider = dynamic_cast<const QgsRasterDataProvider*>( iface->srcInput() );
//QgsRasterDataProvider* srcProvider = dynamic_cast<QgsRasterDataProvider*>( iface->srcInput() );
QgsRasterDataProvider* srcProvider = const_cast<QgsRasterDataProvider*>( dynamic_cast<const QgsRasterDataProvider*>( iface->srcInput() ) );
if ( !srcProvider )
{
QgsDebugMsg( "Cannot get source data provider" );
Expand All @@ -105,12 +115,6 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( QgsRaster
destDir.mkdir( mOutputUrl );
}

//Get output map units per pixel
int iterLeft = 0;
int iterTop = 0;
int iterCols = 0;
int iterRows = 0;

iter->setMaximumTileWidth( mMaxTileWidth );
iter->setMaximumTileHeight( mMaxTileHeight );

Expand All @@ -137,25 +141,157 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( QgsRaster
}
}

// 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<QgsRasterInterface::DataType> destDataTypeList;
for ( int bandNo = 1; bandNo <= nBands; bandNo++ )
{

bool srcHasNoDataValue = srcProvider->srcHasNoDataValue( bandNo );
bool destHasNoDataValue = false;
double destNoDataValue;
QgsRasterInterface::DataType destDataType = srcProvider->srcDataType( bandNo );
if ( srcHasNoDataValue )
{
// If source has no data value, it is used by provider
// TODO: this is not realy source no data, we would need srcNoDataValue() but it
// can be safely used I think
destNoDataValue = srcProvider->noDataValue();
destHasNoDataValue = true;
}
#if 0
else if ( )
// TODO: see if nuller has user defined aditional values, in that case use one as no data value

}
#endif
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(( QgsContrastEnhancement::QgsRasterDataType )srcProvider->srcDataType( bandNo ) );
double typeMaxValue = QgsContrastEnhancement::maximumValuePossible(( QgsContrastEnhancement::QgsRasterDataType )srcProvider->srcDataType( bandNo ) );
if ( stats.minimumValue > typeMinValue )
{
destNoDataValue = typeMinValue;
}
else if ( stats.maximumValue < typeMaxValue )
{
destNoDataValue = typeMaxValue;
}
else
{
// We have to use wider type
destDataType = QgsRasterInterface::typeWithNoDataValue( destDataType, &destNoDataValue );
}
destHasNoDataValue = true;
}
}
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 );
}


QgsRasterInterface::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)
}
}

destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType );

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
destProvider->remove();
delete destProvider;

// But we dont know which band -> wider all
for ( int i = 0; i < nBands; i++ )
{
double destNoDataValue;
QgsRasterInterface::DataType destDataType = QgsRasterInterface::typeWithNoDataValue( destDataTypeList.value( i ), &destNoDataValue );
destDataTypeList.replace( i, destDataType );
destNoDataValueList.replace( i, destNoDataValue );
}

// Try again
destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, destDataType );
error = writeDataRaster( pipe, iter, nCols, nRows, outputExtent, crs, destDataType, destHasNoDataValueList, destNoDataValueList, destProvider, progressDialog );
}

return error;
}

QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster(
const QgsRasterPipe* pipe,
QgsRasterIterator* iter,
int nCols, int nRows,
const QgsRectangle& outputExtent,
const QgsCoordinateReferenceSystem& crs,
QgsRasterInterface::DataType destDataType,
QList<bool> destHasNoDataValueList,
QList<double> destNoDataValueList,
QgsRasterDataProvider* destProvider,
QProgressDialog* progressDialog )
{
QgsDebugMsg( "Entered" );

const QgsRasterInterface* iface = iter->input();
const QgsRasterDataProvider* srcProvider = dynamic_cast<const QgsRasterDataProvider*>( iface->srcInput() );
int nBands = iface->bandCount();

//Get output map units per pixel
int iterLeft = 0;
int iterTop = 0;
int iterCols = 0;
int iterRows = 0;

int dataTypeSize = srcProvider->typeSize( srcProvider->srcDataType( 1 ) );
QList<void*> dataList;
for ( int i = 1; i <= nBands; ++i )
{
iter->startRasterRead( i, nCols, nRows, outputExtent );
dataList.push_back( VSIMalloc( dataTypeSize * mMaxTileWidth * mMaxTileHeight ) );
destProvider->setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
}

destProvider = initOutput( nCols, nRows, crs, geoTransform, nBands, srcProvider->srcDataType( 1 ) );

int nParts = 0;
int fileIndex = 0;
if ( p )
if ( progressDialog )
{
int nPartsX = nCols / iter->maximumTileWidth() + 1;
int nPartsY = nRows / iter->maximumTileHeight() + 1;
nParts = nPartsX * nPartsY;
p->setMaximum( nParts );
p->show();
p->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 1 ).arg( nParts ) );
progressDialog->setMaximum( nParts );
progressDialog->show();
progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 1 ).arg( nParts ) );
}

while ( true )
Expand All @@ -175,14 +311,15 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( QgsRaster

return NoError; //reached last tile, bail out
}
// TODO: verify if NoDataConflict happened, to do that we need the whole pipe or nuller interface
}

if ( p && fileIndex < ( nParts - 1 ) )
if ( progressDialog && fileIndex < ( nParts - 1 ) )
{
p->setValue( fileIndex + 1 );
p->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 2 ).arg( nParts ) );
progressDialog->setValue( fileIndex + 1 );
progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 2 ).arg( nParts ) );
QCoreApplication::processEvents( QEventLoop::AllEvents, 1000 );
if ( p->wasCanceled() )
if ( progressDialog->wasCanceled() )
{
for ( int i = 0; i < nBands; ++i )
{
Expand All @@ -196,7 +333,7 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( QgsRaster
{
delete destProvider;
destProvider = createPartProvider( outputExtent, nCols, iterCols, iterRows,
iterLeft, iterTop, mOutputUrl, fileIndex, nBands, iface->dataType( 1 ), crs );
iterLeft, iterTop, mOutputUrl, fileIndex, nBands, destDataType, crs );

//write data to output file. todo: loop over the data list
for ( int i = 1; i <= nBands; ++i )
Expand All @@ -215,16 +352,13 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster( QgsRaster
CPLFree( dataList[i - 1] );
}
}



++fileIndex;
}
return NoError;
}

QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent,
const QgsCoordinateReferenceSystem& crs, QProgressDialog* p )
const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog )
{
QgsDebugMsg( "Entered" );
if ( !iter )
Expand Down Expand Up @@ -270,14 +404,14 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRaste
iter->startRasterRead( 1, nCols, nRows, outputExtent );

int nParts = 0;
if ( p )
if ( progressDialog )
{
int nPartsX = nCols / iter->maximumTileWidth() + 1;
int nPartsY = nRows / iter->maximumTileHeight() + 1;
nParts = nPartsX * nPartsY;
p->setMaximum( nParts );
p->show();
p->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 1 ).arg( nParts ) );
progressDialog->setMaximum( nParts );
progressDialog->show();
progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 1 ).arg( nParts ) );
}

while ( iter->readNextRasterPart( 1, iterCols, iterRows, &data, iterLeft, iterTop ) )
Expand All @@ -288,12 +422,12 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRaste
continue;
}

if ( p && fileIndex < ( nParts - 1 ) )
if ( progressDialog && fileIndex < ( nParts - 1 ) )
{
p->setValue( fileIndex + 1 );
p->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 2 ).arg( nParts ) );
progressDialog->setValue( fileIndex + 1 );
progressDialog->setLabelText( QObject::tr( "Reading raster part %1 of %2" ).arg( fileIndex + 2 ).arg( nParts ) );
QCoreApplication::processEvents( QEventLoop::AllEvents, 1000 );
if ( p->wasCanceled() )
if ( progressDialog->wasCanceled() )
{
CPLFree( data );
break;
Expand Down Expand Up @@ -349,9 +483,9 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeImageRaster( QgsRaste
delete destProvider;
CPLFree( redData ); CPLFree( greenData ); CPLFree( blueData ); CPLFree( alphaData );

if ( p )
if ( progressDialog )
{
p->setValue( p->maximum() );
progressDialog->setValue( progressDialog->maximum() );
}

if ( mTiledMode )
Expand Down Expand Up @@ -462,16 +596,16 @@ int QgsRasterFileWriter::pyramidsProgress( double dfComplete, const char *pszMes
{
Q_UNUSED( pszMessage );
GDALTermProgress( dfComplete, 0, 0 );
QProgressDialog* p = static_cast<QProgressDialog*>( pData );
if ( pData && p->wasCanceled() )
QProgressDialog* progressDialog = static_cast<QProgressDialog*>( pData );
if ( pData && progressDialog->wasCanceled() )
{
return 0;
}

if ( pData )
{
p->setRange( 0, 100 );
p->setValue( dfComplete * 100 );
progressDialog->setRange( 0, 100 );
progressDialog->setValue( dfComplete * 100 );
}
return 1;
}
Expand Down
27 changes: 22 additions & 5 deletions src/core/raster/qgsrasterfilewriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ class CORE_EXPORT QgsRasterFileWriter
SourceProviderError = 1,
DestProviderError = 2,
CreateDatasourceError = 3,
WriteError = 4
WriteError = 4,
// Internal error if a value used for 'no data' was found in input
NoDataConflict = 5
};

QgsRasterFileWriter( const QString& outputUrl );
Expand All @@ -48,7 +50,8 @@ class CORE_EXPORT QgsRasterFileWriter
@param outputExtent extent to output
@param crs crs to reproject to
@param p dialog to show progress in */
WriterError writeRaster( QgsRasterIterator* iter, int nCols, int nRows, QgsRectangle outputExtent,
//WriterError writeRaster( QgsRasterIterator* iter, int nCols, int nRows, QgsRectangle outputExtent,
WriterError writeRaster( const QgsRasterPipe* pipe, int nCols, int nRows, QgsRectangle outputExtent,
const QgsCoordinateReferenceSystem& crs, QProgressDialog* p = 0 );

void setOutputFormat( const QString& format ) { mOutputFormat = format; }
Expand All @@ -72,10 +75,24 @@ class CORE_EXPORT QgsRasterFileWriter

private:
QgsRasterFileWriter(); //forbidden
WriterError writeDataRaster( QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent,
const QgsCoordinateReferenceSystem& crs, QProgressDialog* p = 0 );
//WriterError writeDataRaster( QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent,
WriterError writeDataRaster( const QgsRasterPipe* pipe, QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent,
const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog = 0 );

// Helper method used by previous one
WriterError writeDataRaster( const QgsRasterPipe* pipe,
QgsRasterIterator* iter,
int nCols, int nRows,
const QgsRectangle& outputExtent,
const QgsCoordinateReferenceSystem& crs,
QgsRasterInterface::DataType destDataType,
QList<bool> destHasNoDataValueList,
QList<double> destNoDataValueList,
QgsRasterDataProvider* destProvider,
QProgressDialog* progressDialog );

WriterError writeImageRaster( QgsRasterIterator* iter, int nCols, int nRows, const QgsRectangle& outputExtent,
const QgsCoordinateReferenceSystem& crs, QProgressDialog* p = 0 );
const QgsCoordinateReferenceSystem& crs, QProgressDialog* progressDialog = 0 );

//initialize vrt member variables
void createVRT( int xSize, int ySize, const QgsCoordinateReferenceSystem& crs, double* geoTransform );
Expand Down
34 changes: 34 additions & 0 deletions src/core/raster/qgsrasterinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* *
***************************************************************************/

#include <limits>
#include <typeinfo>

#include <QByteArray>
Expand Down Expand Up @@ -86,6 +87,39 @@ bool QgsRasterInterface::typeIsColor( DataType dataType ) const
return false;
}

QgsRasterInterface::DataType QgsRasterInterface::typeWithNoDataValue( DataType dataType, double *noDataValue )
{
DataType newDataType;

switch ( dataType )
{
case QgsRasterInterface::Byte:
*noDataValue = -32768.0;
newDataType = QgsRasterInterface::Int16;
break;
case QgsRasterInterface::Int16:
*noDataValue = -2147483648.0;
newDataType = QgsRasterInterface::Int32;
break;
case QgsRasterInterface::UInt16:
*noDataValue = -2147483648.0;
newDataType = QgsRasterInterface::Int32;
break;
case QgsRasterInterface::UInt32:
case QgsRasterInterface::Int32:
case QgsRasterInterface::Float32:
case QgsRasterInterface::Float64:
*noDataValue = std::numeric_limits<double>::max() * -1.0;
newDataType = QgsRasterInterface::Float64;
default:
QgsDebugMsg( QString( "Unknow data type %1" ).arg( dataType ) );
return UnknownDataType;
break;
}
QgsDebugMsg( QString( "newDataType = %1 noDataValue = %2" ).arg( newDataType ).arg( *noDataValue ) );
return newDataType;
}

// To give to an image preallocated memory is the only way to avoid memcpy
// when we want to keep data but delete QImage
QImage * QgsRasterInterface::createImage( int width, int height, QImage::Format format )
Expand Down
3 changes: 3 additions & 0 deletions src/core/raster/qgsrasterinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ class CORE_EXPORT QgsRasterInterface
return UnknownDataType;
}

/** For given data type returns wider type and sets no data value */
static DataType typeWithNoDataValue( DataType dataType, double *noDataValue );

/** Get number of bands */
virtual int bandCount() const
{
Expand Down
8 changes: 7 additions & 1 deletion src/core/raster/qgsrasterrendererregistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,21 +275,27 @@ QgsRasterRenderer* QgsRasterRendererRegistry::defaultRendererForDrawingStyle( co
if ( bandCount == 1 )
{
QList<QgsRasterTransparency::TransparentSingleValuePixel> transparentSingleList;
// No need to set no data value in transparency list, I think
#if 0
QgsRasterTransparency::TransparentSingleValuePixel singleEntry;
singleEntry.pixelValue = provider->noDataValue();
singleEntry.min = provider->noDataValue();
singleEntry.max = provider->noDataValue();
singleEntry.percentTransparent = 100;
transparentSingleList.push_back( singleEntry );
#endif
tr->setTransparentSingleValuePixelList( transparentSingleList );
}
else if ( bandCount == 3 )
{
QList<QgsRasterTransparency::TransparentThreeValuePixel> transparentThreeValueList;
#if 0
QgsRasterTransparency::TransparentThreeValuePixel threeValueEntry;
threeValueEntry.red = provider->noDataValue();
threeValueEntry.green = provider->noDataValue();
threeValueEntry.blue = provider->noDataValue();
threeValueEntry.percentTransparent = 100;
transparentThreeValueList.push_back( threeValueEntry );
#endif
tr->setTransparentThreeValuePixelList( transparentThreeValueList );
}
renderer->setRasterTransparency( tr );
Expand Down
22 changes: 17 additions & 5 deletions src/core/raster/qgsrastertransparency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ void QgsRasterTransparency::initializeTransparentPixelList( double theValue )

//add the initial value
TransparentSingleValuePixel myTransparentSingleValuePixel;
myTransparentSingleValuePixel.pixelValue = theValue;
myTransparentSingleValuePixel.min = theValue;
myTransparentSingleValuePixel.max = theValue;
myTransparentSingleValuePixel.percentTransparent = 100.0;
mTransparentSingleValuePixelList.append( myTransparentSingleValuePixel );
}
Expand Down Expand Up @@ -113,7 +114,7 @@ int QgsRasterTransparency::alphaValue( double theValue, int theGlobalTransparenc
for ( int myListRunner = 0; myListRunner < mTransparentSingleValuePixelList.count(); myListRunner++ )
{
myTransparentPixel = mTransparentSingleValuePixelList[myListRunner];
if ( myTransparentPixel.pixelValue == theValue )
if ( theValue >= myTransparentPixel.min && theValue <= myTransparentPixel.max )
{
myTransparentPixelFound = true;
break;
Expand Down Expand Up @@ -177,7 +178,7 @@ bool QgsRasterTransparency::isEmpty( double nodataValue ) const
{
return (
( mTransparentSingleValuePixelList.isEmpty() ||
( mTransparentSingleValuePixelList.size() == 1 && doubleNear( mTransparentSingleValuePixelList.at( 0 ).pixelValue, nodataValue ) ) )
( mTransparentSingleValuePixelList.size() == 1 && doubleNear( mTransparentSingleValuePixelList.at( 0 ).min, nodataValue ) && doubleNear( mTransparentSingleValuePixelList.at( 0 ).max, nodataValue ) ) )
&&
( mTransparentThreeValuePixelList.isEmpty() ||
( mTransparentThreeValuePixelList.size() < 4 && doubleNear( mTransparentThreeValuePixelList.at( 0 ).red, nodataValue ) &&
Expand All @@ -194,7 +195,9 @@ void QgsRasterTransparency::writeXML( QDomDocument& doc, QDomElement& parentElem
for ( ; it != mTransparentSingleValuePixelList.constEnd(); ++it )
{
QDomElement pixelListElement = doc.createElement( "pixelListEntry" );
pixelListElement.setAttribute( "pixelValue", QString::number( it->pixelValue, 'f' ) );
//pixelListElement.setAttribute( "pixelValue", QString::number( it->pixelValue, 'f' ) );
pixelListElement.setAttribute( "min", QString::number( it->min, 'f' ) );
pixelListElement.setAttribute( "max", QString::number( it->max, 'f' ) );
pixelListElement.setAttribute( "percentTransparent", QString::number( it->percentTransparent ) );
singleValuePixelListElement.appendChild( pixelListElement );
}
Expand Down Expand Up @@ -239,7 +242,16 @@ void QgsRasterTransparency::readXML( const QDomElement& elem )
{
currentEntryElem = entryList.at( i ).toElement();
sp.percentTransparent = currentEntryElem.attribute( "percentTransparent" ).toDouble();
sp.pixelValue = currentEntryElem.attribute( "pixelValue" ).toDouble();
// Backward compoatibility < 1.9 : pixelValue (before ranges)
if ( currentEntryElem.hasAttribute( "pixelValue" ) )
{
sp.min = sp.max = currentEntryElem.attribute( "pixelValue" ).toDouble();
}
else
{
sp.min = currentEntryElem.attribute( "min" ).toDouble();
sp.max = currentEntryElem.attribute( "max" ).toDouble();
}
mTransparentSingleValuePixelList.append( sp );
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/core/raster/qgsrastertransparency.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ class CORE_EXPORT QgsRasterTransparency

struct TransparentSingleValuePixel
{
double pixelValue;
double min;
double max;
double percentTransparent;
};

Expand Down
7 changes: 7 additions & 0 deletions src/gui/qgsrasterlayersaveasdialog.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgscoordinatetransform.h"
#include "qgsrasterlayersaveasdialog.h"
Expand All @@ -21,6 +22,12 @@ QgsRasterLayerSaveAsDialog::QgsRasterLayerSaveAsDialog( QgsRasterDataProvider* s
, mResolutionState( OriginalResolution )
{
setupUi( this );
mAddNoDataManuallyToolButton->setIcon( QgsApplication::getThemeIcon( "/mActionNewAttribute.png" ) );
mLoadTransparentNoDataToolButton->setIcon( QgsApplication::getThemeIcon( "/mActionCopySelected.png" ) );
mRemoveSelectedNoDataToolButton->setIcon( QgsApplication::getThemeIcon( "/mActionDeleteAttribute.png" ) );
mRemoveAllNoDataToolButton->setIcon( QgsApplication::getThemeIcon( "/mActionRemove.png" ) );
mNoDataGroupBox->setEnabled( false ); // not yet implemented

setValidators();
// Translated labels + EPSG are updated later
mCrsComboBox->addItem( "Layer", OriginalCrs );
Expand Down
47 changes: 47 additions & 0 deletions src/providers/gdal/qgsgdalprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,21 @@ void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent,
}
#endif

bool QgsGdalProvider::srcHasNoDataValue( int bandNo ) const
{
if ( mGdalDataset )
{
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
if ( myGdalBand )
{
int ok;
GDALGetRasterNoDataValue( myGdalBand, &ok );
return ok;
}
}
return false;
}

double QgsGdalProvider::noDataValue() const
{
if ( mNoDataValue.size() > 0 )
Expand Down Expand Up @@ -2274,13 +2289,45 @@ bool QgsGdalProvider::write( void* data, int band, int width, int height, int xO
return ( GDALRasterIO( rasterBand, GF_Write, xOffset, yOffset, width, height, data, width, height, GDALGetRasterDataType( rasterBand ), 0, 0 ) == CE_None );
}

bool QgsGdalProvider::setNoDataValue( int bandNo, double noDataValue )
{
if ( !mGdalDataset ) return false;

GDALRasterBandH rasterBand = GDALGetRasterBand( mGdalDataset, bandNo );
CPLErrorReset();
CPLErr err = GDALSetRasterNoDataValue( rasterBand, noDataValue );
if ( err != CPLE_None )
{
QgsDebugMsg( "Cannot set no data value" );
return false;
}
return true;
}

QStringList QgsGdalProvider::createFormats() const
{
return QStringList();
}

bool QgsGdalProvider::remove()
{
if ( mGdalDataset )
{
GDALDriverH driver = GDALGetDatasetDriver( mGdalDataset );
GDALClose( mGdalDataset );
mGdalDataset = 0;

CPLErrorReset();
CPLErr err = GDALDeleteDataset( driver, TO8F( dataSourceUri() ) );
if ( err != CPLE_None )
{
QgsLogger::warning( "RasterIO error: " + QString::fromUtf8( CPLGetLastErrorMsg() ) );
QgsDebugMsg( "RasterIO error: " + QString::fromUtf8( CPLGetLastErrorMsg() ) );
return false;
}
QgsDebugMsg( "Raster dataset dataSourceUri() successfully deleted" );
return true;
}
return false;
}

Expand Down
3 changes: 3 additions & 0 deletions src/providers/gdal/qgsgdalprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase

//void * readBlock( int bandNo, QgsRectangle const & extent, int width, int height );

bool srcHasNoDataValue( int bandNo ) const;
double noDataValue() const;
void computeMinMax( int bandNo );
double minimumValue( int bandNo ) const;
Expand Down Expand Up @@ -273,6 +274,8 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase
/**Writes into the provider datasource*/
bool write( void* data, int band, int width, int height, int xOffset, int yOffset );

bool setNoDataValue( int bandNo, double noDataValue );

/**Returns the formats supported by create()*/
QStringList createFormats() const;

Expand Down
76 changes: 50 additions & 26 deletions src/ui/qgsrasterlayerpropertiesbase.ui

Large diffs are not rendered by default.

84 changes: 81 additions & 3 deletions src/ui/qgsrasterlayersaveasdialogbase.ui
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>510</width>
<height>543</height>
<y>-118</y>
<width>492</width>
<height>661</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
Expand Down Expand Up @@ -362,6 +362,84 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="mNoDataGroupBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>Additional no data values. The specified values will be set to no data in output raster.</string>
</property>
<property name="title">
<string>No data values (not yet implemented)</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QTableWidget" name="mNoDataTableWidget"/>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QToolButton" name="mAddNoDataManuallyToolButton">
<property name="toolTip">
<string>Add values manually</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset>
<normaloff>../../images/themes/default/mActionNewAttribute.png</normaloff>../../images/themes/default/mActionNewAttribute.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mLoadTransparentNoDataToolButton">
<property name="toolTip">
<string>Load user defined fully transparent (100%) values </string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset>
<normaloff>../../images/themes/default/mActionCopySelected.png</normaloff>../../images/themes/default/mActionCopySelected.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mRemoveSelectedNoDataToolButton">
<property name="toolTip">
<string>Remove selected row</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset>
<normaloff>../../images/themes/default/mActionDeleteAttribute.png</normaloff>../../images/themes/default/mActionDeleteAttribute.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mRemoveAllNoDataToolButton">
<property name="toolTip">
<string>Clear all</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset>
<normaloff>../../images/themes/default/mActionRemove.png</normaloff>../../images/themes/default/mActionRemove.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
Expand Down