Skip to content

Commit 07c5758

Browse files
committed
[RASTER][Feature] Applying scale and offset to raster data - funded
Ifremer An issue has been opened 5 mounth ago [BUG] #8417 incorrect value loaded from netcdf file The data has not be loaded incorrectly, but QGIS doesn't apply scale and offset defined for each band. This commit will apply scale and offset to GDAL Provider BandStatistics and to RASTER block of data. It also adds bandScale and bandOffset method to QgsRasterDataProvider Python API.
1 parent 60f7824 commit 07c5758

10 files changed

+267
-48
lines changed

python/core/raster/qgsrasterblock.sip

+4
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ class QgsRasterBlock
229229

230230
void applyNoDataValues( const QgsRasterRangeList & rangeList );
231231

232+
/** apply band scale and offset to raster block values
233+
* @@note added in 2.3 */
234+
void applyScaleOffset( double scale, double offset );
235+
232236
/** \brief Get error */
233237
QgsError error() const;
234238

python/core/raster/qgsrasterdataprovider.sip

+8
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ class QgsRasterDataProvider : QgsDataProvider, QgsRasterInterface
5656

5757
virtual QString colorInterpretationName( int theBandNo ) const;
5858

59+
/** Read band scale for raster value
60+
* @@note added in 2.3 */
61+
virtual double bandScale( int bandNo ) const;
62+
63+
/** Read band offset for raster value
64+
* @@note added in 2.3 */
65+
virtual double bandOffset( int bandNo ) const;
66+
5967
/** Get block size */
6068
virtual int xBlockSize() const;
6169
virtual int yBlockSize() const;

src/core/raster/qgsrasterblock.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,20 @@ bool QgsRasterBlock::convert( QGis::DataType destDataType )
706706
return true;
707707
}
708708

709+
void QgsRasterBlock::applyScaleOffset( double scale, double offset )
710+
{
711+
if ( isEmpty() ) return;
712+
if ( !typeIsNumeric( mDataType ) ) return;
713+
if ( scale == 1.0 && offset == 0.0 ) return;
714+
715+
qgssize size = (qgssize) mWidth * mHeight;
716+
for ( qgssize i = 0; i < size; ++i )
717+
{
718+
if ( !isNoData( i ) ) setValue( i, value( i ) * scale + offset );
719+
}
720+
return;
721+
}
722+
709723
void QgsRasterBlock::applyNoDataValues( const QgsRasterRangeList & rangeList )
710724
{
711725
if ( rangeList.isEmpty() )

src/core/raster/qgsrasterblock.h

+4
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,10 @@ class CORE_EXPORT QgsRasterBlock
291291

292292
void applyNoDataValues( const QgsRasterRangeList & rangeList );
293293

294+
/** apply band scale and offset to raster block values
295+
* @@note added in 2.3 */
296+
void applyScaleOffset( double scale, double offset );
297+
294298
/** \brief Get error */
295299
QgsError error() const { return mError; }
296300

src/core/raster/qgsrasterdataprovider.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ QgsRasterBlock * QgsRasterDataProvider::block( int theBandNo, QgsRectangle cons
208208
readBlock( theBandNo, theExtent, theWidth, theHeight, block->bits() );
209209
}
210210

211+
// apply scale and offset
212+
block->applyScaleOffset( bandScale( theBandNo ), bandOffset( theBandNo ) );
211213
// apply user no data values
212214
block->applyNoDataValues( userNoDataValues( theBandNo ) );
213215
return block;

src/core/raster/qgsrasterdataprovider.h

+7
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,13 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
161161
return colorName( colorInterpretation( theBandNo ) );
162162
}
163163

164+
/** Read band scale for raster value
165+
* @@note added in 2.3 */
166+
virtual double bandScale( int bandNo ) const { Q_UNUSED( bandNo ); return 1.0; }
167+
/** Read band offset for raster value
168+
* @@note added in 2.3 */
169+
virtual double bandOffset( int bandNo ) const { Q_UNUSED( bandNo ); return 0.0; }
170+
164171
// TODO: remove or make protected all readBlock working with void*
165172

166173
/** Read block of data using given extent and size. */

src/providers/gdal/qgsgdalprovider.cpp

+138-46
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,8 @@ QgsRasterBlock* QgsGdalProvider::block( int theBandNo, const QgsRectangle &theEx
387387
block->setIsNoDataExcept( subRect );
388388
}
389389
readBlock( theBandNo, theExtent, theWidth, theHeight, block->bits() );
390+
// apply scale and offset
391+
block->applyScaleOffset( bandScale( theBandNo ), bandOffset( theBandNo ) );
390392
block->applyNoDataValues( userNoDataValues( theBandNo ) );
391393
return block;
392394
}
@@ -1067,55 +1069,45 @@ int QgsGdalProvider::capabilities() const
10671069
return capability;
10681070
}
10691071

