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