QGIS API Documentation  3.13.0-Master (b73bd58cfb)
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 { return mValid; }
69 
71  void setValid( bool valid ) { mValid = valid; }
72 
78  bool isEmpty() const;
79 
80  // Return data type size in bytes
81  static int typeSize( int dataType )
82  {
83  // Modified and extended copy from GDAL
84  switch ( dataType )
85  {
86  case Qgis::Byte:
87  return 1;
88 
89  case Qgis::UInt16:
90  case Qgis::Int16:
91  return 2;
92 
93  case Qgis::UInt32:
94  case Qgis::Int32:
95  case Qgis::Float32:
96  case Qgis::CInt16:
97  return 4;
98 
99  case Qgis::Float64:
100  case Qgis::CInt32:
101  case Qgis::CFloat32:
102  return 8;
103 
104  case Qgis::CFloat64:
105  return 16;
106 
107  case Qgis::ARGB32:
109  return 4;
110 
111  default:
112  return 0;
113  }
114  }
115 
116  // Data type in bytes
117  int dataTypeSize() const
118  {
119  return typeSize( mDataType );
120  }
121 
123  static bool typeIsNumeric( Qgis::DataType type );
124 
126  static bool typeIsColor( Qgis::DataType type );
127 
129  Qgis::DataType dataType() const { return mDataType; }
130 
132  static Qgis::DataType typeWithNoDataValue( Qgis::DataType dataType, double *noDataValue );
133 
139  bool hasNoDataValue() const { return mHasNoDataValue; }
140 
147  bool hasNoData() const
148  {
149  return mHasNoDataValue || mNoDataBitmap;
150  }
151 
157  void setNoDataValue( double noDataValue );
158 
165  void resetNoDataValue();
166 
173  double noDataValue() const { return mNoDataValue; }
174 
181  static QByteArray valueBytes( Qgis::DataType dataType, double value );
182 
191  double value( int row, int column ) const
192  {
193  return value( static_cast< qgssize >( row ) * mWidth + column );
194  }
195 
208  double valueAndNoData( int row, int column, bool &isNoData ) const SIP_SKIP
209  {
210  return valueAndNoData( static_cast< qgssize >( row ) * mWidth + column, isNoData );
211  }
212 
220  inline double value( qgssize index ) const;
221 
234  inline double valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP;
235 
243  const quint8 *byteData() const SIP_SKIP
244  {
245  if ( mDataType != Qgis::Byte )
246  return nullptr;
247  return static_cast< const quint8 * >( mData );
248  }
249 
256  QRgb color( int row, int column ) const
257  {
258  if ( !mImage ) return NO_DATA_COLOR;
259 
260  return mImage->pixel( column, row );
261  }
262 
268  QRgb color( qgssize index ) const
269  {
270  int row = static_cast< int >( std::floor( static_cast< double >( index ) / mWidth ) );
271  int column = index % mWidth;
272  return color( row, column );
273  }
274 
282  bool isNoData( int row, int column ) const
283  {
284  return isNoData( static_cast< qgssize >( row ) * mWidth + column );
285  }
286 
294  bool isNoData( qgssize row, qgssize column ) const
295  {
296  return isNoData( row * static_cast< qgssize >( mWidth ) + column );
297  }
298 
305  bool isNoData( qgssize index ) const
306  {
307  if ( !mHasNoDataValue && !mNoDataBitmap )
308  return false;
309  if ( index >= static_cast< qgssize >( mWidth )*mHeight )
310  {
311  QgsDebugMsg( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
312  return true; // we consider no data if outside
313  }
314  if ( mHasNoDataValue )
315  {
316  double value = readValue( mData, mDataType, index );
317  return isNoDataValue( value );
318  }
319  // use no data bitmap
320  if ( !mNoDataBitmap )
321  {
322  // no data are not defined
323  return false;
324  }
325  // TODO: optimize
326  int row = static_cast< int >( index ) / mWidth;
327  int column = index % mWidth;
328  qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
329  int bit = column % 8;
330  int mask = 0x80 >> bit;
331  //int x = mNoDataBitmap[byte] & mask;
332  //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) ) );
333  return mNoDataBitmap[byte] & mask;
334  }
335 
343  bool setValue( int row, int column, double value )
344  {
345  return setValue( static_cast< qgssize >( row ) * mWidth + column, value );
346  }
347 
354  bool setValue( qgssize index, double value )
355  {
356  if ( !mData )
357  {
358  QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
359  return false;
360  }
361  if ( index >= static_cast< qgssize >( mWidth ) *mHeight )
362  {
363  QgsDebugMsg( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
364  return false;
365  }
366  writeValue( mData, mDataType, index, value );
367  return true;
368  }
369 
377  bool setColor( int row, int column, QRgb color )
378  {
379  return setColor( static_cast< qgssize >( row ) * mWidth + column, color );
380  }
381 
388  bool setColor( qgssize index, QRgb color )
389  {
390  if ( !mImage )
391  {
392  QgsDebugMsg( QStringLiteral( "Image not allocated" ) );
393  return false;
394  }
395 
396  if ( index >= static_cast< qgssize >( mImage->width() ) * mImage->height() )
397  {
398  QgsDebugMsg( QStringLiteral( "index %1 out of range" ).arg( index ) );
399  return false;
400  }
401 
402  // setPixel() is slow, see Qt doc -> use direct access
403  QRgb *bits = reinterpret_cast< QRgb * >( mImage->bits() );
404  bits[index] = color;
405  return true;
406  }
407 
415  QRgb *colorData() SIP_SKIP
416  {
417  if ( !mImage )
418  return nullptr;
419  return reinterpret_cast< QRgb * >( mImage->bits() );
420  }
421 
428  bool setIsNoData( int row, int column )
429  {
430  return setIsNoData( static_cast< qgssize >( row ) * mWidth + column );
431  }
432 
438  bool setIsNoData( qgssize index )
439  {
440  if ( mHasNoDataValue )
441  {
442  return setValue( index, mNoDataValue );
443  }
444  else
445  {
446  if ( !mNoDataBitmap )
447  {
448  if ( !createNoDataBitmap() )
449  {
450  return false;
451  }
452  }
453  // TODO: optimize
454  int row = static_cast< int >( index ) / mWidth;
455  int column = index % mWidth;
456  qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
457  int bit = column % 8;
458  int nodata = 0x80 >> bit;
459  //QgsDebugMsg ( QString("set byte = %1 bit = %2 no data by %3").arg(byte).arg(bit).arg(nodata, 0,2 ) );
460  mNoDataBitmap[byte] = mNoDataBitmap[byte] | nodata;
461  return true;
462  }
463  }
464 
469  bool setIsNoData();
470 
475  bool setIsNoDataExcept( QRect exceptRect );
476 
486  void setIsData( int row, int column )
487  {
488  setIsData( static_cast< qgssize >( row )*mWidth + column );
489  }
490 
499  void setIsData( qgssize index )
500  {
501  if ( mHasNoDataValue )
502  {
503  //no data value set, so mNoDataBitmap is not being used
504  return;
505  }
506 
507  if ( !mNoDataBitmap )
508  {
509  return;
510  }
511 
512  // TODO: optimize
513  int row = static_cast< int >( index ) / mWidth;
514  int column = index % mWidth;
515  qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
516  int bit = column % 8;
517  int nodata = 0x80 >> bit;
518  mNoDataBitmap[byte] = mNoDataBitmap[byte] & ~nodata;
519  }
520 
530  QByteArray data() const;
531 
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 
569  static QString printValue( double value );
570 
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 
611  void applyScaleOffset( double scale, double offset );
612 
614  QgsError error() const { return mError; }
615 
617  void setError( const QgsError &error ) { mError = error;}
618 
619  QString toString() const;
620 
631  static QRect subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent );
632 
638  int width() const { return mWidth; }
639 
645  int height() const { return mHeight; }
646 
647  private:
648  static QImage::Format imageFormat( Qgis::DataType dataType );
649  static Qgis::DataType dataType( QImage::Format format );
650 
657  static bool isNoDataValue( double value, double noDataValue )
658  {
659  // TODO: optimize no data value test by memcmp()
660  // More precise would be std::isnan(value) && std::isnan(noDataValue(bandNo)), but probably
661  // not important and slower
662  return std::isnan( value ) ||
663  qgsDoubleNear( value, noDataValue );
664  }
665 
671  inline bool isNoDataValue( double value ) const;
672 
677  bool createNoDataBitmap();
678 
688  static void *convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size );
689 
690  // Valid
691  bool mValid = true;
692 
693  // Data type
695 
696  // Data type size in bytes, to make bits() fast
697  int mTypeSize = 0;
698 
699  // Width
700  int mWidth = 0;
701 
702  // Height
703  int mHeight = 0;
704 
705  // Has no data value
706  bool mHasNoDataValue = false;
707 
708  // No data value
709  double mNoDataValue;
710 
711  static const QRgb NO_DATA_COLOR;
712 
713  // Data block for numerical data types, not used with image data types
714  // QByteArray does not seem to be intended for large data blocks, does it?
715  void *mData = nullptr;
716 
717  // Image for image data types, not used with numerical data types
718  QImage *mImage = nullptr;
719 
720  // Bitmap of no data. One bit for each pixel. Bit is 1 if a pixels is no data.
721  // Each row is represented by whole number of bytes (last bits may be unused)
722  // to make processing rows easy.
723  char *mNoDataBitmap = nullptr;
724 
725  // number of bytes in mNoDataBitmap row
726  int mNoDataBitmapWidth = 0;
727 
728  // total size in bytes of mNoDataBitmap
729  qgssize mNoDataBitmapSize = 0;
730 
731  // Error
732  QgsError mError;
733 };
734 
735 inline double QgsRasterBlock::readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP
736 {
737  if ( !data )
738  {
739  return std::numeric_limits<double>::quiet_NaN();
740  }
741 
742  switch ( type )
743  {
744  case Qgis::Byte:
745  return static_cast< double >( ( static_cast< quint8 * >( data ) )[index] );
746  case Qgis::UInt16:
747  return static_cast< double >( ( static_cast< quint16 * >( data ) )[index] );
748  case Qgis::Int16:
749  return static_cast< double >( ( static_cast< qint16 * >( data ) )[index] );
750  case Qgis::UInt32:
751  return static_cast< double >( ( static_cast< quint32 * >( data ) )[index] );
752  case Qgis::Int32:
753  return static_cast< double >( ( static_cast< qint32 * >( data ) )[index] );
754  case Qgis::Float32:
755  return static_cast< double >( ( static_cast< float * >( data ) )[index] );
756  case Qgis::Float64:
757  return static_cast< double >( ( static_cast< double * >( data ) )[index] );
758  default:
759  QgsDebugMsg( QStringLiteral( "Data type %1 is not supported" ).arg( type ) );
760  break;
761  }
762 
763  return std::numeric_limits<double>::quiet_NaN();
764 }
765 
766 inline void QgsRasterBlock::writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP
767 {
768  if ( !data ) return;
769 
770  switch ( type )
771  {
772  case Qgis::Byte:
773  ( static_cast< quint8 * >( data ) )[index] = static_cast< quint8 >( value );
774  break;
775  case Qgis::UInt16:
776  ( static_cast< quint16 * >( data ) )[index] = static_cast< quint16 >( value );
777  break;
778  case Qgis::Int16:
779  ( static_cast< qint16 * >( data ) )[index] = static_cast< qint16 >( value );
780  break;
781  case Qgis::UInt32:
782  ( static_cast< quint32 * >( data ) )[index] = static_cast< quint32 >( value );
783  break;
784  case Qgis::Int32:
785  ( static_cast< qint32 * >( data ) )[index] = static_cast< qint32 >( value );
786  break;
787  case Qgis::Float32:
788  ( static_cast< float * >( data ) )[index] = static_cast< float >( value );
789  break;
790  case Qgis::Float64:
791  ( static_cast< double * >( data ) )[index] = value;
792  break;
793  default:
794  QgsDebugMsg( QStringLiteral( "Data type %1 is not supported" ).arg( type ) );
795  break;
796  }
797 }
798 
799 inline double QgsRasterBlock::value( qgssize index ) const SIP_SKIP
800 {
801  if ( !mData )
802  {
803  QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
804  return std::numeric_limits<double>::quiet_NaN();
805  }
806  return readValue( mData, mDataType, index );
807 }
808 
809 inline double QgsRasterBlock::valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP
810 {
811  if ( !mData )
812  {
813  QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
814  isNoData = true;
815  return std::numeric_limits<double>::quiet_NaN();
816  }
817  if ( index >= static_cast< qgssize >( mWidth )*mHeight )
818  {
819  QgsDebugMsg( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
820  isNoData = true; // we consider no data if outside
821  return std::numeric_limits<double>::quiet_NaN();
822  }
823 
824  const double val = readValue( mData, mDataType, index );
825 
826  if ( !mHasNoDataValue && !mNoDataBitmap )
827  {
828  isNoData = false;
829  return val;
830  }
831 
832  if ( mHasNoDataValue )
833  {
834  isNoData = isNoDataValue( val );
835  return val;
836  }
837  // use no data bitmap
838  if ( !mNoDataBitmap )
839  {
840  // no data are not defined
841  isNoData = false;
842  return val;
843  }
844 
845  // no data is a bitmap
846  isNoData = QgsRasterBlock::isNoData( index );
847  return val;
848 }
849 
850 inline bool QgsRasterBlock::isNoDataValue( double value ) const SIP_SKIP
851 {
852  return std::isnan( value ) || qgsDoubleNear( value, mNoDataValue );
853 }
854 
855 #endif
856 
857 
A rectangle specified with double values.
Definition: qgsrectangle.h:41
bool isNoData(qgssize row, qgssize column) const
Check if value at position is no data.
Thirty two bit signed integer (qint32)
Definition: qgis.h:108
bool isValid() const
Returns true if the block is valid (correctly filled with data).
static double readValue(void *data, Qgis::DataType type, qgssize index)
Qgis::DataType dataType() const
Returns data type.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
bool setValue(int row, int column, double value)
Set value on position.
int height() const
Returns the height (number of rows) of the raster block.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
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.
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:107
QRgb * colorData()
Gives direct read/write access to the raster RGB data.
DataType
Raster data types.
Definition: qgis.h:101
bool setColor(qgssize index, QRgb color)
Set color on index (indexed line by line)
Thirty two bit floating point (float)
Definition: qgis.h:109
bool isNoData(qgssize index) const
Check if value at position is no data.
Sixteen bit signed integer (qint16)
Definition: qgis.h:106
Complex Int16.
Definition: qgis.h:111
Sixty four bit floating point (double)
Definition: qgis.h:110
QgsError error() const
Returns the last error.
bool setIsNoData(int row, int column)
Set no data on pixel.
bool setColor(int row, int column, QRgb color)
Set color on position.
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:116
Raster data container.
void setIsData(qgssize index)
Remove no data flag on pixel.
const quint8 * byteData() const
Gives direct access to the raster block data.
#define SIP_SKIP
Definition: qgis_sip.h:126
void setError(const QgsError &error)
Sets the last error.
Complex Float32.
Definition: qgis.h:113
bool setIsNoData(qgssize index)
Set no data on pixel.
QRgb color(int row, int column) const
Read a single color.
void setIsData(int row, int column)
Remove no data flag on pixel.
Unknown or unspecified type.
Definition: qgis.h:103
Complex Int32.
Definition: qgis.h:112
static int typeSize(int dataType)
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:105
bool isNoData(int row, int column) const
Checks if value at position is no data.
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:703
int width() const
Returns the width (number of columns) of the raster block.
void setValid(bool valid)
Mark block as valid or invalid.
QRgb color(qgssize index) const
Read a single value.
static void writeValue(void *data, Qgis::DataType type, qgssize index, double value)
QList< QgsRasterRange > QgsRasterRangeList
bool setValue(qgssize index, double value)
Set value on index (indexed line by line)
QgsError is container for error messages (report).
Definition: qgserror.h:80
double value(int row, int column) const
Read a single value if type of block is numeric.
bool hasNoDataValue() const
true if the block has no data value.
int dataTypeSize() const
Complex Float64.
Definition: qgis.h:114
bool hasNoData() const
Returns true if the block may contain no data.
double noDataValue() const
Returns no data value.
Eight bit unsigned integer (quint8)
Definition: qgis.h:104
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition: qgis.h:115