QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
Loading...
Searching...
No Matches
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#include <QLocale>
23
24#include "qgslogger.h"
25#include "qgsrasterblock.h"
26#include "qgsrectangle.h"
27#include "qgsgdalutils.h"
28
29#define GDAL_MINMAXELT_NS qgis_gdal
30#include "gdal_minmax_element.hpp"
31
32// See #9101 before any change of NODATA_COLOR!
33const QRgb QgsRasterBlock::NO_DATA_COLOR = qRgba( 0, 0, 0, 0 );
34
36 : mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
37{
38}
39
40QgsRasterBlock::QgsRasterBlock( Qgis::DataType dataType, int width, int height )
41 : mDataType( dataType )
42 , mWidth( width )
43 , mHeight( height )
44 , mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
45{
46 ( void )reset( mDataType, mWidth, mHeight );
47}
48
50{
51 QgsDebugMsgLevel( QStringLiteral( "mData = %1" ).arg( reinterpret_cast< quint64 >( mData ) ), 4 );
52 qgsFree( mData );
53 delete mImage;
54 qgsFree( mNoDataBitmap );
55}
56
57bool QgsRasterBlock::reset( Qgis::DataType dataType, int width, int height )
58{
59 QgsDebugMsgLevel( QStringLiteral( "theWidth= %1 height = %2 dataType = %3" ).arg( width ).arg( height ).arg( qgsEnumValueToKey< Qgis::DataType >( dataType ) ), 4 );
60
61 qgsFree( mData );
62 mData = nullptr;
63 delete mImage;
64 mImage = nullptr;
65 qgsFree( mNoDataBitmap );
66 mNoDataBitmap = nullptr;
68 mTypeSize = 0;
69 mWidth = 0;
70 mHeight = 0;
71 mHasNoDataValue = false;
72 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
73 mValid = false;
74
75 if ( typeIsNumeric( dataType ) )
76 {
77 QgsDebugMsgLevel( QStringLiteral( "Numeric type" ), 4 );
78 const qgssize tSize = typeSize( dataType );
79 QgsDebugMsgLevel( QStringLiteral( "allocate %1 bytes" ).arg( tSize * width * height ), 4 );
80 mData = qgsMalloc( tSize * width * height );
81 if ( !mData )
82 {
83 QgsDebugError( QStringLiteral( "Couldn't allocate data memory of %1 bytes" ).arg( tSize * width * height ) );
84 return false;
85 }
86 }
87 else if ( typeIsColor( dataType ) )
88 {
89 QgsDebugMsgLevel( QStringLiteral( "Color type" ), 4 );
90 const QImage::Format format = imageFormat( dataType );
91 mImage = new QImage( width, height, format );
92 }
93 else
94 {
95 QgsDebugError( QStringLiteral( "Wrong data type" ) );
96 return false;
97 }
98
99 mValid = true;
100 mDataType = dataType;
101 mTypeSize = QgsRasterBlock::typeSize( mDataType );
102 mWidth = width;
103 mHeight = height;
104 QgsDebugMsgLevel( QStringLiteral( "mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5" ).arg( mWidth ).arg( mHeight ).arg( static_cast< int>( mDataType ) )
105 .arg( reinterpret_cast< quint64 >( mData ) ).arg( reinterpret_cast< quint64 >( mImage ) ), 4 );
106 return true;
107}
108
109QImage::Format QgsRasterBlock::imageFormat( Qgis::DataType dataType )
110{
112 {
113 return QImage::Format_ARGB32;
114 }
116 {
117 return QImage::Format_ARGB32_Premultiplied;
118 }
119 return QImage::Format_Invalid;
120}
121
122Qgis::DataType QgsRasterBlock::dataType( QImage::Format format )
123{
124 if ( format == QImage::Format_ARGB32 )
125 {
127 }
128 else if ( format == QImage::Format_ARGB32_Premultiplied )
129 {
131 }
133}
134
136{
137 QgsDebugMsgLevel( QStringLiteral( "mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5" ).arg( mWidth ).arg( mHeight ).arg( qgsEnumValueToKey( mDataType ) )
138 .arg( reinterpret_cast< quint64 >( mData ) ).arg( reinterpret_cast< quint64 >( mImage ) ), 4 );
139 return mWidth == 0 || mHeight == 0 ||
140 ( typeIsNumeric( mDataType ) && !mData ) ||
141 ( typeIsColor( mDataType ) && !mImage );
142}
143
145{
146 switch ( dataType )
147 {
160 return true;
161
165 return false;
166 }
167 return false;
168}
169
171{
172 switch ( dataType )
173 {
176 return true;
177
191 return false;
192 }
193 return false;
194}
195
197{
199
200 switch ( dataType )
201 {
204 *noDataValue = -32768.0;
205 newDataType = Qgis::DataType::Int16;
206 break;
208 *noDataValue = -2147483648.0;
209 newDataType = Qgis::DataType::Int32;
210 break;
212 *noDataValue = -2147483648.0;
213 newDataType = Qgis::DataType::Int32;
214 break;
219 *noDataValue = std::numeric_limits<double>::max() * -1.0;
220 newDataType = Qgis::DataType::Float64;
221 break;
229 QgsDebugError( QStringLiteral( "Unknown data type %1" ).arg( static_cast< int >( dataType ) ) );
231 }
232 QgsDebugMsgLevel( QStringLiteral( "newDataType = %1 noDataValue = %2" ).arg( qgsEnumValueToKey< Qgis::DataType >( newDataType ) ).arg( *noDataValue ), 4 );
233 return newDataType;
234}
235
236void QgsRasterBlock::setNoDataValue( double noDataValue )
237{
238 mHasNoDataValue = true;
239 mNoDataValue = noDataValue;
240}
241
243{
244 mHasNoDataValue = false;
245 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
246}
247
249{
250 QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
251 if ( typeIsNumeric( mDataType ) )
252 {
253 const size_t dataTypeSize = typeSize( mDataType );
254 if ( mHasNoDataValue )
255 {
256 if ( !mData )
257 {
258 QgsDebugError( QStringLiteral( "Data block not allocated" ) );
259 return false;
260 }
261
262 QgsDebugMsgLevel( QStringLiteral( "set mData to mNoDataValue" ), 4 );
263 QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
264 if ( mNoDataValue == 0 )
265 {
266 memset( mData, 0, dataTypeSize * mWidth * mHeight );
267 }
268 else
269 {
270 const char *nodata = noDataByteArray.data();
271 for ( qgssize i = 0; i < static_cast< qgssize >( mWidth )*mHeight; i++ )
272 {
273 memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodata, dataTypeSize );
274 }
275 }
276 }
277 else
278 {
279 // use bitmap
280 if ( !mNoDataBitmap )
281 {
282 if ( !createNoDataBitmap() )
283 {
284 return false;
285 }
286 }
287 QgsDebugMsgLevel( QStringLiteral( "set mNoDataBitmap to 1" ), 4 );
288 memset( mNoDataBitmap, 0xff, mNoDataBitmapSize );
289 if ( mData )
290 {
291 memset( mData, 0, dataTypeSize * mWidth * mHeight );
292 }
293 }
294 return true;
295 }
296 else
297 {
298 // image
299 if ( !mImage )
300 {
301 QgsDebugError( QStringLiteral( "Image not allocated" ) );
302 return false;
303 }
304 QgsDebugMsgLevel( QStringLiteral( "Fill image" ), 4 );
305 mImage->fill( NO_DATA_COLOR );
306 return true;
307 }
308}
309
310bool QgsRasterBlock::setIsNoDataExcept( QRect exceptRect )
311{
312 int top = exceptRect.top();
313 int bottom = exceptRect.bottom();
314 int left = exceptRect.left();
315 int right = exceptRect.right();
316 top = std::min( std::max( top, 0 ), mHeight - 1 );
317 left = std::min( std::max( left, 0 ), mWidth - 1 );
318 bottom = std::max( 0, std::min( bottom, mHeight - 1 ) );
319 right = std::max( 0, std::min( right, mWidth - 1 ) );
320
321 QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
322 if ( typeIsNumeric( mDataType ) )
323 {
324 const size_t dataTypeSize = typeSize( mDataType );
325 if ( mHasNoDataValue )
326 {
327 if ( !mData )
328 {
329 QgsDebugError( QStringLiteral( "Data block not allocated" ) );
330 return false;
331 }
332
333 QgsDebugMsgLevel( QStringLiteral( "set mData to mNoDataValue" ), 4 );
334 QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
335
336 char *nodata = noDataByteArray.data();
337 char *nodataRow = new char[mWidth * dataTypeSize]; // full row of no data
338 for ( int c = 0; c < mWidth; c++ )
339 {
340 memcpy( nodataRow + c * dataTypeSize, nodata, dataTypeSize );
341 }
342
343 // top and bottom
344 for ( int r = 0; r < mHeight; r++ )
345 {
346 if ( r >= top && r <= bottom ) continue; // middle
347 const qgssize i = static_cast< qgssize >( r ) * mWidth;
348 memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( mWidth ) );
349 }
350 // middle
351 for ( int r = top; r <= bottom; r++ )
352 {
353 qgssize i = static_cast< qgssize >( r ) * mWidth;
354 // middle left
355 memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( left ) );
356 // middle right
357 i += right + 1;
358 const int w = mWidth - right - 1;
359 memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( w ) );
360 }
361 delete [] nodataRow;
362 }
363 else
364 {
365 // use bitmap
366 if ( !mNoDataBitmap )
367 {
368 if ( !createNoDataBitmap() )
369 {
370 return false;
371 }
372 }
373 QgsDebugMsgLevel( QStringLiteral( "set mNoDataBitmap to 1" ), 4 );
374
375 if ( mData )
376 {
377 memset( mData, 0, dataTypeSize * mWidth * mHeight );
378 }
379
380 char *nodataRow = new char[mNoDataBitmapWidth]; // full row of no data
381 // TODO: we can simply set all bytes to 11111111 (~0) I think
382 memset( nodataRow, 0, mNoDataBitmapWidth );
383 for ( int c = 0; c < mWidth; c ++ )
384 {
385 const int byte = c / 8;
386 const int bit = c % 8;
387 const char nodata = 0x80 >> bit;
388 memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
389 }
390
391 // top and bottom
392 for ( int r = 0; r < mHeight; r++ )
393 {
394 if ( r >= top && r <= bottom ) continue; // middle
395 const qgssize i = static_cast< qgssize >( r ) * mNoDataBitmapWidth;
396 memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
397 }
398 // middle
399 memset( nodataRow, 0, mNoDataBitmapWidth );
400 for ( int c = 0; c < mWidth; c ++ )
401 {
402 if ( c >= left && c <= right ) continue; // middle
403 const int byte = c / 8;
404 const int bit = c % 8;
405 const char nodata = 0x80 >> bit;
406 memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
407 }
408 for ( int r = top; r <= bottom; r++ )
409 {
410 const qgssize i = static_cast< qgssize >( r ) * mNoDataBitmapWidth;
411 memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
412 }
413 delete [] nodataRow;
414 }
415 return true;
416 }
417 else
418 {
419 // image
420 if ( !mImage )
421 {
422 QgsDebugError( QStringLiteral( "Image not allocated" ) );
423 return false;
424 }
425
426 if ( mImage->width() != mWidth || mImage->height() != mHeight )
427 {
428 QgsDebugError( QStringLiteral( "Image and block size differ" ) );
429 return false;
430 }
431
432 QgsDebugMsgLevel( QStringLiteral( "Fill image depth = %1" ).arg( mImage->depth() ), 4 );
433
434 // TODO: support different depths
435 if ( mImage->depth() != 32 )
436 {
437 QgsDebugError( QStringLiteral( "Unsupported image depth" ) );
438 return false;
439 }
440
441 const QRgb nodataRgba = NO_DATA_COLOR;
442 QRgb *nodataRow = new QRgb[mWidth]; // full row of no data
443 const int rgbSize = sizeof( QRgb );
444 for ( int c = 0; c < mWidth; c ++ )
445 {
446 nodataRow[c] = nodataRgba;
447 }
448
449 // top and bottom
450 for ( int r = 0; r < mHeight; r++ )
451 {
452 if ( r >= top && r <= bottom ) continue; // middle
453 const qgssize i = static_cast< qgssize >( r ) * mWidth;
454 memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( mWidth ) );
455 }
456 // middle
457 for ( int r = top; r <= bottom; r++ )
458 {
459 qgssize i = static_cast< qgssize >( r ) * mWidth;
460 // middle left
461 if ( left > 0 )
462 {
463 memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( left - 1 ) );
464 }
465 // middle right
466 i += right + 1;
467 const int w = mWidth - right - 1;
468 memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( w ) );
469 }
470 delete [] nodataRow;
471 return true;
472 }
473}
474
475QByteArray QgsRasterBlock::data() const
476{
477 if ( mData )
478 return QByteArray::fromRawData( static_cast<const char *>( mData ), typeSize( mDataType ) * mWidth * mHeight );
479 else if ( mImage && mImage->constBits() )
480 return QByteArray::fromRawData( reinterpret_cast<const char *>( mImage->constBits() ), mImage->sizeInBytes() );
481 else
482 return QByteArray();
483}
484
485void QgsRasterBlock::setData( const QByteArray &data, int offset )
486{
487 if ( offset < 0 )
488 return; // negative offsets not allowed
489
490 if ( mData )
491 {
492 const int len = std::min( static_cast<int>( data.size() ), typeSize( mDataType ) * mWidth * mHeight - offset );
493 ::memcpy( static_cast<char *>( mData ) + offset, data.constData(), len );
494 }
495 else if ( mImage && mImage->constBits() )
496 {
497 const qsizetype len = std::min( static_cast< qsizetype >( data.size() ), mImage->sizeInBytes() - offset );
498 ::memcpy( mImage->bits() + offset, data.constData(), len );
499 }
500}
501
503{
504 // Not testing type to avoid too much overhead because this method is called per pixel
505 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
506 {
507 QgsDebugMsgLevel( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ), 4 );
508 return nullptr;
509 }
510 if ( mData )
511 {
512 return reinterpret_cast< char * >( mData ) + index * mTypeSize;
513 }
514 if ( mImage )
515 {
516 if ( uchar *data = mImage->bits() )
517 {
518 return reinterpret_cast< char * >( data + index * 4 );
519 }
520 }
521
522 return nullptr;
523}
524
525const char *QgsRasterBlock::constBits( qgssize index ) const
526{
527 // Not testing type to avoid too much overhead because this method is called per pixel
528 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
529 {
530 QgsDebugMsgLevel( QStringLiteral( "Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ), 4 );
531 return nullptr;
532 }
533 if ( mData )
534 {
535 return reinterpret_cast< const char * >( mData ) + index * mTypeSize;
536 }
537 if ( mImage )
538 {
539 if ( const uchar *data = mImage->constBits() )
540 {
541 return reinterpret_cast< const char * >( data + index * 4 );
542 }
543 }
544
545 return nullptr;
546}
547
548char *QgsRasterBlock::bits( int row, int column )
549{
550 return bits( static_cast< qgssize >( row ) * mWidth + column );
551}
552
554{
555 if ( mData )
556 {
557 return reinterpret_cast< char * >( mData );
558 }
559 if ( mImage )
560 {
561 if ( uchar *data = mImage->bits() )
562 {
563 return reinterpret_cast< char * >( data );
564 }
565 }
566
567 return nullptr;
568}
569
570const char *QgsRasterBlock::constBits() const
571{
572 if ( mData )
573 {
574 return reinterpret_cast< const char * >( mData );
575 }
576 if ( mImage )
577 {
578 if ( const uchar *data = mImage->constBits() )
579 {
580 return reinterpret_cast< const char * >( data );
581 }
582 }
583
584 return nullptr;
585}
586
588{
589 if ( isEmpty() ) return false;
590 if ( destDataType == mDataType ) return true;
591
592 if ( typeIsNumeric( mDataType ) && typeIsNumeric( destDataType ) )
593 {
594 void *data = convert( mData, mDataType, destDataType, static_cast< qgssize >( mWidth ) * static_cast< qgssize >( mHeight ) );
595
596 if ( !data )
597 {
598 QgsDebugError( QStringLiteral( "Cannot convert raster block" ) );
599 return false;
600 }
601 qgsFree( mData );
602 mData = data;
603 mDataType = destDataType;
604 mTypeSize = typeSize( mDataType );
605 }
606 else if ( typeIsColor( mDataType ) && typeIsColor( destDataType ) )
607 {
608 const QImage::Format format = imageFormat( destDataType );
609 const QImage image = mImage->convertToFormat( format );
610 *mImage = image;
611 mDataType = destDataType;
612 mTypeSize = typeSize( mDataType );
613 }
614 else
615 {
616 return false;
617 }
618
619 return true;
620}
621
622void QgsRasterBlock::applyScaleOffset( double scale, double offset )
623{
624 if ( isEmpty() ) return;
625 if ( !typeIsNumeric( mDataType ) ) return;
626 if ( scale == 1.0 && offset == 0.0 ) return;
627
628 const qgssize size = static_cast< qgssize >( mWidth ) * mHeight;
629 for ( qgssize i = 0; i < size; ++i )
630 {
631 if ( !isNoData( i ) ) setValue( i, value( i ) * scale + offset );
632 }
633}
634
636{
637 if ( rangeList.isEmpty() )
638 {
639 return;
640 }
641
642 const qgssize size = static_cast< qgssize >( mWidth ) * static_cast< qgssize >( mHeight );
643 for ( qgssize i = 0; i < size; ++i )
644 {
645 const double val = value( i );
646 if ( QgsRasterRange::contains( val, rangeList ) )
647 {
648 //setValue( i, mNoDataValue );
649 setIsNoData( i );
650 }
651 }
652}
653
655{
656 if ( mImage )
657 {
658 return QImage( *mImage );
659 }
660 return QImage();
661}
662
663bool QgsRasterBlock::setImage( const QImage *image )
664{
665 qgsFree( mData );
666 mData = nullptr;
667 delete mImage;
668 mImage = nullptr;
669 mImage = new QImage( *image );
670 mWidth = mImage->width();
671 mHeight = mImage->height();
672 mDataType = dataType( mImage->format() );
673 mTypeSize = QgsRasterBlock::typeSize( mDataType );
674 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
675 return true;
676}
677
678QString QgsRasterBlock::printValue( double value, bool localized )
679{
680 /*
681 * IEEE 754 double has 15-17 significant digits. It specifies:
682 *
683 * "If a decimal string with at most 15 significant decimal is converted to
684 * IEEE 754 double precision and then converted back to the same number of
685 * significant decimal, then the final string should match the original;
686 * and if an IEEE 754 double precision is converted to a decimal string with at
687 * least 17 significant decimal and then converted back to double, then the final
688 * number must match the original."
689 *
690 * If printing only 15 digits, some precision could be lost. Printing 17 digits may
691 * add some confusing digits.
692 *
693 * Default 'g' precision on linux is 6 digits, not all significant digits like
694 * some sprintf manuals say.
695 *
696 * We need to ensure that the number printed and used in QLineEdit or XML will
697 * give the same number when parsed.
698 *
699 * Is there a better solution?
700 */
701
702 QString s;
703
704 for ( int i = 15; i <= 17; i++ )
705 {
706 s.setNum( value, 'g', i );
707 const double doubleValue { s.toDouble( ) };
708 if ( qgsDoubleNear( doubleValue, value ) )
709 {
710 if ( localized )
711 {
712 return QLocale().toString( doubleValue, 'g', i );
713 }
714 return s;
715 }
716 }
717 // Should not happen
718 QgsDebugError( QStringLiteral( "Cannot correctly parse printed value" ) );
719 return s;
720}
721
722QString QgsRasterBlock::printValue( float value, bool localized )
723{
724 /*
725 * IEEE 754 double has 6-9 significant digits. See printValue(double)
726 */
727
728 QString s;
729
730 for ( int i = 6; i <= 9; i++ )
731 {
732 s.setNum( value, 'g', i );
733 const float floatValue { s.toFloat() };
734 if ( qgsFloatNear( floatValue, value ) )
735 {
736 if ( localized )
737 {
738 return QLocale().toString( floatValue, 'g', i );
739 }
740 return s;
741 }
742 }
743 // Should not happen
744 QgsDebugError( QStringLiteral( "Cannot correctly parse printed value" ) );
745 return s;
746}
747
748void *QgsRasterBlock::convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size )
749{
750 const int destDataTypeSize = typeSize( destDataType );
751 void *destData = qgsMalloc( destDataTypeSize * size );
752 for ( qgssize i = 0; i < size; i++ )
753 {
754 const double value = readValue( srcData, srcDataType, i );
755 writeValue( destData, destDataType, i, value );
756 //double newValue = readValue( destData, destDataType, i );
757 //QgsDebugMsgLevel( QStringLiteral("convert %1 type %2 to %3: %4 -> %5").arg(i).arg(srcDataType).arg(destDataType).arg( value ).arg( newValue ), 2 );
758 }
759 return destData;
760}
761
762QByteArray QgsRasterBlock::valueBytes( Qgis::DataType dataType, double value )
763{
765 QByteArray ba;
766 ba.resize( static_cast< int >( size ) );
767 char *data = ba.data();
768 quint8 uc;
769 quint16 us;
770 qint16 s;
771 quint32 ui;
772 qint32 i;
773 float f;
774 double d;
775 switch ( dataType )
776 {
778 uc = static_cast< quint8 >( value );
779 memcpy( data, &uc, size );
780 break;
782 {
783 const qint8 myint8 = static_cast< qint8 >( value );
784 memcpy( data, &myint8, size );
785 break;
786 }
788 us = static_cast< quint16 >( value );
789 memcpy( data, &us, size );
790 break;
792 s = static_cast< qint16 >( value );
793 memcpy( data, &s, size );
794 break;
796 ui = static_cast< quint32 >( value );
797 memcpy( data, &ui, size );
798 break;
800 i = static_cast< qint32 >( value );
801 memcpy( data, &i, size );
802 break;
804 f = static_cast< float >( value );
805 memcpy( data, &f, size );
806 break;
808 d = static_cast< double >( value );
809 memcpy( data, &d, size );
810 break;
818 QgsDebugError( QStringLiteral( "Data type is not supported" ) );
819 }
820 return ba;
821}
822
823bool QgsRasterBlock::createNoDataBitmap()
824{
825 mNoDataBitmapWidth = mWidth / 8 + 1;
826 mNoDataBitmapSize = static_cast< qgssize >( mNoDataBitmapWidth ) * mHeight;
827 QgsDebugMsgLevel( QStringLiteral( "allocate %1 bytes" ).arg( mNoDataBitmapSize ), 4 );
828 mNoDataBitmap = reinterpret_cast< char * >( qgsMalloc( mNoDataBitmapSize ) );
829 if ( !mNoDataBitmap )
830 {
831 QgsDebugError( QStringLiteral( "Couldn't allocate no data memory of %1 bytes" ).arg( mNoDataBitmapSize ) );
832 return false;
833 }
834 memset( mNoDataBitmap, 0, mNoDataBitmapSize );
835 return true;
836}
837
839{
840 return QStringLiteral( "dataType = %1 width = %2 height = %3" )
841 .arg( qgsEnumValueToKey< Qgis::DataType >( mDataType ) ).arg( mWidth ).arg( mHeight );
842}
843
844QRect QgsRasterBlock::subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent )
845{
846 QgsDebugMsgLevel( "theExtent = " + extent.toString(), 4 );
847 QgsDebugMsgLevel( "theSubExtent = " + subExtent.toString(), 4 );
848 const double xRes = extent.width() / width;
849 const double yRes = extent.height() / height;
850
851 QgsDebugMsgLevel( QStringLiteral( "theWidth = %1 height = %2 xRes = %3 yRes = %4" ).arg( width ).arg( height ).arg( xRes ).arg( yRes ), 4 );
852
853 int top = 0;
854 int bottom = height - 1;
855 int left = 0;
856 int right = width - 1;
857
858 if ( subExtent.yMaximum() < extent.yMaximum() )
859 {
860 top = std::round( ( extent.yMaximum() - subExtent.yMaximum() ) / yRes );
861 }
862 if ( subExtent.yMinimum() > extent.yMinimum() )
863 {
864 bottom = std::round( ( extent.yMaximum() - subExtent.yMinimum() ) / yRes ) - 1;
865 }
866
867 if ( subExtent.xMinimum() > extent.xMinimum() )
868 {
869 left = std::round( ( subExtent.xMinimum() - extent.xMinimum() ) / xRes );
870 }
871 if ( subExtent.xMaximum() < extent.xMaximum() )
872 {
873 right = std::round( ( subExtent.xMaximum() - extent.xMinimum() ) / xRes ) - 1;
874 }
875 QRect subRect = QRect( left, top, right - left + 1, bottom - top + 1 );
876 QgsDebugMsgLevel( QStringLiteral( "subRect: %1 %2 %3 %4" ).arg( subRect.x() ).arg( subRect.y() ).arg( subRect.width() ).arg( subRect.height() ), 4 );
877 return subRect;
878}
879
880bool QgsRasterBlock::minimum( double &minimum, int &row, int &column ) const
881{
882 if ( !mData )
883 {
884 minimum = std::numeric_limits<double>::quiet_NaN();
885 return false;
886 }
887
888 const std::size_t offset = qgis_gdal::min_element( mData, static_cast<std::size_t>( mWidth ) * static_cast< std::size_t>( mHeight ),
889 QgsGdalUtils::gdalDataTypeFromQgisDataType( mDataType ), mHasNoDataValue, mNoDataValue );
890
891 row = static_cast< int >( offset / mWidth );
892 column = static_cast< int >( offset % mWidth );
893 minimum = value( offset );
894
895 return true;
896}
897
898bool QgsRasterBlock::maximum( double &maximum SIP_OUT, int &row SIP_OUT, int &column SIP_OUT ) const
899{
900 if ( !mData )
901 {
902 maximum = std::numeric_limits<double>::quiet_NaN();
903 return false;
904 }
905 const std::size_t offset = qgis_gdal::max_element( mData, static_cast<std::size_t>( mWidth ) * static_cast< std::size_t>( mHeight ),
906 QgsGdalUtils::gdalDataTypeFromQgisDataType( mDataType ), mHasNoDataValue, mNoDataValue );
907
908 row = static_cast< int >( offset / mWidth );
909 column = static_cast< int >( offset % mWidth );
910 maximum = value( offset );
911
912 return true;
913}
914
915bool QgsRasterBlock::minimumMaximum( double &minimum, int &minimumRow, int &minimumColumn, double &maximum, int &maximumRow, int &maximumColumn ) const
916{
917 if ( !mData )
918 {
919 minimum = std::numeric_limits<double>::quiet_NaN();
920 maximum = std::numeric_limits<double>::quiet_NaN();
921 return false;
922 }
923
924 const auto [minOffset, maxOffset] = qgis_gdal::minmax_element( mData, static_cast<std::size_t>( mWidth ) * static_cast< std::size_t>( mHeight ),
925 QgsGdalUtils::gdalDataTypeFromQgisDataType( mDataType ), mHasNoDataValue, mNoDataValue );
926
927 minimumRow = static_cast< int >( minOffset / mWidth );
928 minimumColumn = static_cast< int >( minOffset % mWidth );
929 minimum = value( minOffset );
930
931 maximumRow = static_cast< int >( maxOffset / mWidth );
932 maximumColumn = static_cast< int >( maxOffset % mWidth );
933 maximum = value( maxOffset );
934
935 return true;
936}
DataType
Raster data types.
Definition qgis.h:351
@ CInt32
Complex Int32.
@ Float32
Thirty two bit floating point (float)
@ CFloat64
Complex Float64.
@ Int16
Sixteen bit signed integer (qint16)
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30)
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ UnknownDataType
Unknown or unspecified type.
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
@ Int32
Thirty two bit signed integer (qint32)
@ Float64
Sixty four bit floating point (double)
@ CFloat32
Complex Float32.
@ CInt16
Complex Int16.
@ UInt32
Thirty two bit unsigned integer (quint32)
static GDALDataType gdalDataTypeFromQgisDataType(Qgis::DataType dataType)
Returns the GDAL data type corresponding to the QGIS data type dataType.
bool isEmpty() const
Returns true if block is empty, i.e.
double value(int row, int column) const
Read a single value if type of block is numeric.
static bool typeIsNumeric(Qgis::DataType type)
Returns true if data type is numeric.
bool minimumMaximum(double &minimum, int &minimumRow, int &minimumColumn, double &maximum, int &maximumRow, int &maximumColumn) const
Returns the minimum and maximum value present in the raster block.
int height() const
Returns the height (number of rows) of the raster block.
bool convert(Qgis::DataType destDataType)
Convert data to different type.
bool setIsNoData()
Set the whole block to no data.
bool maximum(double &maximum, int &row, int &column) const
Returns the maximum value present in the raster block.
void resetNoDataValue()
Reset no data value: if there was a no data value previously set, it will be discarded.
int dataTypeSize() const
Data type size in bytes.
QString toString() const
static int typeSize(Qgis::DataType dataType)
Returns the size in bytes for the specified dataType.
bool setIsNoDataExcept(QRect exceptRect)
Set the whole block to no data except specified rectangle.
bool setImage(const QImage *image)
Sets the block data via an image.
QByteArray data() const
Gets access to raw data.
double noDataValue() const
Returns no data value.
const char * constBits() const
Returns a const pointer to block data.
void setData(const QByteArray &data, int offset=0)
Rewrite raw pixel data.
void applyNoDataValues(const QgsRasterRangeList &rangeList)
bool setValue(int row, int column, double value)
Set value on position.
bool isNoData(int row, int column) const
Checks if value at position is no data.
Qgis::DataType dataType() const
Returns data type.
char * bits()
Returns a pointer to block data.
static Qgis::DataType typeWithNoDataValue(Qgis::DataType dataType, double *noDataValue)
For given data type returns wider type and sets no data value.
QImage image() const
Returns an image containing the block data, if the block's data type is color.
void setNoDataValue(double noDataValue)
Sets cell value that will be considered as "no data".
virtual ~QgsRasterBlock()
static void writeValue(void *data, Qgis::DataType type, qgssize index, double value)
int width() const
Returns the width (number of columns) of the raster block.
void applyScaleOffset(double scale, double offset)
Apply band scale and offset to raster block values.
static QString printValue(double value, bool localized=false)
Print double value with all necessary significant digits.
static QRect subRect(const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent)
For extent and width, height find rectangle covered by subextent.
static bool typeIsColor(Qgis::DataType type)
Returns true if data type is color.
static double readValue(void *data, Qgis::DataType type, qgssize index)
static QByteArray valueBytes(Qgis::DataType dataType, double value)
Gets byte array representing a value.
bool reset(Qgis::DataType dataType, int width, int height)
Reset block.
bool minimum(double &minimum, int &row, int &column) const
Returns the minimum value present in the raster block.
bool contains(double value) const
Returns true if this range contains the specified value.
A rectangle specified with double values.
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double width() const
Returns the width of the rectangle.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
double height() const
Returns the height of the rectangle.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
void * qgsMalloc(size_t size)
Allocates size bytes and returns a pointer to the allocated memory.
Definition qgis.cpp:94
void qgsFree(void *ptr)
Frees the memory space pointed to by ptr.
Definition qgis.cpp:116
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6108
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:6465
bool qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference)
Definition qgis.h:5928
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5917
#define SIP_OUT
Definition qgis_sip.h:58
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38
QList< QgsRasterRange > QgsRasterRangeList