QGIS API Documentation  3.37.0-Master (a5b4d9743e8)
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 
30 class QgsRectangle;
31 
36 class 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 
153  bool hasNoData() const SIP_HOLDGIL
154  {
155  return mHasNoDataValue || mNoDataBitmap;
156  }
157 
162  void setNoDataValue( double noDataValue ) SIP_HOLDGIL;
163 
169  void resetNoDataValue() SIP_HOLDGIL;
170 
177  double noDataValue() const SIP_HOLDGIL { return mNoDataValue; }
178 
185  static QByteArray valueBytes( Qgis::DataType dataType, double value );
186 
195  double value( int row, int column ) const SIP_HOLDGIL
196  {
197  return value( static_cast< qgssize >( row ) * mWidth + column );
198  }
199 
212  double valueAndNoData( int row, int column, bool &isNoData ) const SIP_SKIP
213  {
214  return valueAndNoData( static_cast< qgssize >( row ) * mWidth + column, isNoData );
215  }
216 
224  inline double value( qgssize index ) const SIP_HOLDGIL;
225 
238  inline double valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP;
239 
247  const quint8 *byteData() const SIP_SKIP
248  {
249  if ( mDataType != Qgis::DataType::Byte )
250  return nullptr;
251  return static_cast< const quint8 * >( mData );
252  }
253 
260  QRgb color( int row, int column ) const SIP_HOLDGIL
261  {
262  if ( !mImage ) return NO_DATA_COLOR;
263 
264  return mImage->pixel( column, row );
265  }
266 
272  QRgb color( qgssize index ) const SIP_HOLDGIL
273  {
274  const int row = static_cast< int >( std::floor( static_cast< double >( index ) / mWidth ) );
275  const int column = index % mWidth;
276  return color( row, column );
277  }
278 
286  bool isNoData( int row, int column ) const SIP_HOLDGIL
287  {
288  return isNoData( static_cast< qgssize >( row ) * mWidth + column );
289  }
290 
298  bool isNoData( qgssize row, qgssize column ) const SIP_HOLDGIL
299  {
300  return isNoData( row * static_cast< qgssize >( mWidth ) + column );
301  }
302 
309  bool isNoData( qgssize index ) const SIP_HOLDGIL
310  {
311  if ( !mHasNoDataValue && !mNoDataBitmap )
312  return false;
313  if ( index >= static_cast< qgssize >( mWidth )*mHeight )
314  {
315  QgsDebugError( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
316  return true; // we consider no data if outside
317  }
318  if ( mHasNoDataValue )
319  {
320  const double value = readValue( mData, mDataType, index );
321  return isNoDataValue( value );
322  }
323  // use no data bitmap
324  if ( !mNoDataBitmap )
325  {
326  // no data are not defined
327  return false;
328  }
329  // TODO: optimize
330  const int row = static_cast< int >( index ) / mWidth;
331  const int column = index % mWidth;
332  const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
333  const int bit = column % 8;
334  const int mask = 0x80 >> bit;
335  //int x = mNoDataBitmap[byte] & mask;
336  //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) ) );
337  return mNoDataBitmap[byte] & mask;
338  }
339 
347  bool setValue( int row, int column, double value ) SIP_HOLDGIL
348  {
349  return setValue( static_cast< qgssize >( row ) * mWidth + column, value );
350  }
351 
358  bool setValue( qgssize index, double value ) SIP_HOLDGIL
359  {
360  if ( !mData )
361  {
362  QgsDebugError( QStringLiteral( "Data block not allocated" ) );
363  return false;
364  }
365  if ( index >= static_cast< qgssize >( mWidth ) *mHeight )
366  {
367  QgsDebugError( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
368  return false;
369  }
370  writeValue( mData, mDataType, index, value );
371  return true;
372  }
373 
381  bool setColor( int row, int column, QRgb color ) SIP_HOLDGIL
382  {
383  return setColor( static_cast< qgssize >( row ) * mWidth + column, color );
384  }
385 
392  bool setColor( qgssize index, QRgb color ) SIP_HOLDGIL
393  {
394  if ( !mImage )
395  {
396  QgsDebugError( QStringLiteral( "Image not allocated" ) );
397  return false;
398  }
399 
400  if ( index >= static_cast< qgssize >( mImage->width() ) * mImage->height() )
401  {
402  QgsDebugError( QStringLiteral( "index %1 out of range" ).arg( index ) );
403  return false;
404  }
405 
406  // setPixel() is slow, see Qt doc -> use direct access
407  QRgb *bits = reinterpret_cast< QRgb * >( mImage->bits() );
408  bits[index] = color;
409  return true;
410  }
411 
420  {
421  if ( !mImage )
422  return nullptr;
423  return reinterpret_cast< QRgb * >( mImage->bits() );
424  }
425 
432  bool setIsNoData( int row, int column ) SIP_HOLDGIL
433  {
434  return setIsNoData( static_cast< qgssize >( row ) * mWidth + column );
435  }
436 
443  {
444  if ( mHasNoDataValue )
445  {
446  return setValue( index, mNoDataValue );
447  }
448  else
449  {
450  if ( !mNoDataBitmap )
451  {
452  if ( !createNoDataBitmap() )
453  {
454  return false;
455  }
456  }
457  // TODO: optimize
458  const int row = static_cast< int >( index ) / mWidth;
459  const int column = index % mWidth;
460  const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
461  const int bit = column % 8;
462  const int nodata = 0x80 >> bit;
463  //QgsDebugMsg ( QString("set byte = %1 bit = %2 no data by %3").arg(byte).arg(bit).arg(nodata, 0,2 ) );
464  mNoDataBitmap[byte] = mNoDataBitmap[byte] | nodata;
465  return true;
466  }
467  }
468 
473  bool setIsNoData();
474 
479  bool setIsNoDataExcept( QRect exceptRect );
480 
489  void setIsData( int row, int column ) SIP_HOLDGIL
490  {
491  setIsData( static_cast< qgssize >( row )*mWidth + column );
492  }
493 
502  {
503  if ( mHasNoDataValue )
504  {
505  //no data value set, so mNoDataBitmap is not being used
506  return;
507  }
508 
509  if ( !mNoDataBitmap )
510  {
511  return;
512  }
513 
514  // TODO: optimize
515  const int row = static_cast< int >( index ) / mWidth;
516  const int column = index % mWidth;
517  const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
518  const int bit = column % 8;
519  const int nodata = 0x80 >> bit;
520  mNoDataBitmap[byte] = mNoDataBitmap[byte] & ~nodata;
521  }
522 
531  QByteArray data() const;
532 
541  void setData( const QByteArray &data, int offset = 0 );
542 
549  char *bits( int row, int column ) SIP_SKIP;
550 
556  char *bits( qgssize index ) SIP_SKIP;
557 
562  char *bits() SIP_SKIP;
563 
570  static QString printValue( double value );
571 
579  static QString printValue( float value ) SIP_SKIP;
580 
586  bool convert( Qgis::DataType destDataType );
587 
591  QImage image() const;
592 
597  bool setImage( const QImage *image );
598 
600  inline static double readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP;
601 
603  inline static void writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP;
604 
605  void applyNoDataValues( const QgsRasterRangeList &rangeList );
606 
610  void applyScaleOffset( double scale, double offset );
611 
613  QgsError error() const { return mError; }
614 
616  void setError( const QgsError &error ) { mError = error;}
617 
618  QString toString() const;
619 
630  static QRect subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent );
631 
636  int width() const SIP_HOLDGIL { return mWidth; }
637 
642  int height() const SIP_HOLDGIL { return mHeight; }
643 
644  private:
645  static QImage::Format imageFormat( Qgis::DataType dataType );
646  static Qgis::DataType dataType( QImage::Format format );
647 
654  static bool isNoDataValue( double value, double noDataValue )
655  {
656  // TODO: optimize no data value test by memcmp()
657  // More precise would be std::isnan(value) && std::isnan(noDataValue(bandNo)), but probably
658  // not important and slower
659  return std::isnan( value ) ||
660  qgsDoubleNear( value, noDataValue );
661  }
662 
668  inline bool isNoDataValue( double value ) const;
669 
674  bool createNoDataBitmap();
675 
685  static void *convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size );
686 
687  // Valid
688  bool mValid = true;
689 
690  // Data type
692 
693  // Data type size in bytes, to make bits() fast
694  int mTypeSize = 0;
695 
696  // Width
697  int mWidth = 0;
698 
699  // Height
700  int mHeight = 0;
701 
702  // Has no data value
703  bool mHasNoDataValue = false;
704 
705  // No data value
706  double mNoDataValue;
707 
708  static const QRgb NO_DATA_COLOR;
709 
710  // Data block for numerical data types, not used with image data types
711  // QByteArray does not seem to be intended for large data blocks, does it?
712  void *mData = nullptr;
713 
714  // Image for image data types, not used with numerical data types
715  QImage *mImage = nullptr;
716 
717  // Bitmap of no data. One bit for each pixel. Bit is 1 if a pixels is no data.
718  // Each row is represented by whole number of bytes (last bits may be unused)
719  // to make processing rows easy.
720  char *mNoDataBitmap = nullptr;
721 
722  // number of bytes in mNoDataBitmap row
723  int mNoDataBitmapWidth = 0;
724 
725  // total size in bytes of mNoDataBitmap
726  qgssize mNoDataBitmapSize = 0;
727 
728  // Error
729  QgsError mError;
730 };
731 
732 inline double QgsRasterBlock::readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP
733 {
734  if ( !data )
735  {
736  return std::numeric_limits<double>::quiet_NaN();
737  }
738 
739  switch ( type )
740  {
742  return static_cast< double >( ( static_cast< quint8 * >( data ) )[index] );
744  return static_cast< double >( ( static_cast< qint8 * >( data ) )[index] );
746  return static_cast< double >( ( static_cast< quint16 * >( data ) )[index] );
748  return static_cast< double >( ( static_cast< qint16 * >( data ) )[index] );
750  return static_cast< double >( ( static_cast< quint32 * >( data ) )[index] );
752  return static_cast< double >( ( static_cast< qint32 * >( data ) )[index] );
754  return static_cast< double >( ( static_cast< float * >( data ) )[index] );
756  return static_cast< double >( ( static_cast< double * >( data ) )[index] );
764  QgsDebugError( QStringLiteral( "Data type %1 is not supported" ).arg( qgsEnumValueToKey< Qgis::DataType >( type ) ) );
765  break;
766  }
767 
768  return std::numeric_limits<double>::quiet_NaN();
769 }
770 
771 inline void QgsRasterBlock::writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP
772 {
773  if ( !data ) return;
774 
775  switch ( type )
776  {
778  ( static_cast< quint8 * >( data ) )[index] = static_cast< quint8 >( value );
779  break;
781  ( static_cast< qint8 * >( data ) )[index] = static_cast< qint8 >( value );
782  break;
784  ( static_cast< quint16 * >( data ) )[index] = static_cast< quint16 >( value );
785  break;
787  ( static_cast< qint16 * >( data ) )[index] = static_cast< qint16 >( value );
788  break;
790  ( static_cast< quint32 * >( data ) )[index] = static_cast< quint32 >( value );
791  break;
793  ( static_cast< qint32 * >( data ) )[index] = static_cast< qint32 >( value );
794  break;
796  ( static_cast< float * >( data ) )[index] = static_cast< float >( value );
797  break;
799  ( static_cast< double * >( data ) )[index] = value;
800  break;
808  QgsDebugError( QStringLiteral( "Data type %1 is not supported" ).arg( qgsEnumValueToKey< Qgis::DataType >( type ) ) );
809  break;
810  }
811 }
812 
813 inline double QgsRasterBlock::value( qgssize index ) const SIP_SKIP
814 {
815  if ( !mData )
816  {
817  QgsDebugError( QStringLiteral( "Data block not allocated" ) );
818  return std::numeric_limits<double>::quiet_NaN();
819  }
820  return readValue( mData, mDataType, index );
821 }
822 
823 inline double QgsRasterBlock::valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP
824 {
825  if ( !mData )
826  {
827  QgsDebugError( QStringLiteral( "Data block not allocated" ) );
828  isNoData = true;
829  return std::numeric_limits<double>::quiet_NaN();
830  }
831  if ( index >= static_cast< qgssize >( mWidth )*mHeight )
832  {
833  QgsDebugError( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
834  isNoData = true; // we consider no data if outside
835  return std::numeric_limits<double>::quiet_NaN();
836  }
837 
838  const double val = readValue( mData, mDataType, index );
839 
840  if ( !mHasNoDataValue && !mNoDataBitmap )
841  {
842  isNoData = false;
843  return val;
844  }
845 
846  if ( mHasNoDataValue )
847  {
848  isNoData = isNoDataValue( val );
849  return val;
850  }
851  // use no data bitmap
852  if ( !mNoDataBitmap )
853  {
854  // no data are not defined
855  isNoData = false;
856  return val;
857  }
858 
859  // no data is a bitmap
860  isNoData = QgsRasterBlock::isNoData( index );
861  return val;
862 }
863 
864 inline bool QgsRasterBlock::isNoDataValue( double value ) const SIP_SKIP
865 {
866  return std::isnan( value ) || qgsDoubleNear( value, mNoDataValue );
867 }
868 
869 #endif
870 
871 
The Qgis class provides global constants for use throughout the application.
Definition: qgis.h:54
DataType
Raster data types.
Definition: qgis.h:269
@ 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.
QRgb * colorData()
Gives direct read/write access to the raster RGB data.
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.
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.
const quint8 * byteData() const
Gives direct access to the raster block 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)
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 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.
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:5712
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5172
#define SIP_SKIP
Definition: qgis_sip.h:126
#define SIP_HOLDGIL
Definition: qgis_sip.h:171
#define QgsDebugError(str)
Definition: qgslogger.h:38
QList< QgsRasterRange > QgsRasterRangeList