QGIS API Documentation  2.15.0-Master (af20121)
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 
834 void * QgsRasterBlock::convert( void *srcData, QGis::DataType srcDataType, QGis::DataType destDataType, qgssize size )
835 {
836  int destDataTypeSize = typeSize( destDataType );
837  void *destData = qgsMalloc( destDataTypeSize * size );
838  for ( qgssize i = 0; i < size; i++ )
839  {
840  double value = readValue( srcData, srcDataType, i );
841  writeValue( destData, destDataType, i, value );
842  //double newValue = readValue( destData, destDataType, i );
843  //QgsDebugMsg( QString("convert %1 type %2 to %3: %4 -> %5").arg(i).arg(srcDataType).arg(destDataType).arg( value ).arg( newValue ) );
844  }
845  return destData;
846 }
847 
848 QByteArray QgsRasterBlock::valueBytes( QGis::DataType theDataType, double theValue )
849 {
850  qgssize size = QgsRasterBlock::typeSize( theDataType );
851  QByteArray ba;
852  ba.resize( static_cast< int >( size ) );
853  char * data = ba.data();
854  quint8 uc;
855  quint16 us;
856  qint16 s;
857  quint32 ui;
858  qint32 i;
859  float f;
860  double d;
861  switch ( theDataType )
862  {
863  case QGis::Byte:
864  uc = static_cast< quint8 >( theValue );
865  memcpy( data, &uc, size );
866  break;
867  case QGis::UInt16:
868  us = static_cast< quint16 >( theValue );
869  memcpy( data, &us, size );
870  break;
871  case QGis::Int16:
872  s = static_cast< qint16 >( theValue );
873  memcpy( data, &s, size );
874  break;
875  case QGis::UInt32:
876  ui = static_cast< quint32 >( theValue );
877  memcpy( data, &ui, size );
878  break;
879  case QGis::Int32:
880  i = static_cast< qint32 >( theValue );
881  memcpy( data, &i, size );
882  break;
883  case QGis::Float32:
884  f = static_cast< float >( theValue );
885  memcpy( data, &f, size );
886  break;
887  case QGis::Float64:
888  d = static_cast< double >( theValue );
889  memcpy( data, &d, size );
890  break;
891  default:
892  QgsDebugMsg( "Data type is not supported" );
893  }
894  return ba;
895 }
896 
897 bool QgsRasterBlock::createNoDataBitmap()
898 {
899  mNoDataBitmapWidth = mWidth / 8 + 1;
900  mNoDataBitmapSize = static_cast< qgssize >( mNoDataBitmapWidth ) * mHeight;
901  QgsDebugMsgLevel( QString( "allocate %1 bytes" ).arg( mNoDataBitmapSize ), 4 );
902  mNoDataBitmap = reinterpret_cast< char* >( qgsMalloc( mNoDataBitmapSize ) );
903  if ( !mNoDataBitmap )
904  {
905  QgsDebugMsg( QString( "Couldn't allocate no data memory of %1 bytes" ).arg( mNoDataBitmapSize ) );
906  return false;
907  }
908  memset( mNoDataBitmap, 0, mNoDataBitmapSize );
909  return true;
910 }
911 
913 {
914  return QString( "dataType = %1 width = %2 height = %3" )
915  .arg( mDataType ).arg( mWidth ).arg( mHeight );
916 }
917 
918 QRect QgsRasterBlock::subRect( const QgsRectangle & theExtent, int theWidth, int theHeight, const QgsRectangle & theSubExtent )
919 {
920  QgsDebugMsgLevel( "theExtent = " + theExtent.toString(), 4 );
921  QgsDebugMsgLevel( "theSubExtent = " + theSubExtent.toString(), 4 );
922  double xRes = theExtent.width() / theWidth;
923  double yRes = theExtent.height() / theHeight;
924 
925  QgsDebugMsgLevel( QString( "theWidth = %1 theHeight = %2 xRes = %3 yRes = %4" ).arg( theWidth ).arg( theHeight ).arg( xRes ).arg( yRes ), 4 );
926 
927  int top = 0;
928  int bottom = theHeight - 1;
929  int left = 0;
930  int right = theWidth - 1;
931 
932  if ( theSubExtent.yMaximum() < theExtent.yMaximum() )
933  {
934  top = qRound(( theExtent.yMaximum() - theSubExtent.yMaximum() ) / yRes );
935  }
936  if ( theSubExtent.yMinimum() > theExtent.yMinimum() )
937  {
938  bottom = qRound(( theExtent.yMaximum() - theSubExtent.yMinimum() ) / yRes ) - 1;
939  }
940 
941  if ( theSubExtent.xMinimum() > theExtent.xMinimum() )
942  {
943  left = qRound(( theSubExtent.xMinimum() - theExtent.xMinimum() ) / xRes );
944  }
945  if ( theSubExtent.xMaximum() < theExtent.xMaximum() )
946  {
947  right = qRound(( theSubExtent.xMaximum() - theExtent.xMinimum() ) / xRes ) - 1;
948  }
949  QRect subRect = QRect( left, top, right - left + 1, bottom - top + 1 );
950  QgsDebugMsgLevel( QString( "subRect: %1 %2 %3 %4" ).arg( subRect.x() ).arg( subRect.y() ).arg( subRect.width() ).arg( subRect.height() ), 4 );
951  return subRect;
952 }
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:228
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
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:197
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
double noDataValue() const
Return no data value.
Complex Int32.
Definition: qgis.h:144
Thirty two bit floating point (float)
Definition: qgis.h:141
QGis::DataType dataType() const
Returns data type.
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:352
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.
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
bool hasNoData() const
Returns true if the block may contain no data.
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:202
void fill(uint pixelValue)
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:187
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
double value(int row, int column) const
Read a single value if type of block is numeric.
int width() const
int left() const
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
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)
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:489
bool reset(QGis::DataType theDataType, int theWidth, int theHeight)
Reset block.
QString toString() const
int dataTypeSize() const
static void writeValue(void *data, QGis::DataType type, qgssize index, double value)
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)
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
char * data()
uchar * bits()
QImage image() const
Get image if type is color.
int height() const
QRgb color(int row, int column) const
Read a single color.
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.
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:207
QString toString(bool automaticPrecision=false) const
returns string representation of form xmin,ymin xmax,ymax
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:192
void qgsFree(void *ptr)
Frees the memory space pointed to by ptr.
Definition: qgis.cpp:258
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
bool isEmpty() const
Returns true if block is empty, i.e.