Skip to content

Commit

Permalink
raster transparency floating point fix
Browse files Browse the repository at this point in the history
  • Loading branch information
blazek committed Sep 4, 2012
1 parent a441282 commit 2ef60da
Show file tree
Hide file tree
Showing 14 changed files with 246 additions and 96 deletions.
47 changes: 31 additions & 16 deletions src/app/qgsrasterlayerproperties.cpp
Expand Up @@ -996,6 +996,7 @@ void QgsRasterLayerProperties::on_pbnDefaultValues_clicked()

void QgsRasterLayerProperties::setTransparencyCell( int row, int column, double value )
{
QgsDebugMsg( QString( "value = %1" ).arg( value, 0, 'g', 17 ) );
QgsRasterDataProvider* provider = mRasterLayer->dataProvider();
if ( !provider ) return;

Expand Down Expand Up @@ -1026,7 +1027,7 @@ void QgsRasterLayerProperties::setTransparencyCell( int row, int column, double
lineEdit->setValidator( new QDoubleValidator( 0 ) );
if ( !qIsNaN( value ) )
{
valueString = QString::number( value, 'f' );
valueString = QgsRasterInterface::printValue( value );
}
break;
default:
Expand All @@ -1040,18 +1041,23 @@ void QgsRasterLayerProperties::setTransparencyCell( int row, int column, double
lineEdit->setText( valueString );
}
tableTransparency->setCellWidget( row, column, lineEdit );
adjustTransparencyCellWidth( row, column );

if ( nBands == 1 && ( column == 0 || column == 1 ) )
{
connect( lineEdit, SIGNAL( textEdited( const QString & ) ), this, SLOT( transparencyCellTextEdited( const QString & ) ) );
}
tableTransparency->resizeColumnsToContents();
}

void QgsRasterLayerProperties::setTransparencyCellValue( int row, int column, double value )
{
QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
if ( !lineEdit ) return;
lineEdit->setText( QString::number( value, 'f' ) );
lineEdit->setText( QgsRasterInterface::printValue( value ) );
lineEdit->adjustSize();
adjustTransparencyCellWidth( row, column );
tableTransparency->resizeColumnsToContents();
}

double QgsRasterLayerProperties::transparencyCellValue( int row, int column )
Expand All @@ -1064,6 +1070,17 @@ double QgsRasterLayerProperties::transparencyCellValue( int row, int column )
return lineEdit->text().toDouble();
}

void QgsRasterLayerProperties::adjustTransparencyCellWidth( int row, int column )
{
QLineEdit *lineEdit = dynamic_cast<QLineEdit *>( tableTransparency->cellWidget( row, column ) );
if ( !lineEdit ) return;

int width = qMax( lineEdit->fontMetrics().width( lineEdit->text() ) + 10, 100 );
width = qMax( width, tableTransparency->columnWidth( column ) );

lineEdit->setFixedWidth( width );
}

void QgsRasterLayerProperties::on_pbnExportTransparentPixelValues_clicked()
{
QSettings myQSettings;
Expand Down Expand Up @@ -1312,30 +1329,27 @@ void QgsRasterLayerProperties::pixelSelected( const QgsPoint& canvasPoint )
//Get the pixel values and add a new entry to the transparency table
if ( mMapCanvas && mPixelSelectorTool )
{
QMap< int, QString > myPixelMap;
mMapCanvas->unsetMapTool( mPixelSelectorTool );
mRasterLayer->identify( mMapCanvas->mapRenderer()->mapToLayerCoordinates( mRasterLayer, canvasPoint ), myPixelMap );
QMap< int, void *> myPixelMap = mRasterLayer->dataProvider()->identify( mMapCanvas->mapRenderer()->mapToLayerCoordinates( mRasterLayer, canvasPoint ) );

QList<int> bands = renderer->usesBands();
delete renderer;

QgsRasterDataProvider * provider = mRasterLayer->dataProvider();
QList<double> values;
for ( int i = 0; i < bands.size(); ++i )
{
QMap< int, QString >::const_iterator pixelResult = myPixelMap.find( bands.at( i ) );
if ( pixelResult != myPixelMap.constEnd() )
int bandNo = bands.value( i );
if ( myPixelMap.count( bandNo ) == 1 )
{
QString value = pixelResult.value();
if ( value != tr( "out of extent" ) )
void * data = myPixelMap.value( bandNo );
double value = provider->readValue( data, provider->dataType( bandNo ), 0 );
QgsDebugMsg( QString( "value = %1" ).arg( value, 0, 'g', 17 ) );

if ( provider->isNoDataValue( bandNo, value ) )
{
QgsDebugMsg( QString( "Is it %1 of band %2 nodata?" ).arg( value ).arg( bands.at( i ) ) );
if ( value == tr( "null (no data)" ) || // Very bad! TODO: improve identify
mRasterLayer->dataProvider()->isNoDataValue( bands.at( i ), value.toDouble() ) )
{
return; // Don't add nodata, transparent anyway
}
values.append( value.toDouble() );
return; // Don't add nodata, transparent anyway
}
values.append( value );
}
}
if ( bands.size() == 1 )
Expand All @@ -1350,6 +1364,7 @@ void QgsRasterLayerProperties::pixelSelected( const QgsPoint& canvasPoint )
}
setTransparencyCell( tableTransparency->rowCount() - 1, tableTransparency->columnCount() - 1, 100 );
}
delete renderer;

