QGIS API Documentation  3.21.0-Master (5b68dc587e)
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  {
89  return 1;
90 
93  return 2;
94 
99  return 4;
100 
104  return 8;
105 
107  return 16;
108 
111  return 4;
112 
113  default:
114  return 0;
115  }
116  }
117 
122  {
123  return typeSize( mDataType );
124  }
125 
127  static bool typeIsNumeric( Qgis::DataType type );
128 
130  static bool typeIsColor( Qgis::DataType type );
131 
133  Qgis::DataType dataType() const SIP_HOLDGIL { return mDataType; }
134 
136  static Qgis::DataType typeWithNoDataValue( Qgis::DataType dataType, double *noDataValue );
137 
143  bool hasNoDataValue() const SIP_HOLDGIL { return mHasNoDataValue; }
144 
151  bool hasNoData() const SIP_HOLDGIL
152  {
153  return mHasNoDataValue || mNoDataBitmap;
154  }
155 
161  void setNoDataValue( double noDataValue ) SIP_HOLDGIL;
162 
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  QgsDebugMsg( 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  QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
363  return false;
364  }
365  if ( index >= static_cast< qgssize >( mWidth ) *mHeight )
366  {
367  QgsDebugMsg( 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  QgsDebugMsg( QStringLiteral( "Image not allocated" ) );
397  return false;
398  }
399 
400  if ( index >= static_cast< qgssize >( mImage->width() ) * mImage->height() )
401  {
402  QgsDebugMsg( 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 
490  void setIsData( int row, int column ) SIP_HOLDGIL
491  {
492  setIsData( static_cast< qgssize >( row )*mWidth + column );
493  }
494 
504  {
505  if ( mHasNoDataValue )
506  {
507  //no data value set, so mNoDataBitmap is not being used
508  return;
509  }
510 
511  if ( !mNoDataBitmap )
512  {
513  return;
514  }
515 
516  // TODO: optimize
517  const int row = static_cast< int >( index ) / mWidth;
518  const int column = index % mWidth;
519  const qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
520  const int bit = column % 8;
521  const int nodata = 0x80 >> bit;
522  mNoDataBitmap[byte] = mNoDataBitmap[byte] & ~nodata;
523  }
524 
534  QByteArray data() const;
535 
545  void setData( const QByteArray &data, int offset = 0 );
546 
553  char *bits( int row, int column ) SIP_SKIP;
554 
560  char *bits( qgssize index ) SIP_SKIP;
561 
566  char *bits() SIP_SKIP;
567 
574  static QString printValue( double value );
575 
584  static QString printValue( float value ) SIP_SKIP;
585 
591  bool convert( Qgis::DataType destDataType );
592 
596  QImage image() const;
597 
602  bool setImage( const QImage *image );
603 
605  inline static double readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP;
606 
608  inline static void writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP;
609 
610  void applyNoDataValues( const QgsRasterRangeList &rangeList );
611 
616  void applyScaleOffset( double scale, double offset );
617 
619  QgsError error() const { return mError; }
620 
622  void setError( const QgsError &error ) { mError = error;}
623 
624  QString toString() const;
625 
636  static QRect subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent );
637 
643  int width() const SIP_HOLDGIL { return mWidth; }
644 
650  int height() const SIP_HOLDGIL { return mHeight; }
651 
652  private:
653  static QImage::Format imageFormat( Qgis::DataType dataType );
654  static Qgis::DataType dataType( QImage::Format format );
655 
662  static bool isNoDataValue( double value, double noDataValue )
663  {
664  // TODO: optimize no data value test by memcmp()
665  // More precise would be std::isnan(value) && std::isnan(noDataValue(bandNo)), but probably
666  // not important and slower
667  return std::isnan( value ) ||
668  qgsDoubleNear( value, noDataValue );
669  }
670 
676  inline bool isNoDataValue( double value ) const;
677 
682  bool createNoDataBitmap();
683 
693  static void *convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size );
694 
695  // Valid
696  bool mValid = true;
697 
698  // Data type
700 
701  // Data type size in bytes, to make bits() fast
702  int mTypeSize = 0;
703 
704  // Width
705  int mWidth = 0;
706 
707  // Height
708  int mHeight = 0;
709 
710  // Has no data value
711  bool mHasNoDataValue = false;
712 
713  // No data value
714  double mNoDataValue;
715 
716  static const QRgb NO_DATA_COLOR;
717 
718  // Data block for numerical data types, not used with image data types
719  // QByteArray does not seem to be intended for large data blocks, does it?
720  void *mData = nullptr;
721 
722  // Image for image data types, not used with numerical data types
723  QImage *mImage = nullptr;
724 
725  // Bitmap of no data. One bit for each pixel. Bit is 1 if a pixels is no data.
726  // Each row is represented by whole number of bytes (last bits may be unused)
727  // to make processing rows easy.
728  char *mNoDataBitmap = nullptr;
729 
730  // number of bytes in mNoDataBitmap row
731  int mNoDataBitmapWidth = 0;
732 
733  // total size in bytes of mNoDataBitmap
734  qgssize mNoDataBitmapSize = 0;
735 
736  // Error
737  QgsError mError;
738 };
739 
740 inline double QgsRasterBlock::readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP
741 {
742  if ( !data )
743  {
744  return std::numeric_limits<double>::quiet_NaN();
745  }
746 
747  switch ( type )
748  {
750  return static_cast< double >( ( static_cast< quint8 * >( data ) )[index] );
752  return static_cast< double >( ( static_cast< quint16 * >( data ) )[index] );
754  return static_cast< double >( ( static_cast< qint16 * >( data ) )[index] );
756  return static_cast< double >( ( static_cast< quint32 * >( data ) )[index] );
758  return static_cast< double >( ( static_cast< qint32 * >( data ) )[index] );
760  return static_cast< double >( ( static_cast< float * >( data ) )[index] );
762  return static_cast< double >( ( static_cast< double * >( data ) )[index] );
763  default:
764  QgsDebugMsg( 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< quint16 * >( data ) )[index] = static_cast< quint16 >( value );
782  break;
784  ( static_cast< qint16 * >( data ) )[index] = static_cast< qint16 >( value );
785  break;
787  ( static_cast< quint32 * >( data ) )[index] = static_cast< quint32 >( value );
788  break;
790  ( static_cast< qint32 * >( data ) )[index] = static_cast< qint32 >( value );
791  break;
793  ( static_cast< float * >( data ) )[index] = static_cast< float >( value );
794  break;
796  ( static_cast< double * >( data ) )[index] = value;
797  break;
798  default:
799  QgsDebugMsg( QStringLiteral( "Data type %1 is not supported" ).arg( qgsEnumValueToKey< Qgis::DataType >( type ) ) );
800  break;
801  }
802 }
803 
804 inline double QgsRasterBlock::value( qgssize index ) const SIP_SKIP
805 {
806  if ( !mData )
807  {
808  QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
809  return std::numeric_limits<double>::quiet_NaN();
810  }
811  return readValue( mData, mDataType, index );
812 }
813 
814 inline double QgsRasterBlock::valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP
815 {
816  if ( !mData )
817  {
818  QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
819  isNoData = true;
820  return std::numeric_limits<double>::quiet_NaN();
821  }
822  if ( index >= static_cast< qgssize >( mWidth )*mHeight )
823  {
824  QgsDebugMsg( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
825  isNoData = true; // we consider no data if outside
826  return std::numeric_limits<double>::quiet_NaN();
827  }
828 
829  const double val = readValue( mData, mDataType, index );
830 
831  if ( !mHasNoDataValue && !mNoDataBitmap )
832  {
833  isNoData = false;
834  return val;
835  }
836 
837  if ( mHasNoDataValue )
838  {
839  isNoData = isNoDataValue( val );
840  return val;
841  }
842  // use no data bitmap
843  if ( !mNoDataBitmap )
844  {
845  // no data are not defined
846  isNoData = false;
847  return val;
848  }
849 
850  // no data is a bitmap
851  isNoData = QgsRasterBlock::isNoData( index );
852  return val;
853 }
854 
855 inline bool QgsRasterBlock::isNoDataValue( double value ) const SIP_SKIP
856 {
857  return std::isnan( value ) || qgsDoubleNear( value, mNoDataValue );
858 }
859 
860 #endif
861 
862 
The Qgis class provides global constants for use throughout the application.
Definition: qgis.h:63
DataType
Raster data types.
Definition: qgis.h:120
@ 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.
@ 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.
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.
const quint8 * byteData() const
Gives direct access to the raster block data.
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.
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.
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:1701
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1234
#define SIP_SKIP
Definition: qgis_sip.h:126
#define SIP_HOLDGIL
Definition: qgis_sip.h:157
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QList< QgsRasterRange > QgsRasterRangeList