QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgsrasterblock.h
Go to the documentation of this file.
1/***************************************************************************
2 qgsrasterblock.h - Class representing a block of raster data
3 --------------------------------------
4 Date : Oct 9, 2012
5 Copyright : (C) 2012 by Radim Blazek
6 email : radim dot blazek at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#ifndef QGSRASTERBLOCK_H
19#define QGSRASTERBLOCK_H
20
21#include <limits>
22
23#include "qgis.h"
24#include "qgis_core.h"
25#include "qgis_sip.h"
26#include "qgserror.h"
27#include "qgslogger.h"
28#include "qgsrasterrange.h"
29
30#include <QImage>
31#include <QString>
32
33using namespace Qt::StringLiterals;
34
35class QgsRectangle;
36
41class CORE_EXPORT QgsRasterBlock
42{
43 public:
45
53
54 virtual ~QgsRasterBlock();
55
63 bool reset( Qgis::DataType dataType, int width, int height );
64
65 // TODO: consider if use isValid() at all, isEmpty() should be sufficient
66 // and works also if block is valid but empty - difference between valid and empty?
67
73 bool isValid() const SIP_HOLDGIL { return mValid; }
74
76 void setValid( bool valid ) SIP_HOLDGIL { mValid = valid; }
77
83 bool isEmpty() const;
84
89 {
90 // Modified and extended copy from GDAL
91 switch ( dataType )
92 {
95 return 1;
96
99 return 2;
100
105 return 4;
106
110 return 8;
111
113 return 16;
114
117 return 4;
118
120 break;
121 }
122 return 0;
123 }
124
129 {
130 return typeSize( mDataType );
131 }
132
139 static bool typeIsNumeric( Qgis::DataType type );
140
148 static bool typeIsComplex( Qgis::DataType type );
149
156 static bool typeIsColor( Qgis::DataType type );
157
159 Qgis::DataType dataType() const SIP_HOLDGIL { return mDataType; }
160
162 static Qgis::DataType typeWithNoDataValue( Qgis::DataType dataType, double *noDataValue );
163
171 bool hasNoDataValue() const SIP_HOLDGIL { return mHasNoDataValue; }
172
180 {
181 return mHasNoDataValue || mNoDataBitmap;
182 }
183
190 void setNoDataValue( double noDataValue ) SIP_HOLDGIL;
191
199 void resetNoDataValue() SIP_HOLDGIL;
200
209 double noDataValue() const SIP_HOLDGIL { return mNoDataValue; }
210
217 static QByteArray valueBytes( Qgis::DataType dataType, double value );
218
227 double value( int row, int column ) const SIP_HOLDGIL
228 {
229 return value( static_cast< qgssize >( row ) * mWidth + column );
230 }
231
244 double valueAndNoData( int row, int column, bool &isNoData ) const SIP_SKIP
245 {
246 return valueAndNoData( static_cast< qgssize >( row ) * mWidth + column, isNoData );
247 }
248
256 inline double value( qgssize index ) const SIP_HOLDGIL;
257
270 inline double valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP;
271
279 const quint8 *byteData() const SIP_SKIP
280 {
281 if ( mDataType != Qgis::DataType::Byte )
282 return nullptr;
283 return static_cast< const quint8 * >( mData );
284 }
285
292 QRgb color( int row, int column ) const SIP_HOLDGIL
293 {
294 if ( !mImage ) return NO_DATA_COLOR;
295
296 return mImage->pixel( column, row );
297 }
298
304 QRgb color( qgssize index ) const SIP_HOLDGIL
305 {
306 const int row = static_cast< int >( std::floor( static_cast< double >( index ) / mWidth ) );
307 const int column = index % mWidth;
308 return color( row, column );
309 }
310
318 bool isNoData( int row, int column ) const SIP_HOLDGIL
319 {
320 return isNoData( static_cast< qgssize >( row ) * mWidth + column );
321 }
322
330 bool isNoData( qgssize row, qgssize column ) const SIP_HOLDGIL
331 {
332 return isNoData( row * static_cast< qgssize >( mWidth ) + column );
333 }
334
341 bool isNoData( qgssize index ) const SIP_HOLDGIL
342 {
343 if ( !mHasNoDataValue && !mNoDataBitmap )
344 return false;
345 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
346 {
347 QgsDebugError( u"Index %1 out of range (%2 x %3)"_s.arg( index ).arg( mWidth ).arg( mHeight ) );
348 return true; // we consider no data if outside
349 }
350 if ( mHasNoDataValue )
351 {
352 const double value = readValue( mData, mDataType, index );
353 return isNoDataValue( value );
354 }
355 // use no data bitmap
356 if ( !mNoDataBitmap )
357 {
358 // no data are not defined
359 return false;
360 }
361 // TODO: optimize
362 const int row = static_cast< int >( index ) / mWidth;
363 const int column = index % mWidth;
364 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
365 const int bit = column % 8;
366 const int mask = 0x80 >> bit;
367 //int x = mNoDataBitmap[byte] & mask;
368 //QgsDebugMsg ( QString("byte = %1 bit = %2 mask = %3 nodata = %4 is nodata = %5").arg(byte).arg(bit).arg(mask, 0, 2 ).arg( x, 0, 2 ).arg( (bool)(x) ) );
369 return mNoDataBitmap[byte] & mask;
370 }
371
379 bool setValue( int row, int column, double value ) SIP_HOLDGIL
380 {
381 return setValue( static_cast< qgssize >( row ) * mWidth + column, value );
382 }
383
390 bool setValue( qgssize index, double value ) SIP_HOLDGIL
391 {
392 if ( !mData )
393 {
394 QgsDebugError( u"Data block not allocated"_s );
395 return false;
396 }
397 if ( index >= static_cast< qgssize >( mWidth ) *mHeight )
398 {
399 QgsDebugError( u"Index %1 out of range (%2 x %3)"_s.arg( index ).arg( mWidth ).arg( mHeight ) );
400 return false;
401 }
402 writeValue( mData, mDataType, index, value );
403 return true;
404 }
405
413 bool setColor( int row, int column, QRgb color ) SIP_HOLDGIL
414 {
415 return setColor( static_cast< qgssize >( row ) * mWidth + column, color );
416 }
417
424 bool setColor( qgssize index, QRgb color ) SIP_HOLDGIL
425 {
426 if ( !mImage )
427 {
428 QgsDebugError( u"Image not allocated"_s );
429 return false;
430 }
431
432 if ( index >= static_cast< qgssize >( mImage->width() ) * mImage->height() )
433 {
434 QgsDebugError( u"index %1 out of range"_s.arg( index ) );
435 return false;
436 }
437
438 // setPixel() is slow, see Qt doc -> use direct access
439 QRgb *bits = reinterpret_cast< QRgb * >( mImage->bits() );
440 bits[index] = color;
441 return true;
442 }
443
452 {
453 if ( !mImage )
454 return nullptr;
455 return reinterpret_cast< QRgb * >( mImage->bits() );
456 }
457
464 bool setIsNoData( int row, int column ) SIP_HOLDGIL
465 {
466 return setIsNoData( static_cast< qgssize >( row ) * mWidth + column );
467 }
468
475 {
476 if ( mHasNoDataValue )
477 {
478 return setValue( index, mNoDataValue );
479 }
480 else
481 {
482 if ( !mNoDataBitmap )
483 {
484 if ( !createNoDataBitmap() )
485 {
486 return false;
487 }
488 }
489 // TODO: optimize
490 const int row = static_cast< int >( index ) / mWidth;
491 const int column = index % mWidth;
492 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
493 const int bit = column % 8;
494 const int nodata = 0x80 >> bit;
495 //QgsDebugMsg ( QString("set byte = %1 bit = %2 no data by %3").arg(byte).arg(bit).arg(nodata, 0,2 ) );
496 mNoDataBitmap[byte] = mNoDataBitmap[byte] | nodata;
497 return true;
498 }
499 }
500
505 bool setIsNoData();
506
511 bool setIsNoDataExcept( QRect exceptRect );
512
521 void setIsData( int row, int column ) SIP_HOLDGIL
522 {
523 setIsData( static_cast< qgssize >( row )*mWidth + column );
524 }
525
534 {
535 if ( mHasNoDataValue )
536 {
537 //no data value set, so mNoDataBitmap is not being used
538 return;
539 }
540
541 if ( !mNoDataBitmap )
542 {
543 return;
544 }
545
546 // TODO: optimize
547 const int row = static_cast< int >( index ) / mWidth;
548 const int column = index % mWidth;
549 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
550 const int bit = column % 8;
551 const int nodata = 0x80 >> bit;
552 mNoDataBitmap[byte] = mNoDataBitmap[byte] & ~nodata;
553 }
554
555#ifndef SIP_RUN
564 bool fill( double value );
565#else
566
575 void fill( double value );
576 % MethodCode
577 if ( !QgsRasterBlock::typeIsNumeric( sipCpp->dataType() ) )
578 {
579 PyErr_SetString( PyExc_ValueError, u"Cannot fill a block with %1 data type"_s.arg( qgsEnumValueToKey( sipCpp->dataType() ) ).toUtf8().constData() );
580 sipIsErr = 1;
581 }
582 else if ( QgsRasterBlock::typeIsComplex( sipCpp->dataType() ) )
583 {
584 PyErr_SetString( PyExc_ValueError, u"Cannot fill a block with %1 complex data type"_s.arg( qgsEnumValueToKey( sipCpp->dataType() ) ).toUtf8().constData() );
585 sipIsErr = 1;
586 }
587 else if ( sipCpp->isEmpty() )
588 {
589 PyErr_SetString( PyExc_ValueError, u"Cannot fill an empty block"_s.toUtf8().constData() );
590 sipIsErr = 1;
591 }
592 else
593 {
594 sipCpp->fill( a0 );
595 }
596 % End
597#endif
598
607 QByteArray data() const;
608
617 void setData( const QByteArray &data, int offset = 0 );
618
625 char *bits( int row, int column ) SIP_SKIP;
626
634 char *bits( qgssize index ) SIP_SKIP;
635
642 char *bits() SIP_SKIP;
643
653 const char *constBits( qgssize index ) const SIP_SKIP;
654
662 const char *constBits() const SIP_SKIP;
663
671 static QString printValue( double value, bool localized = false );
672
681 static QString printValue( float value, bool localized = false ) SIP_SKIP;
682
688 bool convert( Qgis::DataType destDataType );
689
693 QImage image() const;
694
699 bool setImage( const QImage *image );
700
702 inline static double readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP;
703
705 inline static void writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP;
706
707 void applyNoDataValues( const QgsRasterRangeList &rangeList );
708
712 void applyScaleOffset( double scale, double offset );
713
715 QgsError error() const { return mError; }
716
718 void setError( const QgsError &error ) { mError = error;}
719
720 QString toString() const;
721
732 static QRect subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent );
733
738 int width() const SIP_HOLDGIL { return mWidth; }
739
744 int height() const SIP_HOLDGIL { return mHeight; }
745
763 bool minimum( double &minimum SIP_OUT, int &row SIP_OUT, int &column SIP_OUT ) const;
764
782 bool maximum( double &maximum SIP_OUT, int &row SIP_OUT, int &column SIP_OUT ) const;
783
806 bool minimumMaximum( double &minimum SIP_OUT, int &minimumRow SIP_OUT, int &minimumColumn SIP_OUT, double &maximum SIP_OUT, int &maximumRow SIP_OUT, int &maximumColumn SIP_OUT ) const;
807
808 private:
809 static QImage::Format imageFormat( Qgis::DataType dataType );
810 static Qgis::DataType dataType( QImage::Format format );
811
818 static bool isNoDataValue( double value, double noDataValue )
819 {
820 // TODO: optimize no data value test by memcmp()
821 // More precise would be std::isnan(value) && std::isnan(noDataValue(bandNo)), but probably
822 // not important and slower
823 return std::isnan( value ) ||
824 qgsDoubleNear( value, noDataValue );
825 }
826
832 inline bool isNoDataValue( double value ) const;
833
838 bool createNoDataBitmap();
839
849 static void *convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size );
850
851 // Valid
852 bool mValid = true;
853
854 // Data type
856
857 // Data type size in bytes, to make bits() fast
858 int mTypeSize = 0;
859
860 // Width
861 int mWidth = 0;
862
863 // Height
864 int mHeight = 0;
865
866 // Has no data value
867 bool mHasNoDataValue = false;
868
869 // No data value
870 double mNoDataValue;
871
872 static const QRgb NO_DATA_COLOR;
873
874 // Data block for numerical data types, not used with image data types
875 // QByteArray does not seem to be intended for large data blocks, does it?
876 void *mData = nullptr;
877
878 // Image for image data types, not used with numerical data types
879 QImage *mImage = nullptr;
880
881 // Bitmap of no data. One bit for each pixel. Bit is 1 if a pixels is no data.
882 // Each row is represented by whole number of bytes (last bits may be unused)
883 // to make processing rows easy.
884 char *mNoDataBitmap = nullptr;
885
886 // number of bytes in mNoDataBitmap row
887 int mNoDataBitmapWidth = 0;
888
889 // total size in bytes of mNoDataBitmap
890 qgssize mNoDataBitmapSize = 0;
891
892 // Error
893 QgsError mError;
894};
895
897{
898 if ( !data )
899 {
900 return std::numeric_limits<double>::quiet_NaN();
901 }
902
903 switch ( type )
904 {
906 return static_cast< double >( ( static_cast< quint8 * >( data ) )[index] );
908 return static_cast< double >( ( static_cast< qint8 * >( data ) )[index] );
910 return static_cast< double >( ( static_cast< quint16 * >( data ) )[index] );
912 return static_cast< double >( ( static_cast< qint16 * >( data ) )[index] );
914 return static_cast< double >( ( static_cast< quint32 * >( data ) )[index] );
916 return static_cast< double >( ( static_cast< qint32 * >( data ) )[index] );
918 return static_cast< double >( ( static_cast< float * >( data ) )[index] );
920 return static_cast< double >( ( static_cast< double * >( data ) )[index] );
928 QgsDebugError( u"Data type %1 is not supported"_s.arg( qgsEnumValueToKey< Qgis::DataType >( type ) ) );
929 break;
930 }
931
932 return std::numeric_limits<double>::quiet_NaN();
933}
934
935inline void QgsRasterBlock::writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP
936{
937 if ( !data ) return;
938
939 switch ( type )
940 {
942 ( static_cast< quint8 * >( data ) )[index] = static_cast< quint8 >( value );
943 break;
945 ( static_cast< qint8 * >( data ) )[index] = static_cast< qint8 >( value );
946 break;
948 ( static_cast< quint16 * >( data ) )[index] = static_cast< quint16 >( value );
949 break;
951 ( static_cast< qint16 * >( data ) )[index] = static_cast< qint16 >( value );
952 break;
954 ( static_cast< quint32 * >( data ) )[index] = static_cast< quint32 >( value );
955 break;
957 ( static_cast< qint32 * >( data ) )[index] = static_cast< qint32 >( value );
958 break;
960 ( static_cast< float * >( data ) )[index] = static_cast< float >( value );
961 break;
963 ( static_cast< double * >( data ) )[index] = value;
964 break;
972 QgsDebugError( u"Data type %1 is not supported"_s.arg( qgsEnumValueToKey< Qgis::DataType >( type ) ) );
973 break;
974 }
975}
976
977inline double QgsRasterBlock::value( qgssize index ) const SIP_SKIP
978{
979 if ( !mData )
980 {
981 QgsDebugError( u"Data block not allocated"_s );
982 return std::numeric_limits<double>::quiet_NaN();
983 }
984 return readValue( mData, mDataType, index );
985}
986
987inline double QgsRasterBlock::valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP
988{
989 if ( !mData )
990 {
991 QgsDebugError( u"Data block not allocated"_s );
992 isNoData = true;
993 return std::numeric_limits<double>::quiet_NaN();
994 }
995 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
996 {
997 QgsDebugError( u"Index %1 out of range (%2 x %3)"_s.arg( index ).arg( mWidth ).arg( mHeight ) );
998 isNoData = true; // we consider no data if outside
999 return std::numeric_limits<double>::quiet_NaN();
1000 }
1001
1002 const double val = readValue( mData, mDataType, index );
1003
1004 if ( !mHasNoDataValue && !mNoDataBitmap )
1005 {
1006 isNoData = false;
1007 return val;
1008 }
1009
1010 if ( mHasNoDataValue )
1011 {
1012 isNoData = isNoDataValue( val );
1013 return val;
1014 }
1015 // use no data bitmap
1016 if ( !mNoDataBitmap )
1017 {
1018 // no data are not defined
1019 isNoData = false;
1020 return val;
1021 }
1022
1023 // no data is a bitmap
1025 return val;
1026}
1027
1028inline bool QgsRasterBlock::isNoDataValue( double value ) const SIP_SKIP
1029{
1030 return std::isnan( value ) || qgsDoubleNear( value, mNoDataValue );
1031}
1032
1033#endif
1034
1035
DataType
Raster data types.
Definition qgis.h:379
@ CInt32
Complex Int32.
Definition qgis.h:390
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:387
@ CFloat64
Complex Float64.
Definition qgis.h:392
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:384
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:394
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30).
Definition qgis.h:382
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:383
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:381
@ UnknownDataType
Unknown or unspecified type.
Definition qgis.h:380
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition qgis.h:393
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:386
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:388
@ CFloat32
Complex Float32.
Definition qgis.h:391
@ CInt16
Complex Int16.
Definition qgis.h:389
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:385
A container for error messages.
Definition qgserror.h:83
bool isValid() const
Returns true if the block is valid (correctly filled with data).
QRgb color(int row, int column) const
Read a single color.
double value(int row, int column) const
Read a single value if type of block is numeric.
static bool typeIsNumeric(Qgis::DataType type)
Returns true if a data type is numeric.
void setValid(bool valid)
Mark block as valid or invalid.
int height() const
Returns the height (number of rows) of the raster block.
bool isNoData(qgssize row, qgssize column) const
Check if value at position is no data.
bool isNoData(qgssize index) const
Check if value at position is no data.
bool setColor(qgssize index, QRgb color)
Set color on index (indexed line by line).
int dataTypeSize() const
Data type size in bytes.
bool hasNoData() const
Returns true if the block may contain no data.
char * bits(int row, int column)
Returns a pointer to block data.
double valueAndNoData(int row, int column, bool &isNoData) const
Reads a single value from the pixel at row and column, if type of block is numeric.
static int typeSize(Qgis::DataType dataType)
Returns the size in bytes for the specified dataType.
QByteArray data() const
Gets access to raw data.
bool setIsNoData(qgssize index)
Set no data on pixel.
bool setValue(qgssize index, double value)
Set value on index (indexed line by line).
double noDataValue() const
Returns no data value.
QRgb * colorData()
Gives direct read/write access to the raster RGB data.
void setIsData(qgssize index)
Remove no data flag on pixel.
bool setValue(int row, int column, double value)
Set value on position.
bool setColor(int row, int column, QRgb color)
Set color on position.
bool isNoData(int row, int column) const
Checks if value at position is no data.
void setIsData(int row, int column)
Remove no data flag on pixel.
Qgis::DataType dataType() const
Returns data type.
bool hasNoDataValue() const
true if the block has no data value.
static bool typeIsComplex(Qgis::DataType type)
Returns true if a data type is a complex number type.
const quint8 * byteData() const
Gives direct access to the raster block data.
static void writeValue(void *data, Qgis::DataType type, qgssize index, double value)
int width() const
Returns the width (number of columns) of the raster block.
QgsError error() const
Returns the last error.
void setError(const QgsError &error)
Sets the last error.
static double readValue(void *data, Qgis::DataType type, qgssize index)
bool setIsNoData(int row, int column)
Set no data on pixel.
QRgb color(qgssize index) const
Read a single value.
bool reset(Qgis::DataType dataType, int width, int height)
Reset block.
A rectangle specified with double values.
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7091
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition qgis.h:7423
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6900
#define SIP_SKIP
Definition qgis_sip.h:134
#define SIP_OUT
Definition qgis_sip.h:58
#define SIP_HOLDGIL
Definition qgis_sip.h:179
#define QgsDebugError(str)
Definition qgslogger.h:59
QList< QgsRasterRange > QgsRasterRangeList