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