QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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
129 static bool typeIsNumeric( Qgis::DataType type );
130
132 static bool typeIsColor( Qgis::DataType type );
133
135 Qgis::DataType dataType() const SIP_HOLDGIL { return mDataType; }
136
138 static Qgis::DataType typeWithNoDataValue( Qgis::DataType dataType, double *noDataValue );
139
145 bool hasNoDataValue() const SIP_HOLDGIL { return mHasNoDataValue; }
146
154 {
155 return mHasNoDataValue || mNoDataBitmap;
156 }
157
163 void setNoDataValue( double noDataValue ) SIP_HOLDGIL;
164
171 void resetNoDataValue() SIP_HOLDGIL;
172
179 double noDataValue() const SIP_HOLDGIL { return mNoDataValue; }
180
187 static QByteArray valueBytes( Qgis::DataType dataType, double value );
188
197 double value( int row, int column ) const SIP_HOLDGIL
198 {
199 return value( static_cast< qgssize >( row ) * mWidth + column );
200 }
201
214 double valueAndNoData( int row, int column, bool &isNoData ) const SIP_SKIP
215 {
216 return valueAndNoData( static_cast< qgssize >( row ) * mWidth + column, isNoData );
217 }
218
226 inline double value( qgssize index ) const SIP_HOLDGIL;
227
240 inline double valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP;
241
249 const quint8 *byteData() const SIP_SKIP
250 {
251 if ( mDataType != Qgis::DataType::Byte )
252 return nullptr;
253 return static_cast< const quint8 * >( mData );
254 }
255
262 QRgb color( int row, int column ) const SIP_HOLDGIL
263 {
264 if ( !mImage ) return NO_DATA_COLOR;
265
266 return mImage->pixel( column, row );
267 }
268
274 QRgb color( qgssize index ) const SIP_HOLDGIL
275 {
276 const int row = static_cast< int >( std::floor( static_cast< double >( index ) / mWidth ) );
277 const int column = index % mWidth;
278 return color( row, column );
279 }
280
288 bool isNoData( int row, int column ) const SIP_HOLDGIL
289 {
290 return isNoData( static_cast< qgssize >( row ) * mWidth + column );
291 }
292
300 bool isNoData( qgssize row, qgssize column ) const SIP_HOLDGIL
301 {
302 return isNoData( row * static_cast< qgssize >( mWidth ) + column );
303 }
304
311 bool isNoData( qgssize index ) const SIP_HOLDGIL
312 {
313 if ( !mHasNoDataValue && !mNoDataBitmap )
314 return false;
315 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
316 {
317 QgsDebugMsg( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
318 return true; // we consider no data if outside
319 }
320 if ( mHasNoDataValue )
321 {
322 const double value = readValue( mData, mDataType, index );
323 return isNoDataValue( value );
324 }
325 // use no data bitmap
326 if ( !mNoDataBitmap )
327 {
328 // no data are not defined
329 return false;
330 }
331 // TODO: optimize
332 const int row = static_cast< int >( index ) / mWidth;
333 const int column = index % mWidth;
334 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
335 const int bit = column % 8;
336 const int mask = 0x80 >> bit;
337 //int x = mNoDataBitmap[byte] & mask;
338 //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) ) );
339 return mNoDataBitmap[byte] & mask;
340 }
341
349 bool setValue( int row, int column, double value ) SIP_HOLDGIL
350 {
351 return setValue( static_cast< qgssize >( row ) * mWidth + column, value );
352 }
353
360 bool setValue( qgssize index, double value ) SIP_HOLDGIL
361 {
362 if ( !mData )
363 {
364 QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
365 return false;
366 }
367 if ( index >= static_cast< qgssize >( mWidth ) *mHeight )
368 {
369 QgsDebugMsg( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
370 return false;
371 }
372 writeValue( mData, mDataType, index, value );
373 return true;
374 }
375
383 bool setColor( int row, int column, QRgb color ) SIP_HOLDGIL
384 {
385 return setColor( static_cast< qgssize >( row ) * mWidth + column, color );
386 }
387
394 bool setColor( qgssize index, QRgb color ) SIP_HOLDGIL
395 {
396 if ( !mImage )
397 {
398 QgsDebugMsg( QStringLiteral( "Image not allocated" ) );
399 return false;
400 }
401
402 if ( index >= static_cast< qgssize >( mImage->width() ) * mImage->height() )
403 {
404 QgsDebugMsg( QStringLiteral( "index %1 out of range" ).arg( index ) );
405 return false;
406 }
407
408 // setPixel() is slow, see Qt doc -> use direct access
409 QRgb *bits = reinterpret_cast< QRgb * >( mImage->bits() );
410 bits[index] = color;
411 return true;
412 }
413
422 {
423 if ( !mImage )
424 return nullptr;
425 return reinterpret_cast< QRgb * >( mImage->bits() );
426 }
427
434 bool setIsNoData( int row, int column ) SIP_HOLDGIL
435 {
436 return setIsNoData( static_cast< qgssize >( row ) * mWidth + column );
437 }
438
445 {
446 if ( mHasNoDataValue )
447 {
448 return setValue( index, mNoDataValue );
449 }
450 else
451 {
452 if ( !mNoDataBitmap )
453 {
454 if ( !createNoDataBitmap() )
455 {
456 return false;
457 }
458 }
459 // TODO: optimize
460 const int row = static_cast< int >( index ) / mWidth;
461 const int column = index % mWidth;
462 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
463 const int bit = column % 8;
464 const int nodata = 0x80 >> bit;
465 //QgsDebugMsg ( QString("set byte = %1 bit = %2 no data by %3").arg(byte).arg(bit).arg(nodata, 0,2 ) );
466 mNoDataBitmap[byte] = mNoDataBitmap[byte] | nodata;
467 return true;
468 }
469 }
470
475 bool setIsNoData();
476
481 bool setIsNoDataExcept( QRect exceptRect );
482
492 void setIsData( int row, int column ) SIP_HOLDGIL
493 {
494 setIsData( static_cast< qgssize >( row )*mWidth + column );
495 }
496
506 {
507 if ( mHasNoDataValue )
508 {
509 //no data value set, so mNoDataBitmap is not being used
510 return;
511 }
512
513 if ( !mNoDataBitmap )
514 {
515 return;
516 }
517
518 // TODO: optimize
519 const int row = static_cast< int >( index ) / mWidth;
520 const int column = index % mWidth;
521 const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
522 const int bit = column % 8;
523 const int nodata = 0x80 >> bit;
524 mNoDataBitmap[byte] = mNoDataBitmap[byte] & ~nodata;
525 }
526
536 QByteArray data() const;
537
547 void setData( const QByteArray &data, int offset = 0 );
548
555 char *bits( int row, int column ) SIP_SKIP;
556
562 char *bits( qgssize index ) SIP_SKIP;
563
568 char *bits() SIP_SKIP;
569
576 static QString printValue( double value );
577
586 static QString printValue( float value ) SIP_SKIP;
587
593 bool convert( Qgis::DataType destDataType );
594
598 QImage image() const;
599
604 bool setImage( const QImage *image );
605
607 inline static double readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP;
608
610 inline static void writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP;
611
612 void applyNoDataValues( const QgsRasterRangeList &rangeList );
613
618 void applyScaleOffset( double scale, double offset );
619
621 QgsError error() const { return mError; }
622
624 void setError( const QgsError &error ) { mError = error;}
625
626 QString toString() const;
627
638 static QRect subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent );
639
645 int width() const SIP_HOLDGIL { return mWidth; }
646
652 int height() const SIP_HOLDGIL { return mHeight; }
653
654 private:
655 static QImage::Format imageFormat( Qgis::DataType dataType );
656 static Qgis::DataType dataType( QImage::Format format );
657
664 static bool isNoDataValue( double value, double noDataValue )
665 {
666 // TODO: optimize no data value test by memcmp()
667 // More precise would be std::isnan(value) && std::isnan(noDataValue(bandNo)), but probably
668 // not important and slower
669 return std::isnan( value ) ||
670 qgsDoubleNear( value, noDataValue );
671 }
672
678 inline bool isNoDataValue( double value ) const;
679
684 bool createNoDataBitmap();
685
695 static void *convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size );
696
697 // Valid
698 bool mValid = true;
699
700 // Data type
702
703 // Data type size in bytes, to make bits() fast
704 int mTypeSize = 0;
705
706 // Width
707 int mWidth = 0;
708
709 // Height
710 int mHeight = 0;
711
712 // Has no data value
713 bool mHasNoDataValue = false;
714
715 // No data value
716 double mNoDataValue;
717
718 static const QRgb NO_DATA_COLOR;
719
720 // Data block for numerical data types, not used with image data types
721 // QByteArray does not seem to be intended for large data blocks, does it?
722 void *mData = nullptr;
723
724 // Image for image data types, not used with numerical data types
725 QImage *mImage = nullptr;
726
727 // Bitmap of no data. One bit for each pixel. Bit is 1 if a pixels is no data.
728 // Each row is represented by whole number of bytes (last bits may be unused)
729 // to make processing rows easy.
730 char *mNoDataBitmap = nullptr;
731
732 // number of bytes in mNoDataBitmap row
733 int mNoDataBitmapWidth = 0;
734
735 // total size in bytes of mNoDataBitmap
736 qgssize mNoDataBitmapSize = 0;
737
738 // Error
739 QgsError mError;
740};
741
742inline double QgsRasterBlock::readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP
743{
744 if ( !data )
745 {
746 return std::numeric_limits<double>::quiet_NaN();
747 }
748
749 switch ( type )
750 {
752 return static_cast< double >( ( static_cast< quint8 * >( data ) )[index] );
754 return static_cast< double >( ( static_cast< qint8 * >( data ) )[index] );
756 return static_cast< double >( ( static_cast< quint16 * >( data ) )[index] );
758 return static_cast< double >( ( static_cast< qint16 * >( data ) )[index] );
760 return static_cast< double >( ( static_cast< quint32 * >( data ) )[index] );
762 return static_cast< double >( ( static_cast< qint32 * >( data ) )[index] );
764 return static_cast< double >( ( static_cast< float * >( data ) )[index] );
766 return static_cast< double >( ( static_cast< double * >( data ) )[index] );
774 QgsDebugMsg( QStringLiteral( "Data type %1 is not supported" ).arg( qgsEnumValueToKey< Qgis::DataType >( type ) ) );
775 break;
776 }
777
778 return std::numeric_limits<double>::quiet_NaN();
779}
780
781inline void QgsRasterBlock::writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP
782{
783 if ( !data ) return;
784
785 switch ( type )
786 {
788 ( static_cast< quint8 * >( data ) )[index] = static_cast< quint8 >( value );
789 break;
791 ( static_cast< qint8 * >( data ) )[index] = static_cast< qint8 >( value );
792 break;
794 ( static_cast< quint16 * >( data ) )[index] = static_cast< quint16 >( value );
795 break;
797 ( static_cast< qint16 * >( data ) )[index] = static_cast< qint16 >( value );
798 break;
800 ( static_cast< quint32 * >( data ) )[index] = static_cast< quint32 >( value );
801 break;
803 ( static_cast< qint32 * >( data ) )[index] = static_cast< qint32 >( value );
804 break;
806 ( static_cast< float * >( data ) )[index] = static_cast< float >( value );
807 break;
809 ( static_cast< double * >( data ) )[index] = value;
810 break;
818 QgsDebugMsg( QStringLiteral( "Data type %1 is not supported" ).arg( qgsEnumValueToKey< Qgis::DataType >( type ) ) );
819 break;
820 }
821}
822
823inline double QgsRasterBlock::value( qgssize index ) const SIP_SKIP
824{
825 if ( !mData )
826 {
827 QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
828 return std::numeric_limits<double>::quiet_NaN();
829 }
830 return readValue( mData, mDataType, index );
831}
832
833inline double QgsRasterBlock::valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP
834{
835 if ( !mData )
836 {
837 QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
838 isNoData = true;
839 return std::numeric_limits<double>::quiet_NaN();
840 }
841 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
842 {
843 QgsDebugMsg( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
844 isNoData = true; // we consider no data if outside
845 return std::numeric_limits<double>::quiet_NaN();
846 }
847
848 const double val = readValue( mData, mDataType, index );
849
850 if ( !mHasNoDataValue && !mNoDataBitmap )
851 {
852 isNoData = false;
853 return val;
854 }
855
856 if ( mHasNoDataValue )
857 {
858 isNoData = isNoDataValue( val );
859 return val;
860 }
861 // use no data bitmap
862 if ( !mNoDataBitmap )
863 {
864 // no data are not defined
865 isNoData = false;
866 return val;
867 }
868
869 // no data is a bitmap
870 isNoData = QgsRasterBlock::isNoData( index );
871 return val;
872}
873
874inline bool QgsRasterBlock::isNoDataValue( double value ) const SIP_SKIP
875{
876 return std::isnan( value ) || qgsDoubleNear( value, mNoDataValue );
877}
878
879#endif
880
881
The Qgis class provides global constants for use throughout the application.
Definition: qgis.h:55
DataType
Raster data types.
Definition: qgis.h:242
@ 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)
QgsError is container for error messages (report).
Definition: qgserror.h:81
Raster data container.
void setIsData(qgssize index) SIP_HOLDGIL
Remove no data flag on pixel.
bool setValue(qgssize index, double value) SIP_HOLDGIL
Set value on index (indexed line by line)
void setValid(bool valid) SIP_HOLDGIL
Mark block as valid or invalid.
int width() const SIP_HOLDGIL
Returns the width (number of columns) of the raster block.
static int typeSize(Qgis::DataType dataType) SIP_HOLDGIL
Returns the size in bytes for the specified dataType.
bool isValid() const SIP_HOLDGIL
Returns true if the block is valid (correctly filled with 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.
Qgis::DataType dataType() const SIP_HOLDGIL
Returns data type.
bool isNoData(qgssize row, qgssize column) const SIP_HOLDGIL
Check if value at position is no data.
QRgb * colorData()
Gives direct read/write access to the raster RGB data.
double value(int row, int column) const SIP_HOLDGIL
Read a single value if type of block is numeric.
QRgb color(int row, int column) const SIP_HOLDGIL
Read a single color.
int dataTypeSize() const SIP_HOLDGIL
Data type size in bytes.
bool hasNoDataValue() const SIP_HOLDGIL
true if the block has no data value.
const quint8 * byteData() const
Gives direct access to the raster block data.
bool setColor(qgssize index, QRgb color) SIP_HOLDGIL
Set color on index (indexed line by line)
bool setColor(int row, int column, QRgb color) SIP_HOLDGIL
Set color on position.
static void writeValue(void *data, Qgis::DataType type, qgssize index, double value)
bool setIsNoData(qgssize index) SIP_HOLDGIL
Set no data on pixel.
bool isNoData(int row, int column) const SIP_HOLDGIL
Checks if value at position is no data.
bool isNoData(qgssize index) const SIP_HOLDGIL
Check if value at position is no data.
bool hasNoData() const SIP_HOLDGIL
Returns true if the block may contain no data.
bool setValue(int row, int column, double value) SIP_HOLDGIL
Set value on position.
void setError(const QgsError &error)
Sets the last error.
int height() const SIP_HOLDGIL
Returns the height (number of rows) of the raster block.
static double readValue(void *data, Qgis::DataType type, qgssize index)
bool setIsNoData(int row, int column) SIP_HOLDGIL
Set no data on pixel.
QRgb color(qgssize index) const SIP_HOLDGIL
Read a single value.
void setIsData(int row, int column) SIP_HOLDGIL
Remove no data flag on pixel.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
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:4064
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3509
#define SIP_SKIP
Definition: qgis_sip.h:126
#define SIP_HOLDGIL
Definition: qgis_sip.h:166
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QList< QgsRasterRange > QgsRasterRangeList