tableTransparency->resizeColumnsToContents();
tableTransparency->resizeRowsToContents();
Expand Down
1 change: 1 addition & 0 deletions src/app/qgsrasterlayerproperties.h
Expand Up @@ -149,6 +149,7 @@ class QgsRasterLayerProperties : public QDialog, private Ui::QgsRasterLayerPrope
void setTransparencyCellValue( int row, int column, double value );
double transparencyCellValue( int row, int column );
void setTransparencyToEdited( int row );
void adjustTransparencyCellWidth( int row, int column );

void setRendererWidget( const QString& rendererName );

Expand Down
36 changes: 36 additions & 0 deletions src/core/qgsrasterdataprovider.cpp
Expand Up @@ -202,6 +202,7 @@ QString QgsRasterDataProvider::capabilitiesString() const
return abilitiesList.join( ", " );
}

#if 0
bool QgsRasterDataProvider::identify( const QgsPoint& thePoint, QMap<QString, QString>& theResults )
{
Q_UNUSED( thePoint );
Expand All @@ -215,6 +216,41 @@ bool QgsRasterDataProvider::identify( const QgsPoint & point, QMap<int, QString>
results.clear();
return false;
}
#endif

QMap<int, void *> QgsRasterDataProvider::identify( const QgsPoint & point )
{
QMap<int, void *> results;

QgsRectangle myExtent = extent();

for ( int i = 1; i <= bandCount(); i++ )
{
double x = point.x();
double y = point.y();

// Calculate the row / column where the point falls
double xres = ( myExtent.xMaximum() - myExtent.xMinimum() ) / xSize();
double yres = ( myExtent.yMaximum() - myExtent.yMinimum() ) / ySize();

int col = ( int ) floor(( x - myExtent.xMinimum() ) / xres );
int row = ( int ) floor(( myExtent.yMaximum() - y ) / yres );

double xMin = myExtent.xMinimum() + col * xres;
double xMax = xMin + xres;
double yMax = myExtent.yMaximum() - row * yres;
double yMin = yMax - yres;
QgsRectangle pixelExtent( xMin, yMin, xMax, yMax );

void * data = block( i, pixelExtent, 1, 2 );

if ( data )
{
results.insert( i, data );
}
}
return results;
}

QString QgsRasterDataProvider::lastErrorFormat()
{
Expand Down
13 changes: 11 additions & 2 deletions src/core/qgsrasterdataprovider.h
Expand Up @@ -414,9 +414,18 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
virtual QString metadata() = 0;

/** \brief Identify raster value(s) found on the point position */
virtual bool identify( const QgsPoint & point, QMap<QString, QString>& results );
//virtual bool identify( const QgsPoint & point, QMap<QString, QString>& results );

virtual bool identify( const QgsPoint & point, QMap<int, QString>& results );
//virtual bool identify( const QgsPoint & point, QMap<int, QString>& results );

/** \brief Identify raster value(s) found on the point position
* @param point coordinates in data source CRS
* @return list of pointers to data blocks for all bands,
* caller is responsible to free the allocated memory,
* readValue() may be used to get values
*/
// TODO: Consider QVariant or similar instead of void*
virtual QMap<int, void *> identify( const QgsPoint & point );

/**
* \brief Identify details from a server (e.g. WMS) from the last screen update
Expand Down
38 changes: 38 additions & 0 deletions src/core/raster/qgsrasterinterface.cpp
Expand Up @@ -210,3 +210,41 @@ double QgsRasterInterface::time( bool cumulative )
return t;
}

QString QgsRasterInterface::printValue( double value )
{
/*
* IEEE 754 double has 15-17 significant digits. It specifies:
*
* "If a decimal string with at most 15 significant decimal is converted to
* IEEE 754 double precision and then converted back to the same number of
* significant decimal, then the final string should match the original;
* and if an IEEE 754 double precision is converted to a decimal string with at
* least 17 significant decimal and then converted back to double, then the final
* number must match the original."
*
* If printing only 15 digits, some precision could be lost. Printing 17 digits may
* add some confusing digits.
*
* Default 'g' precision on linux is 6 digits, not all significant digits like
* some sprintf manuals say.
*
* We need to ensure that the number printed and used in QLineEdit or XML will
* give the same number when parsed.
*
* Is there a better solution?
*/

QString s;

for ( int i = 15; i <= 17; i++ )
{
s.setNum( value, 'g', i );
if ( s.toDouble() == value )
{
return s;
}
}
// Should not happen
QgsDebugMsg( "Cannot correctly parse printed value" );
return s;
}
7 changes: 6 additions & 1 deletion src/core/raster/qgsrasterinterface.h
Expand Up @@ -191,6 +191,12 @@ class CORE_EXPORT QgsRasterInterface
* returned. */
double time( bool cumulative = false );

/** \brief Print double value with all necessary significant digits.
* It is ensured that conversion back to double gives the same number.
* @param value the value to be printed
* @return string representing the value*/
static QString printValue( double value );

protected:
// QgsRasterInterface used as input
QgsRasterInterface* mInput;
Expand Down Expand Up @@ -255,7 +261,6 @@ inline double QgsRasterInterface::readValue( void *data, QgsRasterInterface::Dat

inline void QgsRasterInterface::writeValue( void *data, QgsRasterInterface::DataType type, int index, double value )
{
if ( !mInput ) return;
if ( !data ) return;

switch ( type )
Expand Down
58 changes: 49 additions & 9 deletions src/core/raster/qgsrasterlayer.cpp
Expand Up @@ -957,22 +957,62 @@ bool QgsRasterLayer::hasStatistics( int theBandNo )
bool QgsRasterLayer::identify( const QgsPoint& thePoint, QMap<QString, QString>& theResults )
{
theResults.clear();
// QgsDebugMsg( "identify provider : " + mProviderKey ) ;
return ( mDataProvider->identify( thePoint, theResults ) );

if ( !mDataProvider ) return false;

QMap<int, QString> results;
if ( ! identify( thePoint, results ) ) return false;

foreach ( int bandNo, results.keys() )
{
theResults[ mDataProvider->generateBandName( bandNo )] = results.value( bandNo );
}
return true;
}

bool QgsRasterLayer::identify( const QgsPoint & point, QMap<int, QString>& results )
bool QgsRasterLayer::identify( const QgsPoint & point, QMap<int, QString>& theResults )
{
if ( !mDataProvider )
if ( !mDataProvider ) return false;

theResults.clear();
//return mDataProvider->identify( point, theResults );

QMap<int, void *> dataMap = mDataProvider->identify( point );
foreach ( int bandNo, dataMap.keys() )
{
return false;
QgsRasterInterface::DataType dataType = mDataProvider->dataType( bandNo );
void * data = dataMap.value( bandNo );
QString str;
if ( !data )
{
str = tr( "Cannot read data" );
}
else
{
if ( mDataProvider->typeIsNumeric( dataType ) )
{
double value = mDataProvider->readValue( data, dataType, 0 );
if ( mDataProvider->isNoDataValue( bandNo, value ) )
{
str = tr( "null (no data)" );
}
else
{
str.setNum( value );
}
}
else
{
QRgb c((( uint* )data )[0] );
str = QString( "%1,%2,%3,%4" ).arg( qRed( c ) ).arg( qGreen( c ) ).arg( qBlue( c ) ).arg( qAlpha( c ) );
}
free( data );
}
theResults[ bandNo ] = str;
}

results.clear();
return mDataProvider->identify( point, results );
return true;
}


/**
* @note The arbitraryness of the returned document is enforced by WMS standards up to at least v1.3.0
*
Expand Down
19 changes: 11 additions & 8 deletions src/core/raster/qgsrastertransparency.cpp
Expand Up @@ -16,6 +16,7 @@ email : ersts@amnh.org
* *
***************************************************************************/

#include "qgsrasterinterface.h"
#include "qgsrastertransparency.h"
#include "qgis.h"
#include "qgslogger.h"
Expand Down Expand Up @@ -114,7 +115,9 @@ int QgsRasterTransparency::alphaValue( double theValue, int theGlobalTransparenc
for ( int myListRunner = 0; myListRunner < mTransparentSingleValuePixelList.count(); myListRunner++ )
{
myTransparentPixel = mTransparentSingleValuePixelList[myListRunner];
if ( theValue >= myTransparentPixel.min && theValue <= myTransparentPixel.max )
if (( theValue >= myTransparentPixel.min && theValue <= myTransparentPixel.max ) ||
doubleNear( theValue, myTransparentPixel.min ) ||
doubleNear( theValue, myTransparentPixel.max ) )
{
myTransparentPixelFound = true;
break;
Expand Down Expand Up @@ -181,8 +184,8 @@ bool QgsRasterTransparency::isEmpty( double nodataValue ) const
( mTransparentSingleValuePixelList.size() == 1 &&
( doubleNear( mTransparentSingleValuePixelList.at( 0 ).min, nodataValue ) ||
doubleNear( mTransparentSingleValuePixelList.at( 0 ).max, nodataValue ) ||
( nodataValue > mTransparentSingleValuePixelList.at( 0 ).min &&
nodataValue < mTransparentSingleValuePixelList.at( 0 ).max ) ) ) )
( nodataValue >= mTransparentSingleValuePixelList.at( 0 ).min &&
nodataValue <= mTransparentSingleValuePixelList.at( 0 ).max ) ) ) )
&&
( mTransparentThreeValuePixelList.isEmpty() ||
( mTransparentThreeValuePixelList.size() == 1 &&
Expand All @@ -202,8 +205,8 @@ void QgsRasterTransparency::writeXML( QDomDocument& doc, QDomElement& parentElem
{
QDomElement pixelListElement = doc.createElement( "pixelListEntry" );
//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( "min", QgsRasterInterface::printValue( it->min ) );
pixelListElement.setAttribute( "max", QgsRasterInterface::printValue( it->max ) );
pixelListElement.setAttribute( "percentTransparent", QString::number( it->percentTransparent ) );
singleValuePixelListElement.appendChild( pixelListElement );
}
Expand All @@ -217,9 +220,9 @@ void QgsRasterTransparency::writeXML( QDomDocument& doc, QDomElement& parentElem
for ( ; it != mTransparentThreeValuePixelList.constEnd(); ++it )
{
QDomElement pixelListElement = doc.createElement( "pixelListEntry" );
pixelListElement.setAttribute( "red", QString::number( it->red, 'f' ) );
pixelListElement.setAttribute( "green", QString::number( it->green, 'f' ) );
pixelListElement.setAttribute( "blue", QString::number( it->blue, 'f' ) );
pixelListElement.setAttribute( "red", QgsRasterInterface::printValue( it->red ) );
pixelListElement.setAttribute( "green", QgsRasterInterface::printValue( it->green ) );
pixelListElement.setAttribute( "blue", QgsRasterInterface::printValue( it->blue ) );
pixelListElement.setAttribute( "percentTransparent", QString::number( it->percentTransparent ) );
threeValuePixelListElement.appendChild( pixelListElement );
}
Expand Down

0 comments on commit 2ef60da

Please sign in to comment.