1070-
QGis::DataType QgsGdalProvider::dataTypeFormGdal( int theGdalDataType ) const
1071-
{
1072-
switch ( theGdalDataType )
1073-
{
1074-
case GDT_Unknown:
1075-
return QGis::UnknownDataType;
1076-
break;
1077-
case GDT_Byte:
1078-
return QGis::Byte;
1079-
break;
1080-
case GDT_UInt16:
1081-
return QGis::UInt16;
1082-
break;
1083-
case GDT_Int16:
1084-
return QGis::Int16;
1085-
break;
1086-
case GDT_UInt32:
1087-
return QGis::UInt32;
1088-
break;
1089-
case GDT_Int32:
1090-
return QGis::Int32;
1091-
break;
1092-
case GDT_Float32:
1093-
return QGis::Float32;
1094-
break;
1095-
case GDT_Float64:
1096-
return QGis::Float64;
1097-
break;
1098-
case GDT_CInt16:
1099-
return QGis::CInt16;
1100-
break;
1101-
case GDT_CInt32:
1102-
return QGis::CInt32;
1103-
break;
1104-
case GDT_CFloat32:
1105-
return QGis::CFloat32;
1106-
break;
1107-
case GDT_CFloat64:
1108-
return QGis::CFloat64;
1109-
break;
1110-
}
1111-
return QGis::UnknownDataType;
1112-
}
1113-
11141072
QGis::DataType QgsGdalProvider::srcDataType( int bandNo ) const
11151073
{
11161074
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
11171075
GDALDataType myGdalDataType = GDALGetRasterDataType( myGdalBand );
1118-
return dataTypeFromGdal( myGdalDataType );
1076+
QGis::DataType myDataType = dataTypeFromGdal( myGdalDataType );
1077+
1078+
// define if the band has scale and offset to apply
1079+
double myScale = bandScale( bandNo );
1080+
double myOffset = bandOffset( bandNo );
1081+
if ( myScale != 1.0 && myOffset != 0.0 )
1082+
{
1083+
// if the band has scale and offset to apply change dataType
1084+
switch ( myDataType )
1085+
{
1086+
case QGis::UnknownDataType:
1087+
case QGis::ARGB32:
1088+
case QGis::ARGB32_Premultiplied:
1089+
return myDataType;
1090+
break;
1091+
case QGis::Byte:
1092+
case QGis::UInt16:
1093+
case QGis::Int16:
1094+
case QGis::UInt32:
1095+
case QGis::Int32:
1096+
case QGis::Float32:
1097+
case QGis::CInt16:
1098+
myDataType = QGis::Float32;
1099+
break;
1100+
case QGis::Float64:
1101+
case QGis::CInt32:
1102+
case QGis::CFloat32:
1103+
myDataType = QGis::Float64;
1104+
break;
1105+
case QGis::CFloat64:
1106+
return myDataType;
1107+
break;
1108+
}
1109+
}
1110+
return myDataType;
11191111
}
11201112

11211113
QGis::DataType QgsGdalProvider::dataType( int bandNo ) const
@@ -1125,6 +1117,38 @@ QGis::DataType QgsGdalProvider::dataType( int bandNo ) const
11251117
return dataTypeFromGdal( mGdalDataType[bandNo-1] );
11261118
}
11271119

