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