QGIS API Documentation 4.3.0-Master (0978c174f8e)
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
46 QgsRasterBlock( const QgsRasterBlock &rh ) = delete;
47 QgsRasterBlock &operator=( const QgsRasterBlock &rh ) = delete;
48
56
57 virtual ~QgsRasterBlock();
58
66 bool reset( Qgis::DataType dataType, int width, int height );
67
68 // TODO: consider if use isValid() at all, isEmpty() should be sufficient
69 // and works also if block is valid but empty - difference between valid and empty?
70
71 // clang-format off
77 bool isValid() const SIP_HOLDGIL { return mValid; }
78 // clang-format on
79
81 void setValid( bool valid ) SIP_HOLDGIL { mValid = valid; }
82
88 bool isEmpty() const;
89
94 {
95 // Modified and extended copy from GDAL
96 switch ( dataType )
97 {
100 return 1;
101
104 return 2;
105
110 return 4;
111
115 return 8;
116
118 return 16;
119
122 return 4;
123
125 break;
126 }
127 return 0;
128 }
129
134 {
135 return typeSize( mDataType );
136 }
137
144 static bool typeIsNumeric( Qgis::DataType type );
145
153 static bool typeIsComplex( Qgis::DataType type );
154
161 static bool typeIsColor( Qgis::DataType type );
162
164 Qgis::DataType dataType() const SIP_HOLDGIL { return mDataType; }
165
167 static Qgis::DataType typeWithNoDataValue( Qgis::DataType dataType, double *noDataValue );
168
176 bool hasNoDataValue() const SIP_HOLDGIL { return mHasNoDataValue; }
177
185 {
186 return mHasNoDataValue || mNoDataBitmap;
187 }
188
195 void setNoDataValue( double noDataValue ) SIP_HOLDGIL;
196
204 void resetNoDataValue() SIP_HOLDGIL;
205
214 double noDataValue() const SIP_HOLDGIL { return mNoDataValue; }
215
222 static QByteArray valueBytes( Qgis::DataType dataType, double value );
223
232 double value( int row, int column ) const SIP_HOLDGIL
233 {
234 return value( static_cast< qgssize >( row ) * mWidth + column );
235 }
236
249 double valueAndNoData( int row, int column, bool &isNoData ) const SIP_SKIP
250 {
251 return valueAndNoData( static_cast< qgssize >( row ) * mWidth + column, isNoData );
252 }
253
261 inline double value( qgssize index ) const SIP_HOLDGIL;
262
275 inline double valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP;
276
284 const quint8 *byteData() const SIP_SKIP
285 {
286 if ( mDataType != Qgis::DataType::Byte )
287 return nullptr;
288 return static_cast< const quint8 * >( mData );
289 }
290
297 QRgb color( int row, int column ) const SIP_HOLDGIL
298 {
299 if ( !mImage ) return NO_DATA_COLOR;
300
301 return mImage->pixel( column, row );
302 }
303
309 QRgb color( qgssize index ) const SIP_HOLDGIL
310 {
311 const int row = static_cast< int >( std::floor( static_cast< double >( index ) / mWidth ) );
312 const int column = index % mWidth;
313 return color( row, column );
314 }
315
323 bool isNoData( int row, int column ) const SIP_HOLDGIL
324 {
325 return isNoData( static_cast< qgssize >( row ) * mWidth + column );
326 }
327
335 bool isNoData( qgssize row, qgssize column ) const SIP_HOLDGIL
336 {
337 return isNoData( row * static_cast< qgssize >( mWidth ) + column );
338 }
339
346 bool isNoData( qgssize index ) const SIP_HOLDGIL
347 {
348 if ( !mHasNoDataValue && !mNoDataBitmap )
349 return false;
350 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
351 {
352 QgsDebugError( u"Index %1 out of range (%2 x %3)"_s.arg( index ).arg( mWidth ).arg( mHeight ) );
353 return true; // we consider no data if outside
354 }
355 if ( mHasNoDataValue )
356 {
357 const double value = readValue( mData, mDataType, index );
358 return isNoDataValue( value );
359 }
360 // use no data bitmap
361 if ( !mNoDataBitmap )
362 {
363 // no data are not defined
364 return false;
365 }
366 // TODO: optimize
367 const int row = static_cast< int >( index ) / mWidth;
368 const int column = index % mWidth;
369 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
370 const int bit = column % 8;
371 const int mask = 0x80 >> bit;
372 //int x = mNoDataBitmap[byte] & mask;
373 //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) ) );
374 return mNoDataBitmap[byte] & mask;
375 }
376
384 bool setValue( int row, int column, double value ) SIP_HOLDGIL
385 {
386 return setValue( static_cast< qgssize >( row ) * mWidth + column, value );
387 }
388
395 bool setValue( qgssize index, double value ) SIP_HOLDGIL
396 {
397 if ( !mData )
398 {
399 QgsDebugError( u"Data block not allocated"_s );
400 return false;
401 }
402 if ( index >= static_cast< qgssize >( mWidth ) *mHeight )
403 {
404 QgsDebugError( u"Index %1 out of range (%2 x %3)"_s.arg( index ).arg( mWidth ).arg( mHeight ) );
405 return false;
406 }
407 writeValue( mData, mDataType, index, value );
408 return true;
409 }
410
418 bool setColor( int row, int column, QRgb color ) SIP_HOLDGIL
419 {
420 return setColor( static_cast< qgssize >( row ) * mWidth + column, color );
421 }
422
429 bool setColor( qgssize index, QRgb color ) SIP_HOLDGIL
430 {
431 if ( !mImage )
432 {
433 QgsDebugError( u"Image not allocated"_s );
434 return false;
435 }
436
437 if ( index >= static_cast< qgssize >( mImage->width() ) * mImage->height() )
438 {
439 QgsDebugError( u"index %1 out of range"_s.arg( index ) );
440 return false;
441 }
442
443 // setPixel() is slow, see Qt doc -> use direct access
444 QRgb *bits = reinterpret_cast< QRgb * >( mImage->bits() );
445 bits[index] = color;
446 return true;
447 }
448
457 {
458 if ( !mImage )
459 return nullptr;
460 return reinterpret_cast< QRgb * >( mImage->bits() );
461 }
462
469 bool setIsNoData( int row, int column ) SIP_HOLDGIL
470 {
471 return setIsNoData( static_cast< qgssize >( row ) * mWidth + column );
472 }
473
480 {
481 if ( mHasNoDataValue )
482 {
483 return setValue( index, mNoDataValue );
484 }
485 else
486 {
487 if ( !mNoDataBitmap )
488 {
489 if ( !createNoDataBitmap() )
490 {
491 return false;
492 }
493 }
494 // TODO: optimize
495 const int row = static_cast< int >( index ) / mWidth;
496 const int column = index % mWidth;
497 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
498 const int bit = column % 8;
499 const int nodata = 0x80 >> bit;
500 //QgsDebugMsg ( QString("set byte = %1 bit = %2 no data by %3").arg(byte).arg(bit).arg(nodata, 0,2 ) );
501 mNoDataBitmap[byte] = mNoDataBitmap[byte] | nodata;
502 return true;
503 }
504 }
505
510 bool setIsNoData();
511
516 bool setIsNoDataExcept( QRect exceptRect );
517
526 void setIsData( int row, int column ) SIP_HOLDGIL
527 {
528 setIsData( static_cast< qgssize >( row )*mWidth + column );
529 }
530
539 {
540 if ( mHasNoDataValue )
541 {
542 //no data value set, so mNoDataBitmap is not being used
543 return;
544 }
545
546 if ( !mNoDataBitmap )
547 {
548 return;
549 }
550
551 // TODO: optimize
552 const int row = static_cast< int >( index ) / mWidth;
553 const int column = index % mWidth;
554 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
555 const int bit = column % 8;
556 const int nodata = 0x80 >> bit;
557 mNoDataBitmap[byte] = mNoDataBitmap[byte] & ~nodata;
558 }
559
560#ifndef SIP_RUN
569 bool fill( double value );
570#else
571// clang-format off
572
581 void fill( double value );
582 % MethodCode
583 if ( !QgsRasterBlock::typeIsNumeric( sipCpp->dataType() ) )
584 {
585 PyErr_SetString( PyExc_ValueError, u"Cannot fill a block with %1 data type"_s.arg( qgsEnumValueToKey( sipCpp->dataType() ) ).toUtf8().constData() );
586 sipIsErr = 1;
587 }
588 else if ( QgsRasterBlock::typeIsComplex( sipCpp->dataType() ) )
589 {
590 PyErr_SetString( PyExc_ValueError, u"Cannot fill a block with %1 complex data type"_s.arg( qgsEnumValueToKey( sipCpp->dataType() ) ).toUtf8().constData() );
591 sipIsErr = 1;
592 }
593 else if ( sipCpp->isEmpty() )
594 {
595 PyErr_SetString( PyExc_ValueError, u"Cannot fill an empty block"_s.toUtf8().constData() );
596 sipIsErr = 1;
597 }
598 else
599 {
600 sipCpp->fill( a0 );
601 }
602 % End
603// clang-format on
604#endif
605
614 QByteArray data() const;
615
624 void setData( const QByteArray &data, int offset = 0 );
625
632 char *bits( int row, int column ) SIP_SKIP;
633
641 char *bits( qgssize index ) SIP_SKIP;
642
649 char *bits() SIP_SKIP;
650
660 const char *constBits( qgssize index ) const SIP_SKIP;
661
669 const char *constBits() const SIP_SKIP;
670
678 static QString printValue( double value, bool localized = false );
679
688 static QString printValue( float value, bool localized = false ) SIP_SKIP;
689
695 bool convert( Qgis::DataType destDataType );
696
700 QImage image() const;
701
706 bool setImage( const QImage *image );
707
709 inline static double readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP;
710
712 inline static void writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP;
713
714 void applyNoDataValues( const QgsRasterRangeList &rangeList );
715
719 void applyScaleOffset( double scale, double offset );
720
722 QgsError error() const { return mError; }
723
725 void setError( const QgsError &error ) { mError = error;}
726
727 QString toString() const;
728
739 static QRect subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent );
740
745 int width() const SIP_HOLDGIL { return mWidth; }
746
751 int height() const SIP_HOLDGIL { return mHeight; }
752
770 bool minimum( double &minimum SIP_OUT, int &row SIP_OUT, int &column SIP_OUT ) const;
771
789 bool maximum( double &maximum SIP_OUT, int &row SIP_OUT, int &column SIP_OUT ) const;
790
813 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;
814
815 private:
816
817#ifdef SIP_RUN
818 QgsRasterBlock( const QgsRasterBlock &rh );
819#endif
820
821 static QImage::Format imageFormat( Qgis::DataType dataType );
822 static Qgis::DataType dataType( QImage::Format format );
823
830 static bool isNoDataValue( double value, double noDataValue )
831 {
832 // TODO: optimize no data value test by memcmp()
833 // More precise would be std::isnan(value) && std::isnan(noDataValue(bandNo)), but probably
834 // not important and slower
835 return std::isnan( value ) ||
836 qgsDoubleNear( value, noDataValue );
837 }
838
844 inline bool isNoDataValue( double value ) const;
845
850 bool createNoDataBitmap();
851
861 static void *convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size );
862
863 // Valid
864 bool mValid = true;
865
866 // Data type
868
869 // Data type size in bytes, to make bits() fast
870 int mTypeSize = 0;
871
872 // Width
873 int mWidth = 0;
874
875 // Height
876 int mHeight = 0;
877
878 // Has no data value
879 bool mHasNoDataValue = false;
880
881 // No data value
882 double mNoDataValue;
883
884 static const QRgb NO_DATA_COLOR;
885
886 // Data block for numerical data types, not used with image data types
887 // QByteArray does not seem to be intended for large data blocks, does it?
888 void *mData = nullptr;
889
890 // Image for image data types, not used with numerical data types
891 std::unique_ptr<QImage> mImage;
892
893 // Bitmap of no data. One bit for each pixel. Bit is 1 if a pixels is no data.
894 // Each row is represented by whole number of bytes (last bits may be unused)
895 // to make processing rows easy.
896 char *mNoDataBitmap = nullptr;
897
898 // number of bytes in mNoDataBitmap row
899 int mNoDataBitmapWidth = 0;
900
901 // total size in bytes of mNoDataBitmap
902 qgssize mNoDataBitmapSize = 0;
903
904 // Error
905 QgsError mError;
906};
907
909{
910 if ( !data )
911 {
912 return std::numeric_limits<double>::quiet_NaN();
913 }
914
915 switch ( type )
916 {
918 return static_cast< double >( ( static_cast< quint8 * >( data ) )[index] );
920 return static_cast< double >( ( static_cast< qint8 * >( data ) )[index] );
922 return static_cast< double >( ( static_cast< quint16 * >( data ) )[index] );
924 return static_cast< double >( ( static_cast< qint16 * >( data ) )[index] );
926 return static_cast< double >( ( static_cast< quint32 * >( data ) )[index] );
928 return static_cast< double >( ( static_cast< qint32 * >( data ) )[index] );
930 return static_cast< double >( ( static_cast< float * >( data ) )[index] );
932 return static_cast< double >( ( static_cast< double * >( data ) )[index] );
940 QgsDebugError( u"Data type %1 is not supported"_s.arg( qgsEnumValueToKey< Qgis::DataType >( type ) ) );
941 break;
942 }
943
944 return std::numeric_limits<double>::quiet_NaN();
945}
946
947inline void QgsRasterBlock::writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP
948{
949 if ( !data ) return;
950
951 switch ( type )
952 {
954 ( static_cast< quint8 * >( data ) )[index] = static_cast< quint8 >( value );
955 break;
957 ( static_cast< qint8 * >( data ) )[index] = static_cast< qint8 >( value );
958 break;
960 ( static_cast< quint16 * >( data ) )[index] = static_cast< quint16 >( value );
961 break;
963 ( static_cast< qint16 * >( data ) )[index] = static_cast< qint16 >( value );
964 break;
966 ( static_cast< quint32 * >( data ) )[index] = static_cast< quint32 >( value );
967 break;
969 ( static_cast< qint32 * >( data ) )[index] = static_cast< qint32 >( value );
970 break;
972 ( static_cast< float * >( data ) )[index] = static_cast< float >( value );
973 break;
975 ( static_cast< double * >( data ) )[index] = value;
976 break;
984 QgsDebugError( u"Data type %1 is not supported"_s.arg( qgsEnumValueToKey< Qgis::DataType >( type ) ) );
985 break;
986 }
987}
988
989inline double QgsRasterBlock::value( qgssize index ) const SIP_SKIP
990{
991 if ( !mData )
992 {
993 QgsDebugError( u"Data block not allocated"_s );
994 return std::numeric_limits<double>::quiet_NaN();
995 }
996 return readValue( mData, mDataType, index );
997}
998
999inline double QgsRasterBlock::valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP
1000{
1001 if ( !mData )
1002 {
1003 QgsDebugError( u"Data block not allocated"_s );
1004 isNoData = true;
1005 return std::numeric_limits<double>::quiet_NaN();
1006 }
1007 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
1008 {
1009 QgsDebugError( u"Index %1 out of range (%2 x %3)"_s.arg( index ).arg( mWidth ).arg( mHeight ) );
1010 isNoData = true; // we consider no data if outside
1011 return std::numeric_limits<double>::quiet_NaN();
1012 }
1013
1014 const double val = readValue( mData, mDataType, index );
1015
1016 if ( !mHasNoDataValue && !mNoDataBitmap )
1017 {
1018 isNoData = false;
1019 return val;
1020 }
1021
1022 if ( mHasNoDataValue )
1023 {
1024 isNoData = isNoDataValue( val );
1025 return val;
1026 }
1027 // use no data bitmap
1028 if ( !mNoDataBitmap )
1029 {
1030 // no data are not defined
1031 isNoData = false;
1032 return val;
1033 }
1034
1035 // no data is a bitmap
1037 return val;
1038}
1039
1040inline bool QgsRasterBlock::isNoDataValue( double value ) const SIP_SKIP
1041{
1042 return std::isnan( value ) || qgsDoubleNear( value, mNoDataValue );
1043}
1044
1045#endif
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
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.
QgsRasterBlock(const QgsRasterBlock &rh)=delete
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.
QgsRasterBlock & operator=(const QgsRasterBlock &rh)=delete
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:7653
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:7996
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7417
#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:71
QList< QgsRasterRange > QgsRasterRangeList