QGIS API Documentation 3.43.0-Master (1896cf2247d)
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 "qgis_core.h"
22#include "qgis_sip.h"
23#include <limits>
24#include <QImage>
25#include "qgis.h"
26#include "qgserror.h"
27#include "qgslogger.h"
28#include "qgsrasterrange.h"
29
30class QgsRectangle;
31
36class CORE_EXPORT QgsRasterBlock
37{
38 public:
40
47 QgsRasterBlock( Qgis::DataType dataType, int width, int height );
48
49 virtual ~QgsRasterBlock();
50
58 bool reset( Qgis::DataType dataType, int width, int height );
59
60 // TODO: consider if use isValid() at all, isEmpty() should be sufficient
61 // and works also if block is valid but empty - difference between valid and empty?
62
68 bool isValid() const SIP_HOLDGIL { return mValid; }
69
71 void setValid( bool valid ) SIP_HOLDGIL { mValid = valid; }
72
78 bool isEmpty() const;
79
83 static int typeSize( Qgis::DataType dataType ) SIP_HOLDGIL
84 {
85 // Modified and extended copy from GDAL
86 switch ( dataType )
87 {
90 return 1;
91
94 return 2;
95
100 return 4;
101
105 return 8;
106
108 return 16;
109
112 return 4;
113
115 break;
116 }
117 return 0;
118 }
119
124 {
125 return typeSize( mDataType );
126 }
127
134 static bool typeIsNumeric( Qgis::DataType type );
135
143 static bool typeIsComplex( Qgis::DataType type );
144
151 static bool typeIsColor( Qgis::DataType type );
152
154 Qgis::DataType dataType() const SIP_HOLDGIL { return mDataType; }
155
157 static Qgis::DataType typeWithNoDataValue( Qgis::DataType dataType, double *noDataValue );
158
166 bool hasNoDataValue() const SIP_HOLDGIL { return mHasNoDataValue; }
167
175 {
176 return mHasNoDataValue || mNoDataBitmap;
177 }
178
185 void setNoDataValue( double noDataValue ) SIP_HOLDGIL;
186
194 void resetNoDataValue() SIP_HOLDGIL;
195
204 double noDataValue() const SIP_HOLDGIL { return mNoDataValue; }
205
212 static QByteArray valueBytes( Qgis::DataType dataType, double value );
213
222 double value( int row, int column ) const SIP_HOLDGIL
223 {
224 return value( static_cast< qgssize >( row ) * mWidth + column );
225 }
226
239 double valueAndNoData( int row, int column, bool &isNoData ) const SIP_SKIP
240 {
241 return valueAndNoData( static_cast< qgssize >( row ) * mWidth + column, isNoData );
242 }
243
251 inline double value( qgssize index ) const SIP_HOLDGIL;
252
265 inline double valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP;
266
274 const quint8 *byteData() const SIP_SKIP
275 {
276 if ( mDataType != Qgis::DataType::Byte )
277 return nullptr;
278 return static_cast< const quint8 * >( mData );
279 }
280
287 QRgb color( int row, int column ) const SIP_HOLDGIL
288 {
289 if ( !mImage ) return NO_DATA_COLOR;
290
291 return mImage->pixel( column, row );
292 }
293
299 QRgb color( qgssize index ) const SIP_HOLDGIL
300 {
301 const int row = static_cast< int >( std::floor( static_cast< double >( index ) / mWidth ) );
302 const int column = index % mWidth;
303 return color( row, column );
304 }
305
313 bool isNoData( int row, int column ) const SIP_HOLDGIL
314 {
315 return isNoData( static_cast< qgssize >( row ) * mWidth + column );
316 }
317
325 bool isNoData( qgssize row, qgssize column ) const SIP_HOLDGIL
326 {
327 return isNoData( row * static_cast< qgssize >( mWidth ) + column );
328 }
329
336 bool isNoData( qgssize index ) const SIP_HOLDGIL
337 {
338 if ( !mHasNoDataValue && !mNoDataBitmap )
339 return false;
340 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
341 {
342 QgsDebugError( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
343 return true; // we consider no data if outside
344 }
345 if ( mHasNoDataValue )
346 {
347 const double value = readValue( mData, mDataType, index );
348 return isNoDataValue( value );
349 }
350 // use no data bitmap
351 if ( !mNoDataBitmap )
352 {
353 // no data are not defined
354 return false;
355 }
356 // TODO: optimize
357 const int row = static_cast< int >( index ) / mWidth;
358 const int column = index % mWidth;
359 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
360 const int bit = column % 8;
361 const int mask = 0x80 >> bit;
362 //int x = mNoDataBitmap[byte] & mask;
363 //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) ) );
364 return mNoDataBitmap[byte] & mask;
365 }
366
374 bool setValue( int row, int column, double value ) SIP_HOLDGIL
375 {
376 return setValue( static_cast< qgssize >( row ) * mWidth + column, value );
377 }
378
385 bool setValue( qgssize index, double value ) SIP_HOLDGIL
386 {
387 if ( !mData )
388 {
389 QgsDebugError( QStringLiteral( "Data block not allocated" ) );
390 return false;
391 }
392 if ( index >= static_cast< qgssize >( mWidth ) *mHeight )
393 {
394 QgsDebugError( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
395 return false;
396 }
397 writeValue( mData, mDataType, index, value );
398 return true;
399 }
400
408 bool setColor( int row, int column, QRgb color ) SIP_HOLDGIL
409 {
410 return setColor( static_cast< qgssize >( row ) * mWidth + column, color );
411 }
412
419 bool setColor( qgssize index, QRgb color ) SIP_HOLDGIL
420 {
421 if ( !mImage )
422 {
423 QgsDebugError( QStringLiteral( "Image not allocated" ) );
424 return false;
425 }
426
427 if ( index >= static_cast< qgssize >( mImage->width() ) * mImage->height() )
428 {
429 QgsDebugError( QStringLiteral( "index %1 out of range" ).arg( index ) );
430 return false;
431 }
432
433 // setPixel() is slow, see Qt doc -> use direct access
434 QRgb *bits = reinterpret_cast< QRgb * >( mImage->bits() );
435 bits[index] = color;
436 return true;
437 }
438
447 {
448 if ( !mImage )
449 return nullptr;
450 return reinterpret_cast< QRgb * >( mImage->bits() );
451 }
452
459 bool setIsNoData( int row, int column ) SIP_HOLDGIL
460 {
461 return setIsNoData( static_cast< qgssize >( row ) * mWidth + column );
462 }
463
470 {
471 if ( mHasNoDataValue )
472 {
473 return setValue( index, mNoDataValue );
474 }
475 else
476 {
477 if ( !mNoDataBitmap )
478 {
479 if ( !createNoDataBitmap() )
480 {
481 return false;
482 }
483 }
484 // TODO: optimize
485 const int row = static_cast< int >( index ) / mWidth;
486 const int column = index % mWidth;
487 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
488 const int bit = column % 8;
489 const int nodata = 0x80 >> bit;
490 //QgsDebugMsg ( QString("set byte = %1 bit = %2 no data by %3").arg(byte).arg(bit).arg(nodata, 0,2 ) );
491 mNoDataBitmap[byte] = mNoDataBitmap[byte] | nodata;
492 return true;
493 }
494 }
495
500 bool setIsNoData();
501
506 bool setIsNoDataExcept( QRect exceptRect );
507
516 void setIsData( int row, int column ) SIP_HOLDGIL
517 {
518 setIsData( static_cast< qgssize >( row )*mWidth + column );
519 }
520
529 {
530 if ( mHasNoDataValue )
531 {
532 //no data value set, so mNoDataBitmap is not being used
533 return;
534 }
535
536 if ( !mNoDataBitmap )
537 {
538 return;
539 }
540
541 // TODO: optimize
542 const int row = static_cast< int >( index ) / mWidth;
543 const int column = index % mWidth;
544 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
545 const int bit = column % 8;
546 const int nodata = 0x80 >> bit;
547 mNoDataBitmap[byte] = mNoDataBitmap[byte] & ~nodata;
548 }
549
550#ifndef SIP_RUN
559 bool fill( double value );
560#else
561
570 void fill( double value );
571 % MethodCode
572 if ( !QgsRasterBlock::typeIsNumeric( sipCpp->dataType() ) )
573 {
574 PyErr_SetString( PyExc_ValueError, QStringLiteral( "Cannot fill a block with %1 data type" ).arg( qgsEnumValueToKey( sipCpp->dataType() ) ).toUtf8().constData() );
575 sipIsErr = 1;
576 }
577 else if ( QgsRasterBlock::typeIsComplex( sipCpp->dataType() ) )
578 {
579 PyErr_SetString( PyExc_ValueError, QStringLiteral( "Cannot fill a block with %1 complex data type" ).arg( qgsEnumValueToKey( sipCpp->dataType() ) ).toUtf8().constData() );
580 sipIsErr = 1;
581 }
582 else if ( sipCpp->isEmpty() )
583 {
584 PyErr_SetString( PyExc_ValueError, QStringLiteral( "Cannot fill an empty block" ).toUtf8().constData() );
585 sipIsErr = 1;
586 }
587 else
588 {
589 sipCpp->fill( a0 );
590 }
591 % End
592#endif
593
602 QByteArray data() const;
603
612 void setData( const QByteArray &data, int offset = 0 );
613
620 char *bits( int row, int column ) SIP_SKIP;
621
629 char *bits( qgssize index ) SIP_SKIP;
630
637 char *bits() SIP_SKIP;
638
648 const char *constBits( qgssize index ) const SIP_SKIP;
649
657 const char *constBits() const SIP_SKIP;
658
666 static QString printValue( double value, bool localized = false );
667
676 static QString printValue( float value, bool localized = false ) SIP_SKIP;
677
683 bool convert( Qgis::DataType destDataType );
684
688 QImage image() const;
689
694 bool setImage( const QImage *image );
695
697 inline static double readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP;
698
700 inline static void writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP;
701
702 void applyNoDataValues( const QgsRasterRangeList &rangeList );
703
707 void applyScaleOffset( double scale, double offset );
708
710 QgsError error() const { return mError; }
711
713 void setError( const QgsError &error ) { mError = error;}
714
715 QString toString() const;
716
727 static QRect subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent );
728
733 int width() const SIP_HOLDGIL { return mWidth; }
734
739 int height() const SIP_HOLDGIL { return mHeight; }
740
758 bool minimum( double &minimum SIP_OUT, int &row SIP_OUT, int &column SIP_OUT ) const;
759
777 bool maximum( double &maximum SIP_OUT, int &row SIP_OUT, int &column SIP_OUT ) const;
778
801 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;
802
803 private:
804 static QImage::Format imageFormat( Qgis::DataType dataType );
805 static Qgis::DataType dataType( QImage::Format format );
806
813 static bool isNoDataValue( double value, double noDataValue )
814 {
815 // TODO: optimize no data value test by memcmp()
816 // More precise would be std::isnan(value) && std::isnan(noDataValue(bandNo)), but probably
817 // not important and slower
818 return std::isnan( value ) ||
819 qgsDoubleNear( value, noDataValue );
820 }
821
827 inline bool isNoDataValue( double value ) const;
828
833 bool createNoDataBitmap();
834
844 static void *convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size );
845
846 // Valid
847 bool mValid = true;
848
849 // Data type
851
852 // Data type size in bytes, to make bits() fast
853 int mTypeSize = 0;
854
855 // Width
856 int mWidth = 0;
857
858 // Height
859 int mHeight = 0;
860
861 // Has no data value
862 bool mHasNoDataValue = false;
863
864 // No data value
865 double mNoDataValue;
866
867 static const QRgb NO_DATA_COLOR;
868
869 // Data block for numerical data types, not used with image data types
870 // QByteArray does not seem to be intended for large data blocks, does it?
871 void *mData = nullptr;
872
873 // Image for image data types, not used with numerical data types
874 QImage *mImage = nullptr;
875
876 // Bitmap of no data. One bit for each pixel. Bit is 1 if a pixels is no data.
877 // Each row is represented by whole number of bytes (last bits may be unused)
878 // to make processing rows easy.
879 char *mNoDataBitmap = nullptr;
880
881 // number of bytes in mNoDataBitmap row
882 int mNoDataBitmapWidth = 0;
883
884 // total size in bytes of mNoDataBitmap
885 qgssize mNoDataBitmapSize = 0;
886
887 // Error
888 QgsError mError;
889};
890
891inline double QgsRasterBlock::readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP
892{
893 if ( !data )
894 {
895 return std::numeric_limits<double>::quiet_NaN();
896 }
897
898 switch ( type )
899 {
901 return static_cast< double >( ( static_cast< quint8 * >( data ) )[index] );
903 return static_cast< double >( ( static_cast< qint8 * >( data ) )[index] );
905 return static_cast< double >( ( static_cast< quint16 * >( data ) )[index] );
907 return static_cast< double >( ( static_cast< qint16 * >( data ) )[index] );
909 return static_cast< double >( ( static_cast< quint32 * >( data ) )[index] );
911 return static_cast< double >( ( static_cast< qint32 * >( data ) )[index] );
913 return static_cast< double >( ( static_cast< float * >( data ) )[index] );
915 return static_cast< double >( ( static_cast< double * >( data ) )[index] );
923 QgsDebugError( QStringLiteral( "Data type %1 is not supported" ).arg( qgsEnumValueToKey< Qgis::DataType >( type ) ) );
924 break;
925 }
926
927 return std::numeric_limits<double>::quiet_NaN();
928}
929
930inline void QgsRasterBlock::writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP
931{
932 if ( !data ) return;
933
934 switch ( type )
935 {
937 ( static_cast< quint8 * >( data ) )[index] = static_cast< quint8 >( value );
938 break;
940 ( static_cast< qint8 * >( data ) )[index] = static_cast< qint8 >( value );
941 break;
943 ( static_cast< quint16 * >( data ) )[index] = static_cast< quint16 >( value );
944 break;
946 ( static_cast< qint16 * >( data ) )[index] = static_cast< qint16 >( value );
947 break;
949 ( static_cast< quint32 * >( data ) )[index] = static_cast< quint32 >( value );
950 break;
952 ( static_cast< qint32 * >( data ) )[index] = static_cast< qint32 >( value );
953 break;
955 ( static_cast< float * >( data ) )[index] = static_cast< float >( value );
956 break;
958 ( static_cast< double * >( data ) )[index] = value;
959 break;
967 QgsDebugError( QStringLiteral( "Data type %1 is not supported" ).arg( qgsEnumValueToKey< Qgis::DataType >( type ) ) );
968 break;
969 }
970}
971
972inline double QgsRasterBlock::value( qgssize index ) const SIP_SKIP
973{
974 if ( !mData )
975 {
976 QgsDebugError( QStringLiteral( "Data block not allocated" ) );
977 return std::numeric_limits<double>::quiet_NaN();
978 }
979 return readValue( mData, mDataType, index );
980}
981
982inline double QgsRasterBlock::valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP
983{
984 if ( !mData )
985 {
986 QgsDebugError( QStringLiteral( "Data block not allocated" ) );
987 isNoData = true;
988 return std::numeric_limits<double>::quiet_NaN();
989 }
990 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
991 {
992 QgsDebugError( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
993 isNoData = true; // we consider no data if outside
994 return std::numeric_limits<double>::quiet_NaN();
995 }
996
997 const double val = readValue( mData, mDataType, index );
998
999 if ( !mHasNoDataValue && !mNoDataBitmap )
1000 {
1001 isNoData = false;
1002 return val;
1003 }
1004
1005 if ( mHasNoDataValue )
1006 {
1007 isNoData = isNoDataValue( val );
1008 return val;
1009 }
1010 // use no data bitmap
1011 if ( !mNoDataBitmap )
1012 {
1013 // no data are not defined
1014 isNoData = false;
1015 return val;
1016 }
1017
1018 // no data is a bitmap
1019 isNoData = QgsRasterBlock::isNoData( index );
1020 return val;
1021}
1022
1023inline bool QgsRasterBlock::isNoDataValue( double value ) const SIP_SKIP
1024{
1025 return std::isnan( value ) || qgsDoubleNear( value, mNoDataValue );
1026}
1027
1028#endif
1029
1030
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:54
DataType
Raster data types.
Definition qgis.h:351
@ CInt32
Complex Int32.
@ Float32
Thirty two bit floating point (float)
@ CFloat64
Complex Float64.
@ Int16
Sixteen bit signed integer (qint16)
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30)
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ UnknownDataType
Unknown or unspecified type.
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
@ Int32
Thirty two bit signed integer (qint32)
@ Float64
Sixty four bit floating point (double)
@ CFloat32
Complex Float32.
@ CInt16
Complex Int16.
@ UInt32
Thirty two bit unsigned integer (quint32)
A container for error messages.
Definition qgserror.h:81
Raster data container.
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.
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.
bool setIsNoData(qgssize index)
Set no data on pixel.
bool setValue(qgssize index, double value)
Set value on index (indexed line by line)
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.
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.
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:6493
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:6806
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6302
#define SIP_SKIP
Definition qgis_sip.h:126
#define SIP_OUT
Definition qgis_sip.h:58
#define SIP_HOLDGIL
Definition qgis_sip.h:171
#define QgsDebugError(str)
Definition qgslogger.h:40
QList< QgsRasterRange > QgsRasterRangeList