Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsrasterblock.h - 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 : : #ifndef QGSRASTERBLOCK_H
19 : : #define QGSRASTERBLOCK_H
20 : :
21 : : #include "qgis_core.h"
22 : : #include "qgis_sip.h"
23 : : #include <limits>
24 : : #include <QImage>
25 : : #include "qgis.h"
26 : : #include "qgserror.h"
27 : : #include "qgslogger.h"
28 : : #include "qgsrasterrange.h"
29 : :
30 : : class QgsRectangle;
31 : :
32 : : /**
33 : : * \ingroup core
34 : : * \brief Raster data container.
35 : : */
36 : : class CORE_EXPORT QgsRasterBlock
37 : : {
38 : : public:
39 : : QgsRasterBlock();
40 : :
41 : : /**
42 : : * \brief Constructor which allocates data block in memory
43 : : * \param dataType raster data type
44 : : * \param width width of data matrix
45 : : * \param height height of data matrix
46 : : */
47 : : QgsRasterBlock( Qgis::DataType dataType, int width, int height );
48 : :
49 : : virtual ~QgsRasterBlock();
50 : :
51 : : /**
52 : : * \brief Reset block
53 : : * \param dataType raster data type
54 : : * \param width width of data matrix
55 : : * \param height height of data matrix
56 : : * \returns TRUE on success
57 : : */
58 : : bool reset( Qgis::DataType dataType, int width, int height );
59 : :
60 : : // TODO: consider if use isValid() at all, isEmpty() should be sufficient
61 : : // and works also if block is valid but empty - difference between valid and empty?
62 : :
63 : : /**
64 : : * \brief Returns TRUE if the block is valid (correctly filled with data).
65 : : * An empty block may still be valid (if zero size block was requested).
66 : : * If the block is not valid, error may be retrieved by error() method.
67 : : */
68 : 0 : bool isValid() const SIP_HOLDGIL { return mValid; }
69 : :
70 : : //! \brief Mark block as valid or invalid
71 : 0 : void setValid( bool valid ) SIP_HOLDGIL { mValid = valid; }
72 : :
73 : : /**
74 : : * Returns TRUE if block is empty, i.e. its size is 0 (zero rows or cols).
75 : : * This method does not return TRUE if size is not zero and all values are
76 : : * 'no data' (null).
77 : : */
78 : : bool isEmpty() const;
79 : :
80 : : /**
81 : : * Returns the size in bytes for the specified \a dataType.
82 : : */
83 : 0 : static int typeSize( int dataType ) SIP_HOLDGIL
84 : : {
85 : : // Modified and extended copy from GDAL
86 : 0 : switch ( dataType )
87 : : {
88 : : case Qgis::Byte:
89 : 0 : return 1;
90 : :
91 : : case Qgis::UInt16:
92 : : case Qgis::Int16:
93 : 0 : return 2;
94 : :
95 : : case Qgis::UInt32:
96 : : case Qgis::Int32:
97 : : case Qgis::Float32:
98 : : case Qgis::CInt16:
99 : 0 : return 4;
100 : :
101 : : case Qgis::Float64:
102 : : case Qgis::CInt32:
103 : : case Qgis::CFloat32:
104 : 0 : return 8;
105 : :
106 : : case Qgis::CFloat64:
107 : 0 : return 16;
108 : :
109 : : case Qgis::ARGB32:
110 : : case Qgis::ARGB32_Premultiplied:
111 : 0 : return 4;
112 : :
113 : : default:
114 : 0 : return 0;
115 : : }
116 : 0 : }
117 : :
118 : : /**
119 : : * Data type size in bytes.
120 : : */
121 : : int dataTypeSize() const SIP_HOLDGIL
122 : : {
123 : : return typeSize( mDataType );
124 : : }
125 : :
126 : : //! Returns TRUE if data type is numeric
127 : : static bool typeIsNumeric( Qgis::DataType type );
128 : :
129 : : //! Returns TRUE if data type is color
130 : : static bool typeIsColor( Qgis::DataType type );
131 : :
132 : : //! Returns data type
133 : 0 : Qgis::DataType dataType() const SIP_HOLDGIL { return mDataType; }
134 : :
135 : : //! For given data type returns wider type and sets no data value
136 : : static Qgis::DataType typeWithNoDataValue( Qgis::DataType dataType, double *noDataValue );
137 : :
138 : : /**
139 : : * TRUE if the block has no data value.
140 : : * \returns TRUE if the block has no data value
141 : : * \see noDataValue(), setNoDataValue(), resetNoDataValue()
142 : : */
143 : 0 : bool hasNoDataValue() const SIP_HOLDGIL { return mHasNoDataValue; }
144 : :
145 : : /**
146 : : * Returns TRUE if the block may contain no data. It does not guarantee
147 : : * that it really contains any no data. It can be used to speed up processing.
148 : : * Not the difference between this method and hasNoDataValue().
149 : : * \returns TRUE if the block may contain no data
150 : : */
151 : 0 : bool hasNoData() const SIP_HOLDGIL
152 : : {
153 : 0 : return mHasNoDataValue || mNoDataBitmap;
154 : : }
155 : :
156 : : /**
157 : : * Sets cell value that will be considered as "no data".
158 : : * \see noDataValue(), hasNoDataValue(), resetNoDataValue()
159 : : * \since QGIS 3.0
160 : : */
161 : : void setNoDataValue( double noDataValue ) SIP_HOLDGIL;
162 : :
163 : : /**
164 : : * Reset no data value: if there was a no data value previously set,
165 : : * it will be discarded.
166 : : * \see noDataValue(), hasNoDataValue(), setNoDataValue()
167 : : * \since QGIS 3.0
168 : : */
169 : : void resetNoDataValue() SIP_HOLDGIL;
170 : :
171 : : /**
172 : : * Returns no data value. If the block does not have a no data value the
173 : : * returned value is undefined.
174 : : * \returns No data value
175 : : * \see hasNoDataValue(), setNoDataValue(), resetNoDataValue()
176 : : */
177 : 0 : double noDataValue() const SIP_HOLDGIL { return mNoDataValue; }
178 : :
179 : : /**
180 : : * Gets byte array representing a value.
181 : : * \param dataType data type
182 : : * \param value value
183 : : * \returns byte array representing the value
184 : : */
185 : : static QByteArray valueBytes( Qgis::DataType dataType, double value );
186 : :
187 : : /**
188 : : * Read a single value if type of block is numeric. If type is color,
189 : : * returned value is undefined.
190 : : * \param row row index
191 : : * \param column column index
192 : : * \returns value
193 : : * \see valueAndNoData()
194 : : */
195 : 0 : double value( int row, int column ) const SIP_HOLDGIL
196 : : {
197 : 0 : return value( static_cast< qgssize >( row ) * mWidth + column );
198 : : }
199 : :
200 : : /**
201 : : * Reads a single value from the pixel at \a row and \a column, if type of block is numeric. If type is color,
202 : : * returned value is undefined.
203 : : *
204 : : * Additionally, the \a isNoData argument will be set to TRUE if the pixel represents a nodata value. This method
205 : : * is more efficient then calling isNoData() and value() separately.
206 : : *
207 : : * \note Not available in Python bindings
208 : : * \see value()
209 : : * \see isNoData()
210 : : * \since QGIS 3.6
211 : : */
212 : 0 : double valueAndNoData( int row, int column, bool &isNoData ) const SIP_SKIP
213 : : {
214 : 0 : return valueAndNoData( static_cast< qgssize >( row ) * mWidth + column, isNoData );
215 : : }
216 : :
217 : : /**
218 : : * Reads a single value if type of block is numeric. If type is color,
219 : : * returned value is undefined.
220 : : * \param index data matrix index (long type in Python)
221 : : * \returns value
222 : : * \see valueAndNoData()
223 : : */
224 : : inline double value( qgssize index ) const SIP_HOLDGIL;
225 : :
226 : : /**
227 : : * Reads a single value from the pixel at the specified data matrix \a index, if type of block is numeric. If type is color,
228 : : * returned value is undefined.
229 : : *
230 : : * Additionally, the \a isNoData argument will be set to TRUE if the pixel represents a nodata value. This method
231 : : * is more efficient then calling isNoData() and value() separately.
232 : : *
233 : : * \note Not available in Python bindings
234 : : * \see value()
235 : : * \see isNoData()
236 : : * \since QGIS 3.6
237 : : */
238 : : inline double valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP;
239 : :
240 : : /**
241 : : * Gives direct access to the raster block data.
242 : : * The data type of the block must be Qgis::Byte otherwise it returns NULLPTR.
243 : : * Useful for most efficient read access.
244 : : * \note not available in Python bindings
245 : : * \since QGIS 3.4
246 : : */
247 : 0 : const quint8 *byteData() const SIP_SKIP
248 : : {
249 : 0 : if ( mDataType != Qgis::Byte )
250 : 0 : return nullptr;
251 : 0 : return static_cast< const quint8 * >( mData );
252 : 0 : }
253 : :
254 : : /**
255 : : * \brief Read a single color
256 : : * \param row row index
257 : : * \param column column index
258 : : * \returns color
259 : : */
260 : 0 : QRgb color( int row, int column ) const SIP_HOLDGIL
261 : : {
262 : 0 : if ( !mImage ) return NO_DATA_COLOR;
263 : :
264 : 0 : return mImage->pixel( column, row );
265 : 0 : }
266 : :
267 : : /**
268 : : * \brief Read a single value
269 : : * \param index data matrix index (long type in Python)
270 : : * \returns color
271 : : */
272 : 0 : QRgb color( qgssize index ) const SIP_HOLDGIL
273 : : {
274 : 0 : int row = static_cast< int >( std::floor( static_cast< double >( index ) / mWidth ) );
275 : 0 : int column = index % mWidth;
276 : 0 : return color( row, column );
277 : : }
278 : :
279 : : /**
280 : : * Checks if value at position is no data
281 : : * \param row row index
282 : : * \param column column index
283 : : * \returns TRUE if value is no data
284 : : * \see valueAndNoData()
285 : : */
286 : 0 : bool isNoData( int row, int column ) const SIP_HOLDGIL
287 : : {
288 : 0 : return isNoData( static_cast< qgssize >( row ) * mWidth + column );
289 : : }
290 : :
291 : : /**
292 : : * Check if value at position is no data
293 : : * \param row row index
294 : : * \param column column index
295 : : * \returns TRUE if value is no data
296 : : * \see valueAndNoData()
297 : : */
298 : 0 : bool isNoData( qgssize row, qgssize column ) const SIP_HOLDGIL
299 : : {
300 : 0 : return isNoData( row * static_cast< qgssize >( mWidth ) + column );
301 : : }
302 : :
303 : : /**
304 : : * Check if value at position is no data
305 : : * \param index data matrix index (long type in Python)
306 : : * \returns TRUE if value is no data
307 : : * \see valueAndNoData()
308 : : */
309 : 0 : bool isNoData( qgssize index ) const SIP_HOLDGIL
310 : : {
311 : 0 : if ( !mHasNoDataValue && !mNoDataBitmap )
312 : 0 : return false;
313 : 0 : if ( index >= static_cast< qgssize >( mWidth )*mHeight )
314 : : {
315 : 0 : QgsDebugMsg( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
316 : 0 : return true; // we consider no data if outside
317 : : }
318 : 0 : if ( mHasNoDataValue )
319 : : {
320 : 0 : double value = readValue( mData, mDataType, index );
321 : 0 : return isNoDataValue( value );
322 : : }
323 : : // use no data bitmap
324 : 0 : if ( !mNoDataBitmap )
325 : : {
326 : : // no data are not defined
327 : 0 : return false;
328 : : }
329 : : // TODO: optimize
330 : 0 : int row = static_cast< int >( index ) / mWidth;
331 : 0 : int column = index % mWidth;
332 : 0 : qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
333 : 0 : int bit = column % 8;
334 : 0 : 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 : 0 : return mNoDataBitmap[byte] & mask;
338 : 0 : }
339 : :
340 : : /**
341 : : * \brief Set value on position
342 : : * \param row row index
343 : : * \param column column index
344 : : * \param value the value to be set
345 : : * \returns TRUE on success
346 : : */
347 : 0 : bool setValue( int row, int column, double value ) SIP_HOLDGIL
348 : : {
349 : 0 : return setValue( static_cast< qgssize >( row ) * mWidth + column, value );
350 : : }
351 : :
352 : : /**
353 : : * \brief Set value on index (indexed line by line)
354 : : * \param index data matrix index (long type in Python)
355 : : * \param value the value to be set
356 : : * \returns TRUE on success
357 : : */
358 : 0 : bool setValue( qgssize index, double value ) SIP_HOLDGIL
359 : : {
360 : 0 : if ( !mData )
361 : : {
362 : 0 : QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
363 : 0 : return false;
364 : : }
365 : 0 : if ( index >= static_cast< qgssize >( mWidth ) *mHeight )
366 : : {
367 : 0 : QgsDebugMsg( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
368 : 0 : return false;
369 : : }
370 : 0 : writeValue( mData, mDataType, index, value );
371 : 0 : return true;
372 : 0 : }
373 : :
374 : : /**
375 : : * \brief Set color on position
376 : : * \param row row index
377 : : * \param column column index
378 : : * \param color the color to be set, QRgb value
379 : : * \returns TRUE on success
380 : : */
381 : 0 : bool setColor( int row, int column, QRgb color ) SIP_HOLDGIL
382 : : {
383 : 0 : return setColor( static_cast< qgssize >( row ) * mWidth + column, color );
384 : : }
385 : :
386 : : /**
387 : : * \brief Set color on index (indexed line by line)
388 : : * \param index data matrix index (long type in Python)
389 : : * \param color the color to be set, QRgb value
390 : : * \returns TRUE on success
391 : : */
392 : 0 : bool setColor( qgssize index, QRgb color ) SIP_HOLDGIL
393 : : {
394 : 0 : if ( !mImage )
395 : : {
396 : 0 : QgsDebugMsg( QStringLiteral( "Image not allocated" ) );
397 : 0 : return false;
398 : : }
399 : :
400 : 0 : if ( index >= static_cast< qgssize >( mImage->width() ) * mImage->height() )
401 : : {
402 : 0 : QgsDebugMsg( QStringLiteral( "index %1 out of range" ).arg( index ) );
403 : 0 : return false;
404 : : }
405 : :
406 : : // setPixel() is slow, see Qt doc -> use direct access
407 : 0 : QRgb *bits = reinterpret_cast< QRgb * >( mImage->bits() );
408 : 0 : bits[index] = color;
409 : 0 : return true;
410 : 0 : }
411 : :
412 : : /**
413 : : * Gives direct read/write access to the raster RGB data.
414 : : * The data type of the block must be Qgis::ARGB32 or Qgis::ARGB32_Premultiplied otherwise it returns NULLPTR.
415 : : * Useful for most efficient read/write access to RGB blocks.
416 : : * \note not available in Python bindings
417 : : * \since QGIS 3.4
418 : : */
419 : 0 : QRgb *colorData() SIP_SKIP
420 : : {
421 : 0 : if ( !mImage )
422 : 0 : return nullptr;
423 : 0 : return reinterpret_cast< QRgb * >( mImage->bits() );
424 : 0 : }
425 : :
426 : : /**
427 : : * \brief Set no data on pixel
428 : : * \param row row index
429 : : * \param column column index
430 : : * \returns TRUE on success
431 : : */
432 : 0 : bool setIsNoData( int row, int column ) SIP_HOLDGIL
433 : : {
434 : 0 : return setIsNoData( static_cast< qgssize >( row ) * mWidth + column );
435 : : }
436 : :
437 : : /**
438 : : * \brief Set no data on pixel
439 : : * \param index data matrix index (long type in Python)
440 : : * \returns TRUE on success
441 : : */
442 : 0 : bool setIsNoData( qgssize index ) SIP_HOLDGIL
443 : : {
444 : 0 : if ( mHasNoDataValue )
445 : : {
446 : 0 : return setValue( index, mNoDataValue );
447 : : }
448 : : else
449 : : {
450 : 0 : if ( !mNoDataBitmap )
451 : : {
452 : 0 : if ( !createNoDataBitmap() )
453 : : {
454 : 0 : return false;
455 : : }
456 : 0 : }
457 : : // TODO: optimize
458 : 0 : int row = static_cast< int >( index ) / mWidth;
459 : 0 : int column = index % mWidth;
460 : 0 : qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
461 : 0 : int bit = column % 8;
462 : 0 : int nodata = 0x80 >> bit;
463 : : //QgsDebugMsg ( QString("set byte = %1 bit = %2 no data by %3").arg(byte).arg(bit).arg(nodata, 0,2 ) );
464 : 0 : mNoDataBitmap[byte] = mNoDataBitmap[byte] | nodata;
465 : 0 : return true;
466 : : }
467 : 0 : }
468 : :
469 : : /**
470 : : * \brief Set the whole block to no data
471 : : * \returns TRUE on success
472 : : */
473 : : bool setIsNoData();
474 : :
475 : : /**
476 : : * \brief Set the whole block to no data except specified rectangle
477 : : * \returns TRUE on success
478 : : */
479 : : bool setIsNoDataExcept( QRect exceptRect );
480 : :
481 : : /**
482 : : * \brief Remove no data flag on pixel. If the raster block does not have an explicit
483 : : * no data value set then an internal map of no data pixels is maintained for the block.
484 : : * In this case it is possible to reset a pixel to flag it as having valid data using this
485 : : * method. This method has no effect for raster blocks with an explicit no data value set.
486 : : * \param row row index
487 : : * \param column column index
488 : : * \since QGIS 2.10
489 : : */
490 : 0 : void setIsData( int row, int column ) SIP_HOLDGIL
491 : : {
492 : 0 : setIsData( static_cast< qgssize >( row )*mWidth + column );
493 : 0 : }
494 : :
495 : : /**
496 : : * \brief Remove no data flag on pixel. If the raster block does not have an explicit
497 : : * no data value set then an internal map of no data pixels is maintained for the block.
498 : : * In this case it is possible to reset a pixel to flag it as having valid data using this
499 : : * method. This method has no effect for raster blocks with an explicit no data value set.
500 : : * \param index data matrix index (long type in Python)
501 : : * \since QGIS 2.10
502 : : */
503 : 0 : void setIsData( qgssize index ) SIP_HOLDGIL
504 : : {
505 : 0 : if ( mHasNoDataValue )
506 : : {
507 : : //no data value set, so mNoDataBitmap is not being used
508 : 0 : return;
509 : : }
510 : :
511 : 0 : if ( !mNoDataBitmap )
512 : : {
513 : 0 : return;
514 : : }
515 : :
516 : : // TODO: optimize
517 : 0 : int row = static_cast< int >( index ) / mWidth;
518 : 0 : int column = index % mWidth;
519 : 0 : qgssize byte = static_cast< qgssize >( row ) * mNoDataBitmapWidth + column / 8;
520 : 0 : int bit = column % 8;
521 : 0 : int nodata = 0x80 >> bit;
522 : 0 : mNoDataBitmap[byte] = mNoDataBitmap[byte] & ~nodata;
523 : 0 : }
524 : :
525 : : /**
526 : : * Gets access to raw data.
527 : : * The returned QByteArray instance is not a copy of the data: it only refers to the array
528 : : * owned by the QgsRasterBlock, therefore it is only valid while the QgsRasterBlock object
529 : : * still exists. Writing to the returned QByteArray will not affect the original data:
530 : : * a deep copy of the data will be made and only the local copy will be modified.
531 : : * \note in Python the method returns ordinary bytes object as the
532 : : * \since QGIS 3.0
533 : : */
534 : : QByteArray data() const;
535 : :
536 : : /**
537 : : * Rewrite raw pixel data.
538 : : * If the data array is shorter than the internal array within the raster block object,
539 : : * pixels at the end will stay untouched. If the data array is longer than the internal
540 : : * array, only the initial data from the input array will be used.
541 : : * Optionally it is possible to set non-zero offset (in bytes) if the input data should
542 : : * overwrite data somewhere in the middle of the internal buffer.
543 : : * \since QGIS 3.0
544 : : */
545 : : void setData( const QByteArray &data, int offset = 0 );
546 : :
547 : : /**
548 : : * Returns a pointer to block data.
549 : : * \param row row index
550 : : * \param column column index
551 : : * \note not available in Python bindings
552 : : */
553 : : char *bits( int row, int column ) SIP_SKIP;
554 : :
555 : : /**
556 : : * Returns a pointer to block data.
557 : : * \param index data matrix index (long type in Python)
558 : : * \note not available in Python bindings
559 : : */
560 : : char *bits( qgssize index ) SIP_SKIP;
561 : :
562 : : /**
563 : : * Returns a pointer to block data.
564 : : * \note not available in Python bindings
565 : : */
566 : : char *bits() SIP_SKIP;
567 : :
568 : : /**
569 : : * \brief Print double value with all necessary significant digits.
570 : : * It is ensured that conversion back to double gives the same number.
571 : : * \param value the value to be printed
572 : : * \returns string representing the value
573 : : */
574 : : static QString printValue( double value );
575 : :
576 : : /**
577 : : * \brief Print float value with all necessary significant digits.
578 : : * It is ensured that conversion back to float gives the same number.
579 : : * \param value the value to be printed
580 : : * \returns string representing the value
581 : : * \note not available in Python bindings
582 : : * \since QGIS 2.16
583 : : */
584 : : static QString printValue( float value ) SIP_SKIP;
585 : :
586 : : /**
587 : : * \brief Convert data to different type.
588 : : * \param destDataType dest data type
589 : : * \returns TRUE on success
590 : : */
591 : : bool convert( Qgis::DataType destDataType );
592 : :
593 : : /**
594 : : * Returns an image containing the block data, if the block's data type is color.
595 : : */
596 : : QImage image() const;
597 : :
598 : : /**
599 : : * Sets the block data via an \a image.
600 : : * \returns TRUE on success
601 : : */
602 : : bool setImage( const QImage *image );
603 : :
604 : : //! \note not available in Python bindings
605 : : inline static double readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP;
606 : :
607 : : //! \note not available in Python bindings
608 : : inline static void writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP;
609 : :
610 : : void applyNoDataValues( const QgsRasterRangeList &rangeList );
611 : :
612 : : /**
613 : : * Apply band scale and offset to raster block values
614 : : * \since QGIS 2.3
615 : : */
616 : : void applyScaleOffset( double scale, double offset );
617 : :
618 : : //! Returns the last error
619 : : QgsError error() const { return mError; }
620 : :
621 : : //! Sets the last error
622 : 0 : void setError( const QgsError &error ) { mError = error;}
623 : :
624 : : QString toString() const;
625 : :
626 : : /**
627 : : * \brief For extent and width, height find rectangle covered by subextent.
628 : : * The output rect has x oriented from left to right and y from top to bottom
629 : : * (upper-left to lower-right orientation).
630 : : * \param extent extent, usually the larger
631 : : * \param width numbers of columns in theExtent
632 : : * \param height numbers of rows in theExtent
633 : : * \param subExtent extent, usually smaller than theExtent
634 : : * \returns the rectangle covered by sub extent
635 : : */
636 : : static QRect subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent );
637 : :
638 : : /**
639 : : * Returns the width (number of columns) of the raster block.
640 : : * \see height
641 : : * \since QGIS 2.10
642 : : */
643 : 0 : int width() const SIP_HOLDGIL { return mWidth; }
644 : :
645 : : /**
646 : : * Returns the height (number of rows) of the raster block.
647 : : * \see width
648 : : * \since QGIS 2.10
649 : : */
650 : 0 : int height() const SIP_HOLDGIL { return mHeight; }
651 : :
652 : : private:
653 : : static QImage::Format imageFormat( Qgis::DataType dataType );
654 : : static Qgis::DataType dataType( QImage::Format format );
655 : :
656 : : /**
657 : : * Test if value is nodata comparing to noDataValue
658 : : * \param value tested value
659 : : * \param noDataValue no data value
660 : : * \returns TRUE if value is nodata
661 : : */
662 : : static bool isNoDataValue( double value, double noDataValue )
663 : : {
664 : : // TODO: optimize no data value test by memcmp()
665 : : // More precise would be std::isnan(value) && std::isnan(noDataValue(bandNo)), but probably
666 : : // not important and slower
667 : : return std::isnan( value ) ||
668 : : qgsDoubleNear( value, noDataValue );
669 : : }
670 : :
671 : : /**
672 : : * Test if value is nodata for specific band
673 : : * \param value tested value
674 : : * \returns TRUE if value is nodata
675 : : */
676 : : inline bool isNoDataValue( double value ) const;
677 : :
678 : : /**
679 : : * Allocate no data bitmap
680 : : * \returns TRUE on success
681 : : */
682 : : bool createNoDataBitmap();
683 : :
684 : : /**
685 : : * \brief Convert block of data from one type to another. Original block memory
686 : : * is not release.
687 : : * \param srcData source data
688 : : * \param srcDataType source data type
689 : : * \param destDataType dest data type
690 : : * \param size block size (width * height)
691 : : * \returns block of data in destDataType
692 : : */
693 : : static void *convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size );
694 : :
695 : : // Valid
696 : : bool mValid = true;
697 : :
698 : : // Data type
699 : : Qgis::DataType mDataType = Qgis::UnknownDataType;
700 : :
701 : : // Data type size in bytes, to make bits() fast
702 : : int mTypeSize = 0;
703 : :
704 : : // Width
705 : : int mWidth = 0;
706 : :
707 : : // Height
708 : : int mHeight = 0;
709 : :
710 : : // Has no data value
711 : : bool mHasNoDataValue = false;
712 : :
713 : : // No data value
714 : : double mNoDataValue;
715 : :
716 : : static const QRgb NO_DATA_COLOR;
717 : :
718 : : // Data block for numerical data types, not used with image data types
719 : : // QByteArray does not seem to be intended for large data blocks, does it?
720 : : void *mData = nullptr;
721 : :
722 : : // Image for image data types, not used with numerical data types
723 : : QImage *mImage = nullptr;
724 : :
725 : : // Bitmap of no data. One bit for each pixel. Bit is 1 if a pixels is no data.
726 : : // Each row is represented by whole number of bytes (last bits may be unused)
727 : : // to make processing rows easy.
728 : : char *mNoDataBitmap = nullptr;
729 : :
730 : : // number of bytes in mNoDataBitmap row
731 : : int mNoDataBitmapWidth = 0;
732 : :
733 : : // total size in bytes of mNoDataBitmap
734 : : qgssize mNoDataBitmapSize = 0;
735 : :
736 : : // Error
737 : : QgsError mError;
738 : : };
739 : :
740 : 0 : inline double QgsRasterBlock::readValue( void *data, Qgis::DataType type, qgssize index ) SIP_SKIP
741 : : {
742 : 0 : if ( !data )
743 : : {
744 : 0 : return std::numeric_limits<double>::quiet_NaN();
745 : : }
746 : :
747 : 0 : switch ( type )
748 : : {
749 : : case Qgis::Byte:
750 : 0 : return static_cast< double >( ( static_cast< quint8 * >( data ) )[index] );
751 : : case Qgis::UInt16:
752 : 0 : return static_cast< double >( ( static_cast< quint16 * >( data ) )[index] );
753 : : case Qgis::Int16:
754 : 0 : return static_cast< double >( ( static_cast< qint16 * >( data ) )[index] );
755 : : case Qgis::UInt32:
756 : 0 : return static_cast< double >( ( static_cast< quint32 * >( data ) )[index] );
757 : : case Qgis::Int32:
758 : 0 : return static_cast< double >( ( static_cast< qint32 * >( data ) )[index] );
759 : : case Qgis::Float32:
760 : 0 : return static_cast< double >( ( static_cast< float * >( data ) )[index] );
761 : : case Qgis::Float64:
762 : 0 : return static_cast< double >( ( static_cast< double * >( data ) )[index] );
763 : : default:
764 : 0 : QgsDebugMsg( QStringLiteral( "Data type %1 is not supported" ).arg( type ) );
765 : 0 : break;
766 : : }
767 : :
768 : 0 : return std::numeric_limits<double>::quiet_NaN();
769 : 0 : }
770 : :
771 : 0 : inline void QgsRasterBlock::writeValue( void *data, Qgis::DataType type, qgssize index, double value ) SIP_SKIP
772 : : {
773 : 0 : if ( !data ) return;
774 : :
775 : 0 : switch ( type )
776 : : {
777 : : case Qgis::Byte:
778 : 0 : ( static_cast< quint8 * >( data ) )[index] = static_cast< quint8 >( value );
779 : 0 : break;
780 : : case Qgis::UInt16:
781 : 0 : ( static_cast< quint16 * >( data ) )[index] = static_cast< quint16 >( value );
782 : 0 : break;
783 : : case Qgis::Int16:
784 : 0 : ( static_cast< qint16 * >( data ) )[index] = static_cast< qint16 >( value );
785 : 0 : break;
786 : : case Qgis::UInt32:
787 : 0 : ( static_cast< quint32 * >( data ) )[index] = static_cast< quint32 >( value );
788 : 0 : break;
789 : : case Qgis::Int32:
790 : 0 : ( static_cast< qint32 * >( data ) )[index] = static_cast< qint32 >( value );
791 : 0 : break;
792 : : case Qgis::Float32:
793 : 0 : ( static_cast< float * >( data ) )[index] = static_cast< float >( value );
794 : 0 : break;
795 : : case Qgis::Float64:
796 : 0 : ( static_cast< double * >( data ) )[index] = value;
797 : 0 : break;
798 : : default:
799 : 0 : QgsDebugMsg( QStringLiteral( "Data type %1 is not supported" ).arg( type ) );
800 : 0 : break;
801 : : }
802 : 0 : }
803 : :
804 : 0 : inline double QgsRasterBlock::value( qgssize index ) const SIP_SKIP
805 : : {
806 : 0 : if ( !mData )
807 : : {
808 : 0 : QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
809 : 0 : return std::numeric_limits<double>::quiet_NaN();
810 : : }
811 : 0 : return readValue( mData, mDataType, index );
812 : 0 : }
813 : :
814 : 0 : inline double QgsRasterBlock::valueAndNoData( qgssize index, bool &isNoData ) const SIP_SKIP
815 : : {
816 : 0 : if ( !mData )
817 : : {
818 : 0 : QgsDebugMsg( QStringLiteral( "Data block not allocated" ) );
819 : 0 : isNoData = true;
820 : 0 : return std::numeric_limits<double>::quiet_NaN();
821 : : }
822 : 0 : if ( index >= static_cast< qgssize >( mWidth )*mHeight )
823 : : {
824 : 0 : QgsDebugMsg( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ) );
825 : 0 : isNoData = true; // we consider no data if outside
826 : 0 : return std::numeric_limits<double>::quiet_NaN();
827 : : }
828 : :
829 : 0 : const double val = readValue( mData, mDataType, index );
830 : :
831 : 0 : if ( !mHasNoDataValue && !mNoDataBitmap )
832 : : {
833 : 0 : isNoData = false;
834 : 0 : return val;
835 : : }
836 : :
837 : 0 : if ( mHasNoDataValue )
838 : : {
839 : 0 : isNoData = isNoDataValue( val );
840 : 0 : return val;
841 : : }
842 : : // use no data bitmap
843 : 0 : if ( !mNoDataBitmap )
844 : : {
845 : : // no data are not defined
846 : 0 : isNoData = false;
847 : 0 : return val;
848 : : }
849 : :
850 : : // no data is a bitmap
851 : 0 : isNoData = QgsRasterBlock::isNoData( index );
852 : 0 : return val;
853 : 0 : }
854 : :
855 : 0 : inline bool QgsRasterBlock::isNoDataValue( double value ) const SIP_SKIP
856 : : {
857 : 0 : return std::isnan( value ) || qgsDoubleNear( value, mNoDataValue );
858 : : }
859 : :
860 : : #endif
861 : :
862 : :
|