QGIS API Documentation  2.10.1-Pisa
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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  ( 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( 0 )
71  , mImage( 0 )
72  , mNoDataBitmap( 0 )
73  , mNoDataBitmapWidth( 0 )
74  , mNoDataBitmapSize( 0 )
75 {
76  ( void )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  break;
265  default:
266  QgsDebugMsg( QString( "Unknow data type %1" ).arg( dataType ) );
267  return QGis::UnknownDataType;
268  break;
269  }
270  QgsDebugMsg( QString( "newDataType = %1 noDataValue = %2" ).arg( newDataType ).arg( *noDataValue ) );
271  return newDataType;
272 }
273 
275 {
276  return mHasNoDataValue || mNoDataBitmap != 0;
277 }
278 
279 bool QgsRasterBlock::isNoDataValue( double value, double noDataValue )
280 {
281  // TODO: optimize no data value test by memcmp()
282  // More precise would be qIsNaN(value) && qIsNaN(noDataValue(bandNo)), but probably
283  // not important and slower
284  if ( qIsNaN( value ) ||
285  qgsDoubleNear( value, noDataValue ) )
286  {
287  return true;
288  }
289  return false;
290 }
291 
292 double QgsRasterBlock::value( int row, int column ) const
293 {
294  return value(( qgssize )row*mWidth + column );
295 }
296 
298 {
299  int row = floor(( double )index / mWidth );
300  int column = index % mWidth;
301  return color( row, column );
302 }
303 
304 QRgb QgsRasterBlock::color( int row, int column ) const
305 {
306  if ( !mImage ) return mNoDataColor;
307 
308  return mImage->pixel( column, row );
309 }
310 
312 {
313  if ( !mHasNoDataValue && !mNoDataBitmap ) return false;
314  if ( index >= ( qgssize )mWidth*mHeight )
315  {
316  QgsDebugMsg( QString( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
317  return true; // we consider no data if outside
318  }
319  if ( mHasNoDataValue )
320  {
321  double value = readValue( mData, mDataType, index );
322  return isNoDataValue( value );
323  }
324  // use no data bitmap
325  if ( mNoDataBitmap == 0 )
326  {
327  // no data are not defined
328  return false;
329  }
330  // TODO: optimize
331  int row = ( int ) index / mWidth;
332  int column = index % mWidth;
333  qgssize byte = ( qgssize )row * mNoDataBitmapWidth + column / 8;
334  int bit = column % 8;
335  int mask = 0x80 >> bit;
336  //int x = mNoDataBitmap[byte] & mask;
337  //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) ) );
338  return mNoDataBitmap[byte] & mask;
339 }
340 
341 bool QgsRasterBlock::isNoData( int row, int column )
342 {
343  return isNoData(( qgssize )row*mWidth + column );
344 }
345 
347 {
348  if ( !mData )
349  {
350  QgsDebugMsg( "Data block not allocated" );
351  return false;
352  }
353  if ( index >= ( qgssize )mWidth*mHeight )
354  {
355  QgsDebugMsg( QString( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
356  return false;
357  }
358  writeValue( mData, mDataType, index, value );
359  return true;
360 }
361 
362 bool QgsRasterBlock::setValue( int row, int column, double value )
363 {
364  return setValue(( qgssize )row*mWidth + column, value );
365 }
366 
367 bool QgsRasterBlock::setColor( int row, int column, QRgb color )
368 {
369  return setColor(( qgssize )row*mWidth + column, color );
370 }
371 
373 {
374  if ( !mImage )
375  {
376  QgsDebugMsg( "Image not allocated" );
377  return false;
378  }
379 
380  if ( index >= ( qgssize )mImage->width()* mImage->height() )
381  {
382  QgsDebugMsg( QString( "index %1 out of range" ).arg( index ) );
383  return false;
384  }
385 
386  // setPixel() is slow, see Qt doc -> use direct access
387  QRgb* bits = ( QRgb* )mImage->bits();
388  bits[index] = color;
389  return true;
390 }
391 
392 bool QgsRasterBlock::setIsNoData( int row, int column )
393 {
394  return setIsNoData(( qgssize )row*mWidth + column );
395 }
396 
398 {
399  if ( mHasNoDataValue )
400  {
401  return setValue( index, mNoDataValue );
402  }
403  else
404  {
405  if ( mNoDataBitmap == 0 )
406  {
407  if ( !createNoDataBitmap() )
408  {
409  return false;
410  }
411  }
412  // TODO: optimize
413  int row = ( int ) index / mWidth;
414  int column = index % mWidth;
415  qgssize byte = ( qgssize )row * mNoDataBitmapWidth + column / 8;
416  int bit = column % 8;
417  int nodata = 0x80 >> bit;
418  //QgsDebugMsg ( QString("set byte = %1 bit = %2 no data by %3").arg(byte).arg(bit).arg(nodata, 0,2 ) );
419  mNoDataBitmap[byte] = mNoDataBitmap[byte] | nodata;
420  return true;
421  }
422 }
423 
425 {
426  QgsDebugMsg( "Entered" );
427  if ( typeIsNumeric( mDataType ) )
428  {
429  if ( mHasNoDataValue )
430  {
431  if ( !mData )
432  {
433  QgsDebugMsg( "Data block not allocated" );
434  return false;
435  }
436 
437  QgsDebugMsg( "set mData to mNoDataValue" );
438  int dataTypeSize = typeSize( mDataType );
439  QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
440 
441  char *nodata = noDataByteArray.data();
442  for ( qgssize i = 0; i < ( qgssize )mWidth*mHeight; i++ )
443  {
444  memcpy(( char* )mData + i*dataTypeSize, nodata, dataTypeSize );
445  }
446  }
447  else
448  {
449  // use bitmap
450  if ( mNoDataBitmap == 0 )
451  {
452  if ( !createNoDataBitmap() )
453  {
454  return false;
455  }
456  }
457  QgsDebugMsg( "set mNoDataBitmap to 1" );
458  memset( mNoDataBitmap, 0xff, mNoDataBitmapSize );
459  }
460  return true;
461  }
462  else
463  {
464  // image
465  if ( !mImage )
466  {
467  QgsDebugMsg( "Image not allocated" );
468  return false;
469  }
470  QgsDebugMsg( "Fill image" );
471  mImage->fill( mNoDataColor );
472  return true;
473  }
474 }
475 
476 bool QgsRasterBlock::setIsNoDataExcept( const QRect & theExceptRect )
477 {
478  int top = theExceptRect.top();
479  int bottom = theExceptRect.bottom();
480  int left = theExceptRect.left();
481  int right = theExceptRect.right();
482  top = qMin( qMax( top, 0 ), mHeight - 1 );
483  left = qMin( qMax( left, 0 ), mWidth - 1 );
484  bottom = qMax( 0, qMin( bottom, mHeight - 1 ) );
485  right = qMax( 0, qMin( right, mWidth - 1 ) );
486 
487  QgsDebugMsg( "Entered" );
488  if ( typeIsNumeric( mDataType ) )
489  {
490  if ( mHasNoDataValue )
491  {
492  if ( !mData )
493  {
494  QgsDebugMsg( "Data block not allocated" );
495  return false;
496  }
497 
498  QgsDebugMsg( "set mData to mNoDataValue" );
499  int dataTypeSize = typeSize( mDataType );
500  QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
501 
502  char *nodata = noDataByteArray.data();
503  char *nodataRow = new char[mWidth*dataTypeSize]; // full row of no data
504  for ( int c = 0; c < mWidth; c++ )
505  {
506  memcpy( nodataRow + c*dataTypeSize, nodata, dataTypeSize );
507  }
508 
509  // top and bottom
510  for ( int r = 0; r < mHeight; r++ )
511  {
512  if ( r >= top && r <= bottom ) continue; // middle
513  qgssize i = ( qgssize )r * mWidth;
514  memcpy(( char* )mData + i*dataTypeSize, nodataRow, dataTypeSize*mWidth );
515  }
516  // middle
517  for ( int r = top; r <= bottom; r++ )
518  {
519  qgssize i = ( qgssize )r * mWidth;
520  // middle left
521  memcpy(( char* )mData + i*dataTypeSize, nodataRow, dataTypeSize*left );
522  // middle right
523  i += right + 1;
524  int w = mWidth - right - 1;
525  memcpy(( char* )mData + i*dataTypeSize, nodataRow, dataTypeSize*w );
526  }
527  delete [] nodataRow;
528  }
529  else
530  {
531  // use bitmap
532  if ( mNoDataBitmap == 0 )
533  {
534  if ( !createNoDataBitmap() )
535  {
536  return false;
537  }
538  }
539  QgsDebugMsg( "set mNoDataBitmap to 1" );
540 
541  char *nodataRow = new char[mNoDataBitmapWidth]; // full row of no data
542  // TODO: we can simply set all bytes to 11111111 (~0) I think
543  memset( nodataRow, 0, mNoDataBitmapWidth );
544  for ( int c = 0; c < mWidth; c ++ )
545  {
546  int byte = c / 8;
547  int bit = c % 8;
548  char nodata = 0x80 >> bit;
549  memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
550  }
551 
552  // top and bottom
553  for ( int r = 0; r < mHeight; r++ )
554  {
555  if ( r >= top && r <= bottom ) continue; // middle
556  qgssize i = ( qgssize )r * mNoDataBitmapWidth;
557  memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
558  }
559  // middle
560  memset( nodataRow, 0, mNoDataBitmapWidth );
561  for ( int c = 0; c < mWidth; c ++ )
562  {
563  if ( c >= left && c <= right ) continue; // middle
564  int byte = c / 8;
565  int bit = c % 8;
566  char nodata = 0x80 >> bit;
567  memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
568  }
569  for ( int r = top; r <= bottom; r++ )
570  {
571  qgssize i = ( qgssize )r * mNoDataBitmapWidth;
572  memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
573  }
574  delete [] nodataRow;
575  }
576  return true;
577  }
578  else
579  {
580  // image
581  if ( !mImage )
582  {
583  QgsDebugMsg( "Image not allocated" );
584  return false;
585  }
586 
587  if ( mImage->width() != mWidth || mImage->height() != mHeight )
588  {
589  QgsDebugMsg( "Image and block size differ" );
590  return false;
591  }
592 
593  QgsDebugMsg( QString( "Fill image depth = %1" ).arg( mImage->depth() ) );
594 
595  // TODO: support different depths
596  if ( mImage->depth() != 32 )
597  {
598  QgsDebugMsg( "Unsupported image depth" );
599  return false;
600  }
601 
602  QRgb nodataRgba = mNoDataColor;
603  QRgb *nodataRow = new QRgb[mWidth]; // full row of no data
604  int rgbSize = sizeof( QRgb );
605  for ( int c = 0; c < mWidth; c ++ )
606  {
607  nodataRow[c] = nodataRgba;
608  }
609 
610  // top and bottom
611  for ( int r = 0; r < mHeight; r++ )
612  {
613  if ( r >= top && r <= bottom ) continue; // middle
614  qgssize i = ( qgssize )r * mWidth;
615  memcpy(( void * )( mImage->bits() + rgbSize*i ), nodataRow, rgbSize*mWidth );
616  }
617  // middle
618  for ( int r = top; r <= bottom; r++ )
619  {
620  qgssize i = ( qgssize )r * mWidth;
621  // middle left
622  if ( left > 0 )
623  {
624  memcpy(( void * )( mImage->bits() + rgbSize*i ), nodataRow, rgbSize*( left - 1 ) );
625  }
626  // middle right
627  i += right + 1;
628  int w = mWidth - right - 1;
629  memcpy(( void * )( mImage->bits() + rgbSize*i ), nodataRow, rgbSize*w );
630  }
631  delete [] nodataRow;
632  return true;
633  }
634 }
635 
636 void QgsRasterBlock::setIsData( int row, int column )
637 {
638  setIsData(( qgssize )row*mWidth + column );
639 }
640 
642 {
643  if ( mHasNoDataValue )
644  {
645  //no data value set, so mNoDataBitmap is not being used
646  return;
647  }
648 
649  if ( mNoDataBitmap == 0 )
650  {
651  return;
652  }
653 
654  // TODO: optimize
655  int row = ( int ) index / mWidth;
656  int column = index % mWidth;
657  qgssize byte = ( qgssize )row * mNoDataBitmapWidth + column / 8;
658  int bit = column % 8;
659  int nodata = 0x80 >> bit;
660  mNoDataBitmap[byte] = mNoDataBitmap[byte] & ~nodata;
661 }
662 
664 {
665  // Not testing type to avoid too much overhead because this method is called per pixel
666  if ( index >= ( qgssize )mWidth*mHeight )
667  {
668  QgsDebugMsg( QString( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
669  return 0;
670  }
671  if ( mData )
672  {
673  return ( char* )mData + index * mTypeSize;
674  }
675  if ( mImage && mImage->bits() )
676  {
677  return ( char* )( mImage->bits() + index * 4 );
678  }
679 
680  return 0;
681 }
682 
683 char * QgsRasterBlock::bits( int row, int column )
684 {
685  return bits(( qgssize )row*mWidth + column );
686 }
687 
689 {
690  if ( mData )
691  {
692  return ( char* )mData;
693  }
694  if ( mImage && mImage->bits() )
695  {
696  return ( char* )( mImage->bits() );
697  }
698 
699  return 0;
700 }
701 
703 {
704  if ( isEmpty() ) return false;
705  if ( destDataType == mDataType ) return true;
706 
707  if ( typeIsNumeric( mDataType ) && typeIsNumeric( destDataType ) )
708  {
709  void *data = convert( mData, mDataType, destDataType, ( qgssize )mWidth * ( qgssize )mHeight );
710 
711  if ( data == 0 )
712  {
713  QgsDebugMsg( "Cannot convert raster block" );
714  return false;
715  }
716  qgsFree( mData );
717  mData = data;
718  mDataType = destDataType;
719  mTypeSize = typeSize( mDataType );
720  }
721  else if ( typeIsColor( mDataType ) && typeIsColor( destDataType ) )
722  {
723  QImage::Format format = imageFormat( destDataType );
724  QImage image = mImage->convertToFormat( format );
725  *mImage = image;
726  mDataType = destDataType;
727  mTypeSize = typeSize( mDataType );
728  }
729  else
730  {
731  return false;
732  }
733 
734  return true;
735 }
736 
737 void QgsRasterBlock::applyScaleOffset( double scale, double offset )
738 {
739  if ( isEmpty() ) return;
740  if ( !typeIsNumeric( mDataType ) ) return;
741  if ( scale == 1.0 && offset == 0.0 ) return;
742 
743  qgssize size = ( qgssize ) mWidth * mHeight;
744  for ( qgssize i = 0; i < size; ++i )
745  {
746  if ( !isNoData( i ) ) setValue( i, value( i ) * scale + offset );
747  }
748  return;
749 }
750 
752 {
753  if ( rangeList.isEmpty() )
754  {
755  return;
756  }
757 
758  qgssize size = ( qgssize )mWidth * ( qgssize )mHeight;
759  for ( qgssize i = 0; i < size; ++i )
760  {
761  double val = value( i );
762  if ( QgsRasterRange::contains( val, rangeList ) )
763  {
764  //setValue( i, mNoDataValue );
765  setIsNoData( i );
766  }
767  }
768 }
769 
771 {
772  if ( mImage )
773  {
774  return QImage( *mImage );
775  }
776  return QImage();
777 }
778 
779 bool QgsRasterBlock::setImage( const QImage * image )
780 {
781  qgsFree( mData );
782  mData = 0;
783  delete mImage;
784  mImage = 0;
785  mImage = new QImage( *image );
786  mWidth = mImage->width();
787  mHeight = mImage->height();
788  mDataType = dataType( mImage->format() );
789  mTypeSize = QgsRasterBlock::typeSize( mDataType );
790  mNoDataValue = std::numeric_limits<double>::quiet_NaN();
791  return true;
792 }
793 
795 {
796  /*
797  * IEEE 754 double has 15-17 significant digits. It specifies:
798  *
799  * "If a decimal string with at most 15 significant decimal is converted to
800  * IEEE 754 double precision and then converted back to the same number of
801  * significant decimal, then the final string should match the original;
802  * and if an IEEE 754 double precision is converted to a decimal string with at
803  * least 17 significant decimal and then converted back to double, then the final
804  * number must match the original."
805  *
806  * If printing only 15 digits, some precision could be lost. Printing 17 digits may
807  * add some confusing digits.
808  *
809  * Default 'g' precision on linux is 6 digits, not all significant digits like
810  * some sprintf manuals say.
811  *
812  * We need to ensure that the number printed and used in QLineEdit or XML will
813  * give the same number when parsed.
814  *
815  * Is there a better solution?
816  */
817 
818  QString s;
819 
820  for ( int i = 15; i <= 17; i++ )
821  {
822  s.setNum( value, 'g', i );
823  if ( s.toDouble() == value )
824  {
825  return s;
826  }
827  }
828  // Should not happen
829  QgsDebugMsg( "Cannot correctly parse printed value" );
830  return s;
831 }
832 
833 void * QgsRasterBlock::convert( void *srcData, QGis::DataType srcDataType, QGis::DataType destDataType, qgssize size )
834 {
835  int destDataTypeSize = typeSize( destDataType );
836  void *destData = qgsMalloc( destDataTypeSize * size );
837  for ( qgssize i = 0; i < size; i++ )
838  {
839  double value = readValue( srcData, srcDataType, i );
840  writeValue( destData, destDataType, i, value );
841  //double newValue = readValue( destData, destDataType, i );
842  //QgsDebugMsg( QString("convert %1 type %2 to %3: %4 -> %5").arg(i).arg(srcDataType).arg(destDataType).arg( value ).arg( newValue ) );
843  }
844  return destData;
845 }
846 
847 QByteArray QgsRasterBlock::valueBytes( QGis::DataType theDataType, double theValue )
848 {
849  qgssize size = QgsRasterBlock::typeSize( theDataType );
850  QByteArray ba;
851  ba.resize(( int )size );
852  char * data = ba.data();
853  quint8 uc;
854  quint16 us;
855  qint16 s;
856  quint32 ui;
857  qint32 i;
858  float f;
859  double d;
860  switch ( theDataType )
861  {
862  case QGis::Byte:
863  uc = ( quint8 )theValue;
864  memcpy( data, &uc, size );
865  break;
866  case QGis::UInt16:
867  us = ( quint16 )theValue;
868  memcpy( data, &us, size );
869  break;
870  case QGis::Int16:
871  s = ( qint16 )theValue;
872  memcpy( data, &s, size );
873  break;
874  case QGis::UInt32:
875  ui = ( quint32 )theValue;
876  memcpy( data, &ui, size );
877  break;
878  case QGis::Int32:
879  i = ( qint32 )theValue;
880  memcpy( data, &i, size );
881  break;
882  case QGis::Float32:
883  f = ( float )theValue;
884  memcpy( data, &f, size );
885  break;
886  case QGis::Float64:
887  d = ( double )theValue;
888  memcpy( data, &d, size );
889  break;
890  default:
891  QgsDebugMsg( "Data type is not supported" );
892  }
893  return ba;
894 }
895 
896 bool QgsRasterBlock::createNoDataBitmap()
897 {
898  mNoDataBitmapWidth = mWidth / 8 + 1;
899  mNoDataBitmapSize = ( qgssize )mNoDataBitmapWidth * mHeight;
900  QgsDebugMsg( QString( "allocate %1 bytes" ).arg( mNoDataBitmapSize ) );
901  mNoDataBitmap = ( char* )qgsMalloc( mNoDataBitmapSize );
902  if ( mNoDataBitmap == 0 )
903  {
904  QgsDebugMsg( QString( "Couldn't allocate no data memory of %1 bytes" ).arg( mNoDataBitmapSize ) );
905  return false;
906  }
907  memset( mNoDataBitmap, 0, mNoDataBitmapSize );
908  return true;
909 }
910 
912 {
913  return QString( "dataType = %1 width = %2 height = %3" )
914  .arg( mDataType ).arg( mWidth ).arg( mHeight );
915 }
916 
917 QRect QgsRasterBlock::subRect( const QgsRectangle & theExtent, int theWidth, int theHeight, const QgsRectangle & theSubExtent )
918 {
919  QgsDebugMsg( "theExtent = " + theExtent.toString() );
920  QgsDebugMsg( "theSubExtent = " + theSubExtent.toString() );
921  double xRes = theExtent.width() / theWidth;
922  double yRes = theExtent.height() / theHeight;
923 
924  QgsDebugMsg( QString( "theWidth = %1 theHeight = %2 xRes = %3 yRes = %4" ).arg( theWidth ).arg( theHeight ).arg( xRes ).arg( yRes ) );
925 
926  int top = 0;
927  int bottom = theHeight - 1;
928  int left = 0;
929  int right = theWidth - 1;
930 
931  if ( theSubExtent.yMaximum() < theExtent.yMaximum() )
932  {
933  top = qRound(( theExtent.yMaximum() - theSubExtent.yMaximum() ) / yRes );
934  }
935  if ( theSubExtent.yMinimum() > theExtent.yMinimum() )
936  {
937  bottom = qRound(( theExtent.yMaximum() - theSubExtent.yMinimum() ) / yRes ) - 1;
938  }
939 
940  if ( theSubExtent.xMinimum() > theExtent.xMinimum() )
941  {
942  left = qRound(( theSubExtent.xMinimum() - theExtent.xMinimum() ) / xRes );
943  }
944  if ( theSubExtent.xMaximum() < theExtent.xMaximum() )
945  {
946  right = qRound(( theSubExtent.xMaximum() - theExtent.xMinimum() ) / xRes ) - 1;
947  }
948  QRect subRect = QRect( left, top, right - left + 1, bottom - top + 1 );
949  QgsDebugMsg( QString( "subRect: %1 %2 %3 %4" ).arg( subRect.x() ).arg( subRect.y() ).arg( subRect.width() ).arg( subRect.height() ) );
950  return subRect;
951 }
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:202
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.
int right() const
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:192
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
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:34
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)
Definition: qgis.h:350
void resize(int size)
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
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:197
void fill(uint pixelValue)
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:182
bool setImage(const QImage *image)
set image.
int top() const
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.
void setIsData(int row, int column)
Remove no data flag on pixel.
char * bits()
Get pointer to data.
static int typeSize(int dataType)
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:449
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:204
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:202
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:187
void qgsFree(void *ptr)
Frees the memory space pointed to by ptr.
Definition: qgis.cpp:232
Format format() const
int max(int a, int b)
Definition: util.h:87
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:207
bool isEmpty() const
Returns true if block is empty, i.e.