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