QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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
68 // clang-format off
74 bool isValid() const SIP_HOLDGIL { return mValid; }
75 // clang-format on
76
78 void setValid( bool valid ) SIP_HOLDGIL { mValid = valid; }
79
85 bool isEmpty() const;
86
91 {
92 // Modified and extended copy from GDAL
93 switch ( dataType )
94 {
97 return 1;
98
101 return 2;
102
107 return 4;
108
112 return 8;
113
115 return 16;
116
119 return 4;
120
122 break;
123 }
124 return 0;
125 }
126
131 {
132 return typeSize( mDataType );
133 }
134
141 static bool typeIsNumeric( Qgis::DataType type );
142
150 static bool typeIsComplex( Qgis::DataType type );
151
158 static bool typeIsColor( Qgis::DataType type );
159
161 Qgis::DataType dataType() const SIP_HOLDGIL { return mDataType; }
162
164 static Qgis::DataType typeWithNoDataValue( Qgis::DataType dataType, double *noDataValue );
165
173 bool hasNoDataValue() const SIP_HOLDGIL { return mHasNoDataValue; }
174
182 {
183 return mHasNoDataValue || mNoDataBitmap;
184 }
185
192 void setNoDataValue( double noDataValue ) SIP_HOLDGIL;
193
201 void resetNoDataValue() SIP_HOLDGIL;
202
211 double noDataValue() const SIP_HOLDGIL { return mNoDataValue; }
212
219 static QByteArray valueBytes( Qgis::DataType dataType, double value );
220
229 double value( int row, int column ) const SIP_HOLDGIL
230 {
231 return value( static_cast< qgssize >( row ) * mWidth + column );
232 }
233
246 double valueAndNoData( int row, int column, bool &isNoData ) const SIP_SKIP
247 {
248 return valueAndNoData( static_cast< qgssize >( row ) * mWidth + column, isNoData );
249 }
250
258 inline double value( qgssize index ) const SIP_HOLDGIL;
259
272 inline double valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP;
273
281 const quint8 *byteData() const SIP_SKIP
282 {
283 if ( mDataType != Qgis::DataType::Byte )
284 return nullptr;
285 return static_cast< const quint8 * >( mData );
286 }
287
294 QRgb color( int row, int column ) const SIP_HOLDGIL
295 {
296 if ( !mImage ) return NO_DATA_COLOR;
297
298 return mImage->pixel( column, row );
299 }
300
306 QRgb color( qgssize index ) const SIP_HOLDGIL
307 {
308 const int row = static_cast< int >( std::floor( static_cast< double >( index ) / mWidth ) );
309 const int column = index % mWidth;
310 return color( row, column );
311 }
312
320 bool isNoData( int row, int column ) const SIP_HOLDGIL
321 {
322 return isNoData( static_cast< qgssize >( row ) * mWidth + column );
323 }
324
332 bool isNoData( qgssize row, qgssize column ) const SIP_HOLDGIL
333 {
334 return isNoData( row * static_cast< qgssize >( mWidth ) + column );
335 }
336
343 bool isNoData( qgssize index ) const SIP_HOLDGIL
344 {
345 if ( !mHasNoDataValue && !mNoDataBitmap )
346 return false;
347 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
348 {
349 QgsDebugError( u"Index %1 out of range (%2 x %3)"_s.arg( index ).arg( mWidth ).arg( mHeight ) );
350 return true; // we consider no data if outside
351 }
352 if ( mHasNoDataValue )
353 {
354 const double value = readValue( mData, mDataType, index );
355 return isNoDataValue( value );
356 }
357 // use no data bitmap
358 if ( !mNoDataBitmap )
359 {
360 // no data are not defined
361 return false;
362 }
363 // TODO: optimize
364 const int row = static_cast< int >( index ) / mWidth;
365 const int column = index % mWidth;
366 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
367 const int bit = column % 8;
368 const int mask = 0x80 >> bit;
369 //int x = mNoDataBitmap[byte] & mask;
370 //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) ) );
371 return mNoDataBitmap[byte] & mask;
372 }
373
381 bool setValue( int row, int column, double value ) SIP_HOLDGIL
382 {
383 return setValue( static_cast< qgssize >( row ) * mWidth + column, value );
384 }
385
392 bool setValue( qgssize index, double value ) SIP_HOLDGIL
393 {
394 if ( !mData )
395 {
396 QgsDebugError( u"Data block not allocated"_s );
397 return false;
398 }
399 if ( index >= static_cast< qgssize >( mWidth ) *mHeight )
400 {
401 QgsDebugError( u"Index %1 out of range (%2 x %3)"_s.arg( index ).arg( mWidth ).arg( mHeight ) );
402 return false;
403 }
404 writeValue( mData, mDataType, index, value );
405 return true;
406 }
407
415 bool setColor( int row, int column, QRgb color ) SIP_HOLDGIL
416 {
417 return setColor( static_cast< qgssize >( row ) * mWidth + column, color );
418 }
419
426 bool setColor( qgssize index, QRgb color ) SIP_HOLDGIL
427 {
428 if ( !mImage )
429 {
430 QgsDebugError( u"Image not allocated"_s );
431 return false;
432 }
433
434 if ( index >= static_cast< qgssize >( mImage->width() ) * mImage->height() )
435 {
436 QgsDebugError( u"index %1 out of range"_s.arg( index ) );
437 return false;
438 }
439
440 // setPixel() is slow, see Qt doc -> use direct access
441 QRgb *bits = reinterpret_cast< QRgb * >( mImage->bits() );
442 bits[index] = color;
443 return true;
444 }
445
454 {
455 if ( !mImage )
456 return nullptr;
457 return reinterpret_cast< QRgb * >( mImage->bits() );
458 }
459
466 bool setIsNoData( int row, int column ) SIP_HOLDGIL
467 {
468 return setIsNoData( static_cast< qgssize >( row ) * mWidth + column );
469 }
470
477 {
478 if ( mHasNoDataValue )
479 {
480 return setValue( index, mNoDataValue );
481 }
482 else
483 {
484 if ( !mNoDataBitmap )
485 {
486 if ( !createNoDataBitmap() )
487 {
488 return false;
489 }
490 }
491 // TODO: optimize
492 const int row = static_cast< int >( index ) / mWidth;
493 const int column = index % mWidth;
494 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
495 const int bit = column % 8;
496 const int nodata = 0x80 >> bit;
497 //QgsDebugMsg ( QString("set byte = %1 bit = %2 no data by %3").arg(byte).arg(bit).arg(nodata, 0,2 ) );
498 mNoDataBitmap[byte] = mNoDataBitmap[byte] | nodata;
499 return true;
500 }
501 }
502
507 bool setIsNoData();
508
513 bool setIsNoDataExcept( QRect exceptRect );
514
523 void setIsData( int row, int column ) SIP_HOLDGIL
524 {
525 setIsData( static_cast< qgssize >( row )*mWidth + column );
526 }
527
536 {
537 if ( mHasNoDataValue )
538 {
539 //no data value set, so mNoDataBitmap is not being used
540 return;
541 }
542
543 if ( !mNoDataBitmap )
544 {
545 return;
546 }
547
548 // TODO: optimize
549 const int row = static_cast< int >( index ) / mWidth;
550 const int column = index % mWidth;
551 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
552 const int bit = column % 8;
553 const int nodata = 0x80 >> bit;
554 mNoDataBitmap[byte] = mNoDataBitmap[byte] & ~nodata;
555 }
556
557#ifndef SIP_RUN
566 bool fill( double value );
567#else
568// clang-format off
569
578 void fill( double value );
579 % MethodCode
580 if ( !QgsRasterBlock::typeIsNumeric( sipCpp->dataType() ) )
581 {
582 PyErr_SetString( PyExc_ValueError, u"Cannot fill a block with %1 data type"_s.arg( qgsEnumValueToKey( sipCpp->dataType() ) ).toUtf8().constData() );
583 sipIsErr = 1;
584 }
585 else if ( QgsRasterBlock::typeIsComplex( sipCpp->dataType() ) )
586 {
587 PyErr_SetString( PyExc_ValueError, u"Cannot fill a block with %1 complex data type"_s.arg( qgsEnumValueToKey( sipCpp->dataType() ) ).toUtf8().constData() );
588 sipIsErr = 1;
589 }
590 else if ( sipCpp->isEmpty() )
591 {
592 PyErr_SetString( PyExc_ValueError, u"Cannot fill an empty block"_s.toUtf8().constData() );
593 sipIsErr = 1;
594 }
595 else
596 {
597 sipCpp->fill( a0 );
598 }
599 % End
600// clang-format on
601#endif
602
611 QByteArray data() const;
612
621 void setData( const QByteArray &data, int offset = 0 );
622
629 char *bits( int row, int column ) SIP_SKIP;
630
638 char *bits( qgssize index ) SIP_SKIP;
639
646 char *bits() SIP_SKIP;
647
657 const char *constBits( qgssize index ) const SIP_SKIP;
658
666 const char *constBits() const SIP_SKIP;
667
675 static QString printValue( double value, bool localized = false );
676
685 static QString printValue( float value, bool localized = false ) SIP_SKIP;
686
692 bool convert( Qgis::DataType destDataType );
693
697 QImage image() const;
698
703 bool setImage( const QImage *image );
704
706 inline static double readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP;
707
709 inline static void writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP;
710
711 void applyNoDataValues( const QgsRasterRangeList &rangeList );
712
716 void applyScaleOffset( double scale, double offset );
717
719 QgsError error() const { return mError; }
720
722 void setError( const QgsError &error ) { mError = error;}
723
724 QString toString() const;
725
736 static QRect subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent );
737
742 int width() const SIP_HOLDGIL { return mWidth; }
743
748 int height() const SIP_HOLDGIL { return mHeight; }
749
767 bool minimum( double &minimum SIP_OUT, int &row SIP_OUT, int &column SIP_OUT ) const;
768
786 bool maximum( double &maximum SIP_OUT, int &row SIP_OUT, int &column SIP_OUT ) const;
787
810 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;
811
812 private:
813 static QImage::Format imageFormat( Qgis::DataType dataType );
814 static Qgis::DataType dataType( QImage::Format format );
815
822 static bool isNoDataValue( double value, double noDataValue )
823 {
824 // TODO: optimize no data value test by memcmp()
825 // More precise would be std::isnan(value) && std::isnan(noDataValue(bandNo)), but probably
826 // not important and slower
827 return std::isnan( value ) ||
828 qgsDoubleNear( value, noDataValue );
829 }
830
836 inline bool isNoDataValue( double value ) const;
837
842 bool createNoDataBitmap();
843
853 static void *convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size );
854
855 // Valid
856 bool mValid = true;
857
858 // Data type
860
861 // Data type size in bytes, to make bits() fast
862 int mTypeSize = 0;
863
864 // Width
865 int mWidth = 0;
866
867 // Height
868 int mHeight = 0;
869
870 // Has no data value
871 bool mHasNoDataValue = false;
872
873 // No data value
874 double mNoDataValue;
875
876 static const QRgb NO_DATA_COLOR;
877
878 // Data block for numerical data types, not used with image data types
879 // QByteArray does not seem to be intended for large data blocks, does it?
880 void *mData = nullptr;
881
882 // Image for image data types, not used with numerical data types
883 QImage *mImage = nullptr;
884
885 // Bitmap of no data. One bit for each pixel. Bit is 1 if a pixels is no data.
886 // Each row is represented by whole number of bytes (last bits may be unused)
887 // to make processing rows easy.
888 char *mNoDataBitmap = nullptr;
889
890 // number of bytes in mNoDataBitmap row
891 int mNoDataBitmapWidth = 0;
892
893 // total size in bytes of mNoDataBitmap
894 qgssize mNoDataBitmapSize = 0;
895
896 // Error
897 QgsError mError;
898};
899
901{
902 if ( !data )
903 {
904 return std::numeric_limits<double>::quiet_NaN();
905 }
906
907 switch ( type )
908 {
910 return static_cast< double >( ( static_cast< quint8 * >( data ) )[index] );
912 return static_cast< double >( ( static_cast< qint8 * >( data ) )[index] );
914 return static_cast< double >( ( static_cast< quint16 * >( data ) )[index] );
916 return static_cast< double >( ( static_cast< qint16 * >( data ) )[index] );
918 return static_cast< double >( ( static_cast< quint32 * >( data ) )[index] );
920 return static_cast< double >( ( static_cast< qint32 * >( data ) )[index] );
922 return static_cast< double >( ( static_cast< float * >( data ) )[index] );
924 return static_cast< double >( ( static_cast< double * >( data ) )[index] );
932 QgsDebugError( u"Data type %1 is not supported"_s.arg( qgsEnumValueToKey< Qgis::DataType >( type ) ) );
933 break;
934 }
935
936 return std::numeric_limits<double>::quiet_NaN();
937}
938
939inline void QgsRasterBlock::writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP
940{
941 if ( !data ) return;
942
943 switch ( type )
944 {
946 ( static_cast< quint8 * >( data ) )[index] = static_cast< quint8 >( value );
947 break;
949 ( static_cast< qint8 * >( data ) )[index] = static_cast< qint8 >( value );
950 break;
952 ( static_cast< quint16 * >( data ) )[index] = static_cast< quint16 >( value );
953 break;
955 ( static_cast< qint16 * >( data ) )[index] = static_cast< qint16 >( value );
956 break;
958 ( static_cast< quint32 * >( data ) )[index] = static_cast< quint32 >( value );
959 break;
961 ( static_cast< qint32 * >( data ) )[index] = static_cast< qint32 >( value );
962 break;
964 ( static_cast< float * >( data ) )[index] = static_cast< float >( value );
965 break;
967 ( static_cast< double * >( data ) )[index] = value;
968 break;
976 QgsDebugError( u"Data type %1 is not supported"_s.arg( qgsEnumValueToKey< Qgis::DataType >( type ) ) );
977 break;
978 }
979}
980
981inline double QgsRasterBlock::value( qgssize index ) const SIP_SKIP
982{
983 if ( !mData )
984 {
985 QgsDebugError( u"Data block not allocated"_s );
986 return std::numeric_limits<double>::quiet_NaN();
987 }
988 return readValue( mData, mDataType, index );
989}
990
991inline double QgsRasterBlock::valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP
992{
993 if ( !mData )
994 {
995 QgsDebugError( u"Data block not allocated"_s );
996 isNoData = true;
997 return std::numeric_limits<double>::quiet_NaN();
998 }
999 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
1000 {
1001 QgsDebugError( u"Index %1 out of range (%2 x %3)"_s.arg( index ).arg( mWidth ).arg( mHeight ) );
1002 isNoData = true; // we consider no data if outside
1003 return std::numeric_limits<double>::quiet_NaN();
1004 }
1005
1006 const double val = readValue( mData, mDataType, index );
1007
1008 if ( !mHasNoDataValue && !mNoDataBitmap )
1009 {
1010 isNoData = false;
1011 return val;
1012 }
1013
1014 if ( mHasNoDataValue )
1015 {
1016 isNoData = isNoDataValue( val );
1017 return val;
1018 }
1019 // use no data bitmap
1020 if ( !mNoDataBitmap )
1021 {
1022 // no data are not defined
1023 isNoData = false;
1024 return val;
1025 }
1026
1027 // no data is a bitmap
1029 return val;
1030}
1031
1032inline bool QgsRasterBlock::isNoDataValue( double value ) const SIP_SKIP
1033{
1034 return std::isnan( value ) || qgsDoubleNear( value, mNoDataValue );
1035}
1036
1037#endif
1038
1039
DataType
Raster data types.
Definition qgis.h:393
@ CInt32
Complex Int32.
Definition qgis.h:404
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:401
@ CFloat64
Complex Float64.
Definition qgis.h:406
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:398
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:408
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30).
Definition qgis.h:396
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:397
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:395
@ UnknownDataType
Unknown or unspecified type.
Definition qgis.h:394
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition qgis.h:407
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:400
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:402
@ CFloat32
Complex Float32.
Definition qgis.h:405
@ CInt16
Complex Int16.
Definition qgis.h:403
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:399
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:7157
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:7485
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975
#define SIP_SKIP
Definition qgis_sip.h:133
#define SIP_OUT
Definition qgis_sip.h:57
#define SIP_HOLDGIL
Definition qgis_sip.h:178
#define QgsDebugError(str)
Definition qgslogger.h:59
QList< QgsRasterRange > QgsRasterRangeList