QGIS API Documentation  2.99.0-Master (53aba61)
qgsrasterblock.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrasterblock.cpp - 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 #include <limits>
19 
20 #include <QByteArray>
21 #include <QColor>
22 
23 #include "qgslogger.h"
24 #include "qgsrasterblock.h"
25 #include "qgsrectangle.h"
26 
27 // See #9101 before any change of NODATA_COLOR!
28 const QRgb QgsRasterBlock::NO_DATA_COLOR = qRgba( 0, 0, 0, 0 );
29 
31  : mValid( true )
32  , mDataType( Qgis::UnknownDataType )
33  , mTypeSize( 0 )
34  , mWidth( 0 )
35  , mHeight( 0 )
36  , mHasNoDataValue( false )
37  , mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
38  , mNoDataBitmapWidth( 0 )
39  , mNoDataBitmapSize( 0 )
40 {
41 }
42 
44  : mValid( true )
45  , mDataType( dataType )
46  , mTypeSize( 0 )
47  , mWidth( width )
48  , mHeight( height )
49  , mHasNoDataValue( false )
50  , mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
51  , mNoDataBitmapWidth( 0 )
52  , mNoDataBitmapSize( 0 )
53 {
54  ( void )reset( mDataType, mWidth, mHeight );
55 }
56 
58 {
59  QgsDebugMsgLevel( QString( "mData = %1" ).arg( reinterpret_cast< quint64 >( mData ) ), 4 );
60  qgsFree( mData );
61  delete mImage;
62  qgsFree( mNoDataBitmap );
63 }
64 
66 {
67  QgsDebugMsgLevel( QString( "theWidth= %1 height = %2 dataType = %3" ).arg( width ).arg( height ).arg( dataType ), 4 );
68 
69  qgsFree( mData );
70  mData = nullptr;
71  delete mImage;
72  mImage = nullptr;
73  qgsFree( mNoDataBitmap );
74  mNoDataBitmap = nullptr;
75  mDataType = Qgis::UnknownDataType;
76  mTypeSize = 0;
77  mWidth = 0;
78  mHeight = 0;
79  mHasNoDataValue = false;
80  mNoDataValue = std::numeric_limits<double>::quiet_NaN();
81  mValid = false;
82 
83  if ( typeIsNumeric( dataType ) )
84  {
85  QgsDebugMsgLevel( "Numeric type", 4 );
86  qgssize tSize = typeSize( dataType );
87  QgsDebugMsgLevel( QString( "allocate %1 bytes" ).arg( tSize * width * height ), 4 );
88  mData = qgsMalloc( tSize * width * height );
89  if ( !mData )
90  {
91  QgsDebugMsg( QString( "Couldn't allocate data memory of %1 bytes" ).arg( tSize * width * height ) );
92  return false;
93  }
94  }
95  else if ( typeIsColor( dataType ) )
96  {
97  QgsDebugMsgLevel( "Color type", 4 );
98  QImage::Format format = imageFormat( dataType );
99  mImage = new QImage( width, height, format );
100  }
101  else
102  {
103  QgsDebugMsg( "Wrong data type" );
104  return false;
105  }
106 
107  mValid = true;
108  mDataType = dataType;
109  mTypeSize = QgsRasterBlock::typeSize( mDataType );
110  mWidth = width;
111  mHeight = height;
112  QgsDebugMsgLevel( QString( "mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5" ).arg( mWidth ).arg( mHeight ).arg( mDataType )
113  .arg( reinterpret_cast< quint64 >( mData ) ).arg( reinterpret_cast< quint64 >( mImage ) ), 4 );
114  return true;
115 }
116 
117 QImage::Format QgsRasterBlock::imageFormat( Qgis::DataType dataType )
118 {
119  if ( dataType == Qgis::ARGB32 )
120  {
121  return QImage::Format_ARGB32;
122  }
123  else if ( dataType == Qgis::ARGB32_Premultiplied )
124  {
125  return QImage::Format_ARGB32_Premultiplied;
126  }
127  return QImage::Format_Invalid;
128 }
129 
130 Qgis::DataType QgsRasterBlock::dataType( QImage::Format format )
131 {
132  if ( format == QImage::Format_ARGB32 )
133  {
134  return Qgis::ARGB32;
135  }
136  else if ( format == QImage::Format_ARGB32_Premultiplied )
137  {
139  }
140  return Qgis::UnknownDataType;
141 }
142 
144 {
145  QgsDebugMsgLevel( QString( "mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5" ).arg( mWidth ).arg( mHeight ).arg( mDataType )
146  .arg( reinterpret_cast< quint64 >( mData ) ).arg( reinterpret_cast< quint64 >( mImage ) ), 4 );
147  return mWidth == 0 || mHeight == 0 ||
148  ( typeIsNumeric( mDataType ) && !mData ) ||
149  ( typeIsColor( mDataType ) && !mImage );
150 }
151 
153 {
154  switch ( dataType )
155  {
156  case Qgis::Byte:
157  case Qgis::UInt16:
158  case Qgis::Int16:
159  case Qgis::UInt32:
160  case Qgis::Int32:
161  case Qgis::Float32:
162  case Qgis::CInt16:
163  case Qgis::Float64:
164  case Qgis::CInt32:
165  case Qgis::CFloat32:
166  case Qgis::CFloat64:
167  return true;
168 
170  case Qgis::ARGB32:
172  return false;
173  }
174  return false;
175 }
176 
178 {
179  switch ( dataType )
180  {
181  case Qgis::ARGB32:
183  return true;
184 
186  case Qgis::Byte:
187  case Qgis::UInt16:
188  case Qgis::Int16:
189  case Qgis::UInt32:
190  case Qgis::Int32:
191  case Qgis::Float32:
192  case Qgis::CInt16:
193  case Qgis::Float64:
194  case Qgis::CInt32:
195  case Qgis::CFloat32:
196  case Qgis::CFloat64:
197  return false;
198  }
199  return false;
200 }
201 
203 {
204  Qgis::DataType newDataType;
205 
206  switch ( dataType )
207  {
208  case Qgis::Byte:
209  *noDataValue = -32768.0;
210  newDataType = Qgis::Int16;
211  break;
212  case Qgis::Int16:
213  *noDataValue = -2147483648.0;
214  newDataType = Qgis::Int32;
215  break;
216  case Qgis::UInt16:
217  *noDataValue = -2147483648.0;
218  newDataType = Qgis::Int32;
219  break;
220  case Qgis::UInt32:
221  case Qgis::Int32:
222  case Qgis::Float32:
223  case Qgis::Float64:
224  *noDataValue = std::numeric_limits<double>::max() * -1.0;
225  newDataType = Qgis::Float64;
226  break;
227  default:
228  QgsDebugMsg( QString( "Unknown data type %1" ).arg( dataType ) );
229  return Qgis::UnknownDataType;
230  }
231  QgsDebugMsgLevel( QString( "newDataType = %1 noDataValue = %2" ).arg( newDataType ).arg( *noDataValue ), 4 );
232  return newDataType;
233 }
234 
236 {
237  return mHasNoDataValue || mNoDataBitmap;
238 }
239 
241 {
242  mHasNoDataValue = true;
243  mNoDataValue = noDataValue;
244 }
245 
247 {
248  mHasNoDataValue = false;
249  mNoDataValue = std::numeric_limits<double>::quiet_NaN();
250 }
251 
252 bool QgsRasterBlock::isNoDataValue( double value, double noDataValue )
253 {
254  // TODO: optimize no data value test by memcmp()
255  // More precise would be std::isnan(value) && std::isnan(noDataValue(bandNo)), but probably
256  // not important and slower
257  return std::isnan( value ) ||
258  qgsDoubleNear( value, noDataValue );
259 }
260 
261 double QgsRasterBlock::value( int row, int column ) const
262 {
263  return value( static_cast< qgssize >( row ) * mWidth + column );
264 }
265 
266 QRgb QgsRasterBlock::color( qgssize index ) const
267 {
268  int row = std::floor( static_cast< double >( index ) / mWidth );
269  int column = index % mWidth;
270  return color( row, column );
271 }
272 
273 QRgb QgsRasterBlock::color( int row, int column ) const
274 {
275  if ( !mImage ) return NO_DATA_COLOR;
276 
277  return mImage->pixel( column, row );
278 }
279 
281 {
282  if ( !mHasNoDataValue && !mNoDataBitmap ) return false;
283  if ( index >= static_cast< qgssize >( mWidth )*mHeight )
284  {
285  QgsDebugMsg( QString( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
286  return true; // we consider no data if outside
287  }
288  if ( mHasNoDataValue )
289  {
290  double value = readValue( mData, mDataType, index );
291  return isNoDataValue( value );
292  }
293  // use no data bitmap
294  if ( !mNoDataBitmap )
295  {
296  // no data are not defined
297  return false;
298  }
299  // TODO: optimize
300  int row = static_cast< int >( index ) / mWidth;
301  int column = index % mWidth;
302  qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
303  int bit = column % 8;
304  int mask = 0x80 >> bit;
305  //int x = mNoDataBitmap[byte] & mask;
306  //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) ) );
307  return mNoDataBitmap[byte] & mask;
308 }
309 
310 bool QgsRasterBlock::isNoData( int row, int column )
311 {
312  return isNoData( static_cast< qgssize >( row ) * mWidth + column );
313 }
314 
316 {
317  if ( !mData )
318  {
319  QgsDebugMsg( "Data block not allocated" );
320  return false;
321  }
322  if ( index >= static_cast< qgssize >( mWidth ) *mHeight )
323  {
324  QgsDebugMsg( QString( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
325  return false;
326  }
327  writeValue( mData, mDataType, index, value );
328  return true;
329 }
330 
331 bool QgsRasterBlock::setValue( int row, int column, double value )
332 {
333  return setValue( static_cast< qgssize >( row ) * mWidth + column, value );
334 }
335 
336 bool QgsRasterBlock::setColor( int row, int column, QRgb color )
337 {
338  return setColor( static_cast< qgssize >( row ) * mWidth + column, color );
339 }
340 
342 {
343  if ( !mImage )
344  {
345  QgsDebugMsg( "Image not allocated" );
346  return false;
347  }
348 
349  if ( index >= static_cast< qgssize >( mImage->width() ) * mImage->height() )
350  {
351  QgsDebugMsg( QString( "index %1 out of range" ).arg( index ) );
352  return false;
353  }
354 
355  // setPixel() is slow, see Qt doc -> use direct access
356  QRgb *bits = reinterpret_cast< QRgb * >( mImage->bits() );
357  bits[index] = color;
358  return true;
359 }
360 
361 bool QgsRasterBlock::setIsNoData( int row, int column )
362 {
363  return setIsNoData( static_cast< qgssize >( row ) * mWidth + column );
364 }
365 
367 {
368  if ( mHasNoDataValue )
369  {
370  return setValue( index, mNoDataValue );
371  }
372  else
373  {
374  if ( !mNoDataBitmap )
375  {
376  if ( !createNoDataBitmap() )
377  {
378  return false;
379  }
380  }
381  // TODO: optimize
382  int row = static_cast< int >( index ) / mWidth;
383  int column = index % mWidth;
384  qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
385  int bit = column % 8;
386  int nodata = 0x80 >> bit;
387  //QgsDebugMsg ( QString("set byte = %1 bit = %2 no data by %3").arg(byte).arg(bit).arg(nodata, 0,2 ) );
388  mNoDataBitmap[byte] = mNoDataBitmap[byte] | nodata;
389  return true;
390  }
391 }
392 
394 {
395  QgsDebugMsgLevel( "Entered", 4 );
396  if ( typeIsNumeric( mDataType ) )
397  {
398  if ( mHasNoDataValue )
399  {
400  if ( !mData )
401  {
402  QgsDebugMsg( "Data block not allocated" );
403  return false;
404  }
405 
406  QgsDebugMsgLevel( "set mData to mNoDataValue", 4 );
407  int dataTypeSize = typeSize( mDataType );
408  QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
409 
410  char *nodata = noDataByteArray.data();
411  for ( qgssize i = 0; i < static_cast< qgssize >( mWidth )*mHeight; i++ )
412  {
413  memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodata, dataTypeSize );
414  }
415  }
416  else
417  {
418  // use bitmap
419  if ( !mNoDataBitmap )
420  {
421  if ( !createNoDataBitmap() )
422  {
423  return false;
424  }
425  }
426  QgsDebugMsgLevel( "set mNoDataBitmap to 1", 4 );
427  memset( mNoDataBitmap, 0xff, mNoDataBitmapSize );
428  }
429  return true;
430  }
431  else
432  {
433  // image
434  if ( !mImage )
435  {
436  QgsDebugMsg( "Image not allocated" );
437  return false;
438  }
439  QgsDebugMsgLevel( "Fill image", 4 );
440  mImage->fill( NO_DATA_COLOR );
441  return true;
442  }
443 }
444 
445 bool QgsRasterBlock::setIsNoDataExcept( QRect exceptRect )
446 {
447  int top = exceptRect.top();
448  int bottom = exceptRect.bottom();
449  int left = exceptRect.left();
450  int right = exceptRect.right();
451  top = std::min( std::max( top, 0 ), mHeight - 1 );
452  left = std::min( std::max( left, 0 ), mWidth - 1 );
453  bottom = std::max( 0, std::min( bottom, mHeight - 1 ) );
454  right = std::max( 0, std::min( right, mWidth - 1 ) );
455 
456  QgsDebugMsgLevel( "Entered", 4 );
457  if ( typeIsNumeric( mDataType ) )
458  {
459  if ( mHasNoDataValue )
460  {
461  if ( !mData )
462  {
463  QgsDebugMsg( "Data block not allocated" );
464  return false;
465  }
466 
467  QgsDebugMsgLevel( "set mData to mNoDataValue", 4 );
468  int dataTypeSize = typeSize( mDataType );
469  QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
470 
471  char *nodata = noDataByteArray.data();
472  char *nodataRow = new char[mWidth * dataTypeSize]; // full row of no data
473  for ( int c = 0; c < mWidth; c++ )
474  {
475  memcpy( nodataRow + c * dataTypeSize, nodata, dataTypeSize );
476  }
477 
478  // top and bottom
479  for ( int r = 0; r < mHeight; r++ )
480  {
481  if ( r >= top && r <= bottom ) continue; // middle
482  qgssize i = static_cast< qgssize >( r ) * mWidth;
483  memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( mWidth ) );
484  }
485  // middle
486  for ( int r = top; r <= bottom; r++ )
487  {
488  qgssize i = static_cast< qgssize >( r ) * mWidth;
489  // middle left
490  memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( left ) );
491  // middle right
492  i += right + 1;
493  int w = mWidth - right - 1;
494  memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( w ) );
495  }
496  delete [] nodataRow;
497  }
498  else
499  {
500  // use bitmap
501  if ( !mNoDataBitmap )
502  {
503  if ( !createNoDataBitmap() )
504  {
505  return false;
506  }
507  }
508  QgsDebugMsgLevel( "set mNoDataBitmap to 1", 4 );
509 
510  char *nodataRow = new char[mNoDataBitmapWidth]; // full row of no data
511  // TODO: we can simply set all bytes to 11111111 (~0) I think
512  memset( nodataRow, 0, mNoDataBitmapWidth );
513  for ( int c = 0; c < mWidth; c ++ )
514  {
515  int byte = c / 8;
516  int bit = c % 8;
517  char nodata = 0x80 >> bit;
518  memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
519  }
520 
521  // top and bottom
522  for ( int r = 0; r < mHeight; r++ )
523  {
524  if ( r >= top && r <= bottom ) continue; // middle
525  qgssize i = static_cast< qgssize >( r ) * mNoDataBitmapWidth;
526  memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
527  }
528  // middle
529  memset( nodataRow, 0, mNoDataBitmapWidth );
530  for ( int c = 0; c < mWidth; c ++ )
531  {
532  if ( c >= left && c <= right ) continue; // middle
533  int byte = c / 8;
534  int bit = c % 8;
535  char nodata = 0x80 >> bit;
536  memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
537  }
538  for ( int r = top; r <= bottom; r++ )
539  {
540  qgssize i = static_cast< qgssize >( r ) * mNoDataBitmapWidth;
541  memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
542  }
543  delete [] nodataRow;
544  }
545  return true;
546  }
547  else
548  {
549  // image
550  if ( !mImage )
551  {
552  QgsDebugMsg( "Image not allocated" );
553  return false;
554  }
555 
556  if ( mImage->width() != mWidth || mImage->height() != mHeight )
557  {
558  QgsDebugMsg( "Image and block size differ" );
559  return false;
560  }
561 
562  QgsDebugMsgLevel( QString( "Fill image depth = %1" ).arg( mImage->depth() ), 4 );
563 
564  // TODO: support different depths
565  if ( mImage->depth() != 32 )
566  {
567  QgsDebugMsg( "Unsupported image depth" );
568  return false;
569  }
570 
571  QRgb nodataRgba = NO_DATA_COLOR;
572  QRgb *nodataRow = new QRgb[mWidth]; // full row of no data
573  int rgbSize = sizeof( QRgb );
574  for ( int c = 0; c < mWidth; c ++ )
575  {
576  nodataRow[c] = nodataRgba;
577  }
578 
579  // top and bottom
580  for ( int r = 0; r < mHeight; r++ )
581  {
582  if ( r >= top && r <= bottom ) continue; // middle
583  qgssize i = static_cast< qgssize >( r ) * mWidth;
584  memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( mWidth ) );
585  }
586  // middle
587  for ( int r = top; r <= bottom; r++ )
588  {
589  qgssize i = static_cast< qgssize >( r ) * mWidth;
590  // middle left
591  if ( left > 0 )
592  {
593  memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( left - 1 ) );
594  }
595  // middle right
596  i += right + 1;
597  int w = mWidth - right - 1;
598  memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( w ) );
599  }
600  delete [] nodataRow;
601  return true;
602  }
603 }
604 
605 void QgsRasterBlock::setIsData( int row, int column )
606 {
607  setIsData( static_cast< qgssize >( row )*mWidth + column );
608 }
609 
611 {
612  if ( mHasNoDataValue )
613  {
614  //no data value set, so mNoDataBitmap is not being used
615  return;
616  }
617 
618  if ( !mNoDataBitmap )
619  {
620  return;
621  }
622 
623  // TODO: optimize
624  int row = static_cast< int >( index ) / mWidth;
625  int column = index % mWidth;
626  qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
627  int bit = column % 8;
628  int nodata = 0x80 >> bit;
629  mNoDataBitmap[byte] = mNoDataBitmap[byte] & ~nodata;
630 }
631 
632 QByteArray QgsRasterBlock::data() const
633 {
634  if ( mData )
635  return QByteArray::fromRawData( static_cast<const char *>( mData ), typeSize( mDataType ) * mWidth * mHeight );
636  else if ( mImage && mImage->constBits() )
637  return QByteArray::fromRawData( reinterpret_cast<const char *>( mImage->constBits() ), mImage->byteCount() );
638  else
639  return QByteArray();
640 }
641 
642 void QgsRasterBlock::setData( const QByteArray &data, int offset )
643 {
644  if ( offset < 0 )
645  return; // negative offsets not allowed
646 
647  if ( mData )
648  {
649  int len = std::min( data.size(), typeSize( mDataType ) * mWidth * mHeight - offset );
650  ::memcpy( static_cast<char *>( mData ) + offset, data.constData(), len );
651  }
652  else if ( mImage && mImage->constBits() )
653  {
654  int len = std::min( data.size(), mImage->byteCount() - offset );
655  ::memcpy( mImage->bits() + offset, data.constData(), len );
656  }
657 }
658 
660 {
661  // Not testing type to avoid too much overhead because this method is called per pixel
662  if ( index >= static_cast< qgssize >( mWidth )*mHeight )
663  {
664  QgsDebugMsgLevel( QString( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ), 4 );
665  return nullptr;
666  }
667  if ( mData )
668  {
669  return reinterpret_cast< char * >( mData ) + index * mTypeSize;
670  }
671  if ( mImage && mImage->bits() )
672  {
673  return reinterpret_cast< char * >( mImage->bits() + index * 4 );
674  }
675 
676  return nullptr;
677 }
678 
679 char *QgsRasterBlock::bits( int row, int column )
680 {
681  return bits( static_cast< qgssize >( row ) * mWidth + column );
682 }
683 
685 {
686  if ( mData )
687  {
688  return reinterpret_cast< char * >( mData );
689  }
690  if ( mImage && mImage->bits() )
691  {
692  return reinterpret_cast< char * >( mImage->bits() );
693  }
694 
695  return nullptr;
696 }
697 
699 {
700  if ( isEmpty() ) return false;
701  if ( destDataType == mDataType ) return true;
702 
703  if ( typeIsNumeric( mDataType ) && typeIsNumeric( destDataType ) )
704  {
705  void *data = convert( mData, mDataType, destDataType, static_cast< qgssize >( mWidth ) * static_cast< qgssize >( mHeight ) );
706 
707  if ( !data )
708  {
709  QgsDebugMsg( "Cannot convert raster block" );
710  return false;
711  }
712  qgsFree( mData );
713  mData = data;
714  mDataType = destDataType;
715  mTypeSize = typeSize( mDataType );
716  }
717  else if ( typeIsColor( mDataType ) && typeIsColor( destDataType ) )
718  {
719  QImage::Format format = imageFormat( destDataType );
720  QImage image = mImage->convertToFormat( format );
721  *mImage = image;
722  mDataType = destDataType;
723  mTypeSize = typeSize( mDataType );
724  }
725  else
726  {
727  return false;
728  }
729 
730  return true;
731 }
732 
733 void QgsRasterBlock::applyScaleOffset( double scale, double offset )
734 {
735  if ( isEmpty() ) return;
736  if ( !typeIsNumeric( mDataType ) ) return;
737  if ( scale == 1.0 && offset == 0.0 ) return;
738 
739  qgssize size = static_cast< qgssize >( mWidth ) * mHeight;
740  for ( qgssize i = 0; i < size; ++i )
741  {
742  if ( !isNoData( i ) ) setValue( i, value( i ) * scale + offset );
743  }
744 }
745 
747 {
748  if ( rangeList.isEmpty() )
749  {
750  return;
751  }
752 
753  qgssize size = static_cast< qgssize >( mWidth ) * static_cast< qgssize >( mHeight );
754  for ( qgssize i = 0; i < size; ++i )
755  {
756  double val = value( i );
757  if ( QgsRasterRange::contains( val, rangeList ) )
758  {
759  //setValue( i, mNoDataValue );
760  setIsNoData( i );
761  }
762  }
763 }
764 
765 QImage QgsRasterBlock::image() const
766 {
767  if ( mImage )
768  {
769  return QImage( *mImage );
770  }
771  return QImage();
772 }
773 
774 bool QgsRasterBlock::setImage( const QImage *image )
775 {
776  qgsFree( mData );
777  mData = nullptr;
778  delete mImage;
779  mImage = nullptr;
780  mImage = new QImage( *image );
781  mWidth = mImage->width();
782  mHeight = mImage->height();
783  mDataType = dataType( mImage->format() );
784  mTypeSize = QgsRasterBlock::typeSize( mDataType );
785  mNoDataValue = std::numeric_limits<double>::quiet_NaN();
786  return true;
787 }
788 
790 {
791  /*
792  * IEEE 754 double has 15-17 significant digits. It specifies:
793  *
794  * "If a decimal string with at most 15 significant decimal is converted to
795  * IEEE 754 double precision and then converted back to the same number of
796  * significant decimal, then the final string should match the original;
797  * and if an IEEE 754 double precision is converted to a decimal string with at
798  * least 17 significant decimal and then converted back to double, then the final
799  * number must match the original."
800  *
801  * If printing only 15 digits, some precision could be lost. Printing 17 digits may
802  * add some confusing digits.
803  *
804  * Default 'g' precision on linux is 6 digits, not all significant digits like
805  * some sprintf manuals say.
806  *
807  * We need to ensure that the number printed and used in QLineEdit or XML will
808  * give the same number when parsed.
809  *
810  * Is there a better solution?
811  */
812 
813  QString s;
814 
815  for ( int i = 15; i <= 17; i++ )
816  {
817  s.setNum( value, 'g', i );
818  if ( qgsDoubleNear( s.toDouble(), value ) )
819  {
820  return s;
821  }
822  }
823  // Should not happen
824  QgsDebugMsg( "Cannot correctly parse printed value" );
825  return s;
826 }
827 
829 {
830  /*
831  * IEEE 754 double has 6-9 significant digits. See printValue(double)
832  */
833 
834  QString s;
835 
836  for ( int i = 6; i <= 9; i++ )
837  {
838  s.setNum( value, 'g', i );
839  if ( qgsFloatNear( s.toFloat(), value ) )
840  {
841  return s;
842  }
843  }
844  // Should not happen
845  QgsDebugMsg( "Cannot correctly parse printed value" );
846  return s;
847 }
848 
849 void *QgsRasterBlock::convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size )
850 {
851  int destDataTypeSize = typeSize( destDataType );
852  void *destData = qgsMalloc( destDataTypeSize * size );
853  for ( qgssize i = 0; i < size; i++ )
854  {
855  double value = readValue( srcData, srcDataType, i );
856  writeValue( destData, destDataType, i, value );
857  //double newValue = readValue( destData, destDataType, i );
858  //QgsDebugMsg( QString("convert %1 type %2 to %3: %4 -> %5").arg(i).arg(srcDataType).arg(destDataType).arg( value ).arg( newValue ) );
859  }
860  return destData;
861 }
862 
864 {
865  qgssize size = QgsRasterBlock::typeSize( dataType );
866  QByteArray ba;
867  ba.resize( static_cast< int >( size ) );
868  char *data = ba.data();
869  quint8 uc;
870  quint16 us;
871  qint16 s;
872  quint32 ui;
873  qint32 i;
874  float f;
875  double d;
876  switch ( dataType )
877  {
878  case Qgis::Byte:
879  uc = static_cast< quint8 >( value );
880  memcpy( data, &uc, size );
881  break;
882  case Qgis::UInt16:
883  us = static_cast< quint16 >( value );
884  memcpy( data, &us, size );
885  break;
886  case Qgis::Int16:
887  s = static_cast< qint16 >( value );
888  memcpy( data, &s, size );
889  break;
890  case Qgis::UInt32:
891  ui = static_cast< quint32 >( value );
892  memcpy( data, &ui, size );
893  break;
894  case Qgis::Int32:
895  i = static_cast< qint32 >( value );
896  memcpy( data, &i, size );
897  break;
898  case Qgis::Float32:
899  f = static_cast< float >( value );
900  memcpy( data, &f, size );
901  break;
902  case Qgis::Float64:
903  d = static_cast< double >( value );
904  memcpy( data, &d, size );
905  break;
906  default:
907  QgsDebugMsg( "Data type is not supported" );
908  }
909  return ba;
910 }
911 
912 bool QgsRasterBlock::createNoDataBitmap()
913 {
914  mNoDataBitmapWidth = mWidth / 8 + 1;
915  mNoDataBitmapSize = static_cast< qgssize >( mNoDataBitmapWidth ) * mHeight;
916  QgsDebugMsgLevel( QString( "allocate %1 bytes" ).arg( mNoDataBitmapSize ), 4 );
917  mNoDataBitmap = reinterpret_cast< char * >( qgsMalloc( mNoDataBitmapSize ) );
918  if ( !mNoDataBitmap )
919  {
920  QgsDebugMsg( QString( "Couldn't allocate no data memory of %1 bytes" ).arg( mNoDataBitmapSize ) );
921  return false;
922  }
923  memset( mNoDataBitmap, 0, mNoDataBitmapSize );
924  return true;
925 }
926 
928 {
929  return QStringLiteral( "dataType = %1 width = %2 height = %3" )
930  .arg( mDataType ).arg( mWidth ).arg( mHeight );
931 }
932 
933 QRect QgsRasterBlock::subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent )
934 {
935  QgsDebugMsgLevel( "theExtent = " + extent.toString(), 4 );
936  QgsDebugMsgLevel( "theSubExtent = " + subExtent.toString(), 4 );
937  double xRes = extent.width() / width;
938  double yRes = extent.height() / height;
939 
940  QgsDebugMsgLevel( QString( "theWidth = %1 height = %2 xRes = %3 yRes = %4" ).arg( width ).arg( height ).arg( xRes ).arg( yRes ), 4 );
941 
942  int top = 0;
943  int bottom = height - 1;
944  int left = 0;
945  int right = width - 1;
946 
947  if ( subExtent.yMaximum() < extent.yMaximum() )
948  {
949  top = std::round( ( extent.yMaximum() - subExtent.yMaximum() ) / yRes );
950  }
951  if ( subExtent.yMinimum() > extent.yMinimum() )
952  {
953  bottom = std::round( ( extent.yMaximum() - subExtent.yMinimum() ) / yRes ) - 1;
954  }
955 
956  if ( subExtent.xMinimum() > extent.xMinimum() )
957  {
958  left = std::round( ( subExtent.xMinimum() - extent.xMinimum() ) / xRes );
959  }
960  if ( subExtent.xMaximum() < extent.xMaximum() )
961  {
962  right = std::round( ( subExtent.xMaximum() - extent.xMinimum() ) / xRes ) - 1;
963  }
964  QRect subRect = QRect( left, top, right - left + 1, bottom - top + 1 );
965  QgsDebugMsgLevel( QString( "subRect: %1 %2 %3 %4" ).arg( subRect.x() ).arg( subRect.y() ).arg( subRect.width() ).arg( subRect.height() ), 4 );
966  return subRect;
967 }
void setNoDataValue(double noDataValue)
Sets cell value that will be considered as "no data".
A rectangle specified with double values.
Definition: qgsrectangle.h:38
Thirty two bit signed integer (qint32)
Definition: qgis.h:81
static QString printValue(double value)
Print double value with all necessary significant digits.
void * qgsMalloc(size_t size)
Allocates size bytes and returns a pointer to the allocated memory.
Definition: qgis.cpp:107
bool setIsNoData()
Set the whole block to no data.
static bool contains(double value, const QgsRasterRangeList &rangeList)
Test if value is within the list of ranges.
static double readValue(void *data, Qgis::DataType type, qgssize index)
Qgis::DataType dataType() const
Returns data type.
bool qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference)
Definition: qgis.h:220
static Qgis::DataType typeWithNoDataValue(Qgis::DataType dataType, double *noDataValue)
For given data type returns wider type and sets no data value.
static bool typeIsColor(Qgis::DataType type)
Returns true if data type is color.
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
void applyNoDataValues(const QgsRasterRangeList &rangeList)
bool setValue(int row, int column, double value)
Set value on position.
bool setIsNoDataExcept(QRect exceptRect)
Set the whole block to no data except specified rectangle.
int height() const
Returns the height (number of rows) of the raster block.
static bool typeIsNumeric(Qgis::DataType type)
Returns true if data type is numeric.
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:80
DataType
Raster data types.
Definition: qgis.h:74
Thirty two bit floating point (float)
Definition: qgis.h:82
Sixteen bit signed integer (qint16)
Definition: qgis.h:79
bool isNoData(int row, int column)
Check if value at position is no data.
Complex Int16.
Definition: qgis.h:84
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:210
The Qgis class provides global constants for use throughout the application.
Definition: qgis.h:54
static QRect subRect(const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent)
For extent and width, height find rectangle covered by subextent.
Sixty four bit floating point (double)
Definition: qgis.h:83
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:89
virtual ~QgsRasterBlock()
bool convert(Qgis::DataType destDataType)
Convert data to different type.
bool setImage(const QImage *image)
set image.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:38
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:123
Complex Float32.
Definition: qgis.h:86
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
QRgb color(int row, int column) const
Read a single color.
bool reset(Qgis::DataType dataType, int width, int height)
Reset block.
void setIsData(int row, int column)
Remove no data flag on pixel.
char * bits()
Get pointer to data.
Unknown or unspecified type.
Definition: qgis.h:76
Complex Int32.
Definition: qgis.h:85
static int typeSize(int dataType)
bool isEmpty() const
Returns true if block is empty, i.e.
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:78
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:374
static QByteArray valueBytes(Qgis::DataType dataType, double value)
Get byte array representing a value.
QByteArray data() const
Get access to raw data.
int width() const
Returns the width (number of columns) of the raster block.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:111
QImage image() const
Get image if type is color.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:96
void resetNoDataValue()
Reset no data value: if there was a no data value previously set, it will be discarded.
static void writeValue(void *data, Qgis::DataType type, qgssize index, double value)
QList< QgsRasterRange > QgsRasterRangeList
void applyScaleOffset(double scale, double offset)
Apply band scale and offset to raster block values.
double value(int row, int column) const
Read a single value if type of block is numeric.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:101
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:106
int dataTypeSize() const
Complex Float64.
Definition: qgis.h:87
bool hasNoData() const
Returns true if the block may contain no data.
void setData(const QByteArray &data, int offset=0)
Rewrite raw pixel data.
double noDataValue() const
Return no data value.
void qgsFree(void *ptr)
Frees the memory space pointed to by ptr.
Definition: qgis.cpp:137
Eight bit unsigned integer (quint8)
Definition: qgis.h:77
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition: qgis.h:88
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:130
QString toString() const