1120+
double QgsGdalProvider::bandScale( int bandNo ) const
1121+
{
1122+
#if GDAL_VERSION_NUM >= 1800
1123+
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
1124+
int bGotScale;
1125+
double myScale = GDALGetRasterScale( myGdalBand, &bGotScale );
1126+
if ( bGotScale )
1127+
return myScale;
1128+
else
1129+
return 1.0;
1130+
#else
1131+
Q_UNUSED( bandNo );
1132+
return 1.0;
1133+
#endif
1134+
}
1135+
1136+
double QgsGdalProvider::bandOffset( int bandNo ) const
1137+
{
1138+
#if GDAL_VERSION_NUM >= 1800
1139+
GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, bandNo );
1140+
int bGotOffset;
1141+
double myOffset = GDALGetRasterOffset( myGdalBand, &bGotOffset );
1142+
if ( bGotOffset )
1143+
return myOffset;
1144+
else
1145+
return 0.0;
1146+
#else
1147+
Q_UNUSED( bandNo );
1148+
return 0.0;
1149+
#endif
1150+
}
1151+
11281152
int QgsGdalProvider::bandCount() const
11291153
{
11301154
if ( mGdalDataset )
@@ -1355,10 +1379,19 @@ QgsRasterHistogram QgsGdalProvider::histogram( int theBandNo,
13551379

13561380
// Min/max, if not specified, are set by histogramDefaults, it does not
13571381
// set however min/max shifted to avoid rounding errors
1358-
1382+
13591383
double myMinVal = myHistogram.minimum;
13601384
double myMaxVal = myHistogram.maximum;
13611385

1386+
// unapply scale anf offset for min and max
1387+
double myScale = bandScale( theBandNo );
1388+
double myOffset = bandOffset( theBandNo );
1389+
if ( myScale != 1.0 || myOffset != 0. )
1390+
{
1391+
myMinVal = (myHistogram.minimum - myOffset) / myScale;
1392+
myMaxVal = (myHistogram.maximum - myOffset) / myScale;
1393+
}
1394+
13621395
double dfHalfBucket = ( myMaxVal - myMinVal ) / ( 2 * myHistogram.binCount );
13631396
myMinVal -= dfHalfBucket;
13641397
myMaxVal += dfHalfBucket;
@@ -2352,6 +2385,35 @@ QgsRasterBandStats QgsGdalProvider::bandStatistics( int theBandNo, int theStats,
23522385
myRasterBandStats.statsGathered = QgsRasterBandStats::Min | QgsRasterBandStats::Max
23532386
| QgsRasterBandStats::Range | QgsRasterBandStats::Mean
23542387
| QgsRasterBandStats::StdDev;
2388+
2389+
// define if the band has scale and offset to apply
2390+
double myScale = bandScale( theBandNo );
2391+
double myOffset = bandOffset( theBandNo );
2392+
if ( myScale != 1.0 || myOffset != 0.0 )
2393+
{
2394+
if ( myScale < 0.0 )
2395+
{
2396+
// update Min and Max value
2397+
myRasterBandStats.minimumValue = pdfMax * myScale + myOffset;
2398+
myRasterBandStats.maximumValue = pdfMin * myScale + myOffset;
2399+
// update the range
2400+
myRasterBandStats.range = (pdfMin - pdfMax) * myScale;
2401+
// update standard deviation
2402+
myRasterBandStats.stdDev = -1.0 * pdfStdDev * myScale;
2403+
}
2404+
else
2405+
{
2406+
// update Min and Max value
2407+
myRasterBandStats.minimumValue = pdfMin * myScale + myOffset;
2408+
myRasterBandStats.maximumValue = pdfMax * myScale + myOffset;
2409+
// update the range
2410+
myRasterBandStats.range = (pdfMax - pdfMin) * myScale;
2411+
// update standard deviation
2412+
myRasterBandStats.stdDev = pdfStdDev * myScale;
2413+
}
2414+
// update the mean
2415+
myRasterBandStats.mean = pdfMean * myScale + myOffset;
2416+
}
23552417

23562418
#ifdef QGISDEBUG
23572419
QgsDebugMsg( "************ STATS **************" );
@@ -2562,6 +2624,36 @@ void QgsGdalProvider::initBaseDataset()
25622624
#endif
25632625
//mGdalDataType.append( myInternalGdalDataType );
25642626

2627+
// define if the band has scale and offset to apply
2628+
double myScale = bandScale( i );
2629+
double myOffset = bandOffset( i );
2630+
if ( myScale != 1.0 && myOffset != 0.0 )
2631+
{
2632+
// if the band has scale and offset to apply change dataType
2633+
switch ( myGdalDataType )
2634+
{
2635+
case GDT_Unknown:
2636+
case GDT_TypeCount:
2637+
break;
2638+
case GDT_Byte:
2639+
case GDT_UInt16:
2640+
case GDT_Int16:
2641+
case GDT_UInt32:
2642+
case GDT_Int32:
2643+
case GDT_Float32:
2644+
case GDT_CInt16:
2645+
myGdalDataType = GDT_Float32;
2646+
break;
2647+
case GDT_Float64:
2648+
case GDT_CInt32:
2649+
case GDT_CFloat32:
2650+
myGdalDataType = GDT_Float64;
2651+
break;
2652+
case GDT_CFloat64:
2653+
break;
2654+
}
2655+
}
2656+
25652657
mGdalDataType.append( myGdalDataType );
25662658
//mInternalNoDataValue.append( myInternalNoDataValue );
25672659
//QgsDebugMsg( QString( "mInternalNoDataValue[%1] = %2" ).arg( i - 1 ).arg( mInternalNoDataValue[i-1] ) );

src/providers/gdal/qgsgdalprovider.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,6 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase
157157
QGis::DataType dataType( int bandNo ) const;
158158
QGis::DataType srcDataType( int bandNo ) const;
159159

160-
QGis::DataType dataTypeFormGdal( int theGdalDataType ) const;
161-
162160
int bandCount() const;
163161

164162
int colorInterpretation( int bandNo ) const;
@@ -177,6 +175,13 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase
177175
void readBlock( int bandNo, int xBlock, int yBlock, void *data );
178176
void readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, void *data );
179177

178+
/** Read band scale for raster value
179+
* @@note added in 2.3 */
180+
double bandScale( int bandNo ) const;
181+
/** Read band offset for raster value
182+
* @@note added in 2.3 */
183+
double bandOffset( int bandNo ) const;
184+
180185
QList<QgsColorRampShader::ColorRampItem> colorTable( int bandNo )const;
181186

182187
/**

0 commit comments

Comments
 (0)