QGIS API Documentation 3.99.0-Master (d270888f95f)
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 "qgsrasterblock.h"
19
20#include <limits>
21
22#include "qgsgdalutils.h"
23#include "qgslogger.h"
24#include "qgsrectangle.h"
25
26#include <QByteArray>
27#include <QColor>
28#include <QLocale>
29#include <QString>
30
31using namespace Qt::StringLiterals;
32
33#define GDAL_MINMAXELT_NS qgis_gdal
34#include "gdal_minmax_element.hpp"
35
36// See #9101 before any change of NODATA_COLOR!
37const QRgb QgsRasterBlock::NO_DATA_COLOR = qRgba( 0, 0, 0, 0 );
38
40 : mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
41{
42}
43
45 : mDataType( dataType )
46 , mWidth( width )
47 , mHeight( height )
48 , mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
49{
50 ( void )reset( mDataType, mWidth, mHeight );
51}
52
54{
55 QgsDebugMsgLevel( u"mData = %1"_s.arg( reinterpret_cast< quint64 >( mData ) ), 4 );
56 qgsFree( mData );
57 delete mImage;
58 qgsFree( mNoDataBitmap );
59}
60
62{
63 QgsDebugMsgLevel( u"theWidth= %1 height = %2 dataType = %3"_s.arg( width ).arg( height ).arg( qgsEnumValueToKey< Qgis::DataType >( dataType ) ), 4 );
64
65 qgsFree( mData );
66 mData = nullptr;
67 delete mImage;
68 mImage = nullptr;
69 qgsFree( mNoDataBitmap );
70 mNoDataBitmap = nullptr;
72 mTypeSize = 0;
73 mWidth = 0;
74 mHeight = 0;
75 mHasNoDataValue = false;
76 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
77 mValid = false;
78
79 if ( typeIsNumeric( dataType ) )
80 {
81 QgsDebugMsgLevel( u"Numeric type"_s, 4 );
82 const qgssize tSize = typeSize( dataType );
83 QgsDebugMsgLevel( u"allocate %1 bytes"_s.arg( tSize * width * height ), 4 );
84 mData = qgsMalloc( tSize * width * height );
85 if ( !mData )
86 {
87 QgsDebugError( u"Couldn't allocate data memory of %1 bytes"_s.arg( tSize * width * height ) );
88 return false;
89 }
90 }
91 else if ( typeIsColor( dataType ) )
92 {
93 QgsDebugMsgLevel( u"Color type"_s, 4 );
94 const QImage::Format format = imageFormat( dataType );
95 mImage = new QImage( width, height, format );
96 }
97 else
98 {
99 QgsDebugError( u"Wrong data type"_s );
100 return false;
101 }
102
103 mValid = true;
104 mDataType = dataType;
105 mTypeSize = QgsRasterBlock::typeSize( mDataType );
106 mWidth = width;
107 mHeight = height;
108 QgsDebugMsgLevel( u"mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5"_s.arg( mWidth ).arg( mHeight ).arg( static_cast< int>( mDataType ) )
109 .arg( reinterpret_cast< quint64 >( mData ) ).arg( reinterpret_cast< quint64 >( mImage ) ), 4 );
110 return true;
111}
112
113QImage::Format QgsRasterBlock::imageFormat( Qgis::DataType dataType )
114{
116 {
117 return QImage::Format_ARGB32;
118 }
120 {
121 return QImage::Format_ARGB32_Premultiplied;
122 }
123 return QImage::Format_Invalid;
124}
125
126Qgis::DataType QgsRasterBlock::dataType( QImage::Format format )
127{
128 if ( format == QImage::Format_ARGB32 )
129 {
131 }
132 else if ( format == QImage::Format_ARGB32_Premultiplied )
133 {
135 }
137}
138
140{
141 QgsDebugMsgLevel( u"mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5"_s.arg( mWidth ).arg( mHeight ).arg( qgsEnumValueToKey( mDataType ) )
142 .arg( reinterpret_cast< quint64 >( mData ) ).arg( reinterpret_cast< quint64 >( mImage ) ), 4 );
143 return mWidth == 0 || mHeight == 0 ||
144 ( typeIsNumeric( mDataType ) && !mData ) ||
145 ( typeIsColor( mDataType ) && !mImage );
146}
147
173
175{
176 switch ( type )
177 {
189 return false;
190
195 return true;
196 }
197 return false;
198}
199
225
227{
229
230 switch ( dataType )
231 {
234 *noDataValue = -32768.0;
235 newDataType = Qgis::DataType::Int16;
236 break;
238 *noDataValue = -2147483648.0;
239 newDataType = Qgis::DataType::Int32;
240 break;
242 *noDataValue = -2147483648.0;
243 newDataType = Qgis::DataType::Int32;
244 break;
249 *noDataValue = std::numeric_limits<double>::max() * -1.0;
250 newDataType = Qgis::DataType::Float64;
251 break;
259 QgsDebugError( u"Unknown data type %1"_s.arg( static_cast< int >( dataType ) ) );
261 }
262 QgsDebugMsgLevel( u"newDataType = %1 noDataValue = %2"_s.arg( qgsEnumValueToKey< Qgis::DataType >( newDataType ) ).arg( *noDataValue ), 4 );
263 return newDataType;
264}
265
267{
268 mHasNoDataValue = true;
269 mNoDataValue = noDataValue;
270}
271
273{
274 mHasNoDataValue = false;
275 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
276}
277
279{
280 QgsDebugMsgLevel( u"Entered"_s, 4 );
281 if ( typeIsNumeric( mDataType ) )
282 {
283 if ( mHasNoDataValue )
284 {
285 return fill( mNoDataValue );
286 }
287 else
288 {
289 // use bitmap
290 if ( !mNoDataBitmap )
291 {
292 if ( !createNoDataBitmap() )
293 {
294 return false;
295 }
296 }
297 QgsDebugMsgLevel( u"set mNoDataBitmap to 1"_s, 4 );
298 memset( mNoDataBitmap, 0xff, mNoDataBitmapSize );
299 const size_t dataTypeSize = typeSize( mDataType );
300 if ( mData )
301 {
302 memset( mData, 0, dataTypeSize * mWidth * mHeight );
303 }
304 }
305 return true;
306 }
307 else
308 {
309 // image
310 if ( !mImage )
311 {
312 QgsDebugError( u"Image not allocated"_s );
313 return false;
314 }
315 QgsDebugMsgLevel( u"Fill image"_s, 4 );
316 mImage->fill( NO_DATA_COLOR );
317 return true;
318 }
319}
320
321bool QgsRasterBlock::setIsNoDataExcept( QRect exceptRect )
322{
323 int top = exceptRect.top();
324 int bottom = exceptRect.bottom();
325 int left = exceptRect.left();
326 int right = exceptRect.right();
327 top = std::min( std::max( top, 0 ), mHeight - 1 );
328 left = std::min( std::max( left, 0 ), mWidth - 1 );
329 bottom = std::max( 0, std::min( bottom, mHeight - 1 ) );
330 right = std::max( 0, std::min( right, mWidth - 1 ) );
331
332 QgsDebugMsgLevel( u"Entered"_s, 4 );
333 if ( typeIsNumeric( mDataType ) )
334 {
335 const size_t dataTypeSize = typeSize( mDataType );
336 if ( mHasNoDataValue )
337 {
338 if ( !mData )
339 {
340 QgsDebugError( u"Data block not allocated"_s );
341 return false;
342 }
343
344 QgsDebugMsgLevel( u"set mData to mNoDataValue"_s, 4 );
345 QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
346
347 char *nodata = noDataByteArray.data();
348 char *nodataRow = new char[mWidth * dataTypeSize]; // full row of no data
349 for ( int c = 0; c < mWidth; c++ )
350 {
351 memcpy( nodataRow + c * dataTypeSize, nodata, dataTypeSize );
352 }
353
354 // top and bottom
355 for ( int r = 0; r < mHeight; r++ )
356 {
357 if ( r >= top && r <= bottom ) continue; // middle
358 const qgssize i = static_cast< qgssize >( r ) * mWidth;
359 memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( mWidth ) );
360 }
361 // middle
362 for ( int r = top; r <= bottom; r++ )
363 {
364 qgssize i = static_cast< qgssize >( r ) * mWidth;
365 // middle left
366 memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( left ) );
367 // middle right
368 i += right + 1;
369 const int w = mWidth - right - 1;
370 memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( w ) );
371 }
372 delete [] nodataRow;
373 }
374 else
375 {
376 // use bitmap
377 if ( !mNoDataBitmap )
378 {
379 if ( !createNoDataBitmap() )
380 {
381 return false;
382 }
383 }
384 QgsDebugMsgLevel( u"set mNoDataBitmap to 1"_s, 4 );
385
386 if ( mData )
387 {
388 memset( mData, 0, dataTypeSize * mWidth * mHeight );
389 }
390
391 char *nodataRow = new char[mNoDataBitmapWidth]; // full row of no data
392 // TODO: we can simply set all bytes to 11111111 (~0) I think
393 memset( nodataRow, 0, mNoDataBitmapWidth );
394 for ( int c = 0; c < mWidth; c ++ )
395 {
396 const int byte = c / 8;
397 const int bit = c % 8;
398 const char nodata = 0x80 >> bit;
399 memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
400 }
401
402 // top and bottom
403 for ( int r = 0; r < mHeight; r++ )
404 {
405 if ( r >= top && r <= bottom ) continue; // middle
406 const qgssize i = static_cast< qgssize >( r ) * mNoDataBitmapWidth;
407 memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
408 }
409 // middle
410 memset( nodataRow, 0, mNoDataBitmapWidth );
411 for ( int c = 0; c < mWidth; c ++ )
412 {
413 if ( c >= left && c <= right ) continue; // middle
414 const int byte = c / 8;
415 const int bit = c % 8;
416 const char nodata = 0x80 >> bit;
417 memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
418 }
419 for ( int r = top; r <= bottom; r++ )
420 {
421 const qgssize i = static_cast< qgssize >( r ) * mNoDataBitmapWidth;
422 memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
423 }
424 delete [] nodataRow;
425 }
426 return true;
427 }
428 else
429 {
430 // image
431 if ( !mImage )
432 {
433 QgsDebugError( u"Image not allocated"_s );
434 return false;
435 }
436
437 if ( mImage->width() != mWidth || mImage->height() != mHeight )
438 {
439 QgsDebugError( u"Image and block size differ"_s );
440 return false;
441 }
442
443 QgsDebugMsgLevel( u"Fill image depth = %1"_s.arg( mImage->depth() ), 4 );
444
445 // TODO: support different depths
446 if ( mImage->depth() != 32 )
447 {
448 QgsDebugError( u"Unsupported image depth"_s );
449 return false;
450 }
451
452 const QRgb nodataRgba = NO_DATA_COLOR;
453 QRgb *nodataRow = new QRgb[mWidth]; // full row of no data
454 const int rgbSize = sizeof( QRgb );
455 for ( int c = 0; c < mWidth; c ++ )
456 {
457 nodataRow[c] = nodataRgba;
458 }
459
460 // top and bottom
461 for ( int r = 0; r < mHeight; r++ )
462 {
463 if ( r >= top && r <= bottom ) continue; // middle
464 const qgssize i = static_cast< qgssize >( r ) * mWidth;
465 memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( mWidth ) );
466 }
467 // middle
468 for ( int r = top; r <= bottom; r++ )
469 {
470 qgssize i = static_cast< qgssize >( r ) * mWidth;
471 // middle left
472 if ( left > 0 )
473 {
474 memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( left - 1 ) );
475 }
476 // middle right
477 i += right + 1;
478 const int w = mWidth - right - 1;
479 memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( w ) );
480 }
481 delete [] nodataRow;
482 return true;
483 }
484}
485
486template <typename T>
487void fillTypedData( double value, void *data, std::size_t count )
488{
489 std::fill_n( static_cast<T *>( data ), count, static_cast<T>( value ) );
490};
491
493{
494 QgsDebugMsgLevel( u"Entered"_s, 4 );
495 if ( !typeIsNumeric( mDataType ) )
496 {
497 QgsDebugError( u"Cannot fill image block"_s );
498 return false;
499 }
500
501 if ( !mData )
502 {
503 QgsDebugError( u"Data block not allocated"_s );
504 return false;
505 }
506
507 const std::size_t dataTypeSize = typeSize( mDataType );
508 const std::size_t valueCount = static_cast<size_t>( mWidth ) * mHeight;
509 const std::size_t totalSize = valueCount * dataTypeSize;
510
511 QgsDebugMsgLevel( u"set mData to %1"_s.arg( value ), 4 );
512
513 // special fast case for zero values
514 if ( value == 0 )
515 {
516 memset( mData, 0, totalSize );
517 return true;
518 }
519
520 switch ( mDataType )
521 {
523 fillTypedData<quint8>( value, mData, valueCount );
524 break;
526 fillTypedData<qint8>( value, mData, valueCount );
527 break;
529 fillTypedData<quint16>( value, mData, valueCount );
530 break;
532 fillTypedData<qint16>( value, mData, valueCount );
533 break;
535 fillTypedData<quint32>( value, mData, valueCount );
536 break;
538 fillTypedData<qint32>( value, mData, valueCount );
539 break;
541 fillTypedData<float>( value, mData, valueCount );
542 break;
544 fillTypedData<double>( value, mData, valueCount );
545 break;
546
554 // not supported
555 return false;
556 }
557
558 return true;
559}
560
561QByteArray QgsRasterBlock::data() const
562{
563 if ( mData )
564 return QByteArray::fromRawData( static_cast<const char *>( mData ), typeSize( mDataType ) * mWidth * mHeight );
565 else if ( mImage && mImage->constBits() )
566 return QByteArray::fromRawData( reinterpret_cast<const char *>( mImage->constBits() ), mImage->sizeInBytes() );
567 else
568 return QByteArray();
569}
570
571void QgsRasterBlock::setData( const QByteArray &data, int offset )
572{
573 if ( offset < 0 )
574 return; // negative offsets not allowed
575
576 if ( mData )
577 {
578 const int len = std::min( static_cast<int>( data.size() ), typeSize( mDataType ) * mWidth * mHeight - offset );
579 ::memcpy( static_cast<char *>( mData ) + offset, data.constData(), len );
580 }
581 else if ( mImage && mImage->constBits() )
582 {
583 const qsizetype len = std::min( static_cast< qsizetype >( data.size() ), mImage->sizeInBytes() - offset );
584 ::memcpy( mImage->bits() + offset, data.constData(), len );
585 }
586}
587
589{
590 // Not testing type to avoid too much overhead because this method is called per pixel
591 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
592 {
593 QgsDebugMsgLevel( u"Index %1 out of range (%2 x %3)"_s.arg( index ).arg( mWidth ).arg( mHeight ), 4 );
594 return nullptr;
595 }
596 if ( mData )
597 {
598 return reinterpret_cast< char * >( mData ) + index * mTypeSize;
599 }
600 if ( mImage )
601 {
602 if ( uchar *data = mImage->bits() )
603 {
604 return reinterpret_cast< char * >( data + index * 4 );
605 }
606 }
607
608 return nullptr;
609}
610
611const char *QgsRasterBlock::constBits( qgssize index ) const
612{
613 // Not testing type to avoid too much overhead because this method is called per pixel
614 if ( index >= static_cast< qgssize >( mWidth )*mHeight )
615 {
616 QgsDebugMsgLevel( u"Index %1 out of range (%2 x %3)"_s.arg( index ).arg( mWidth ).arg( mHeight ), 4 );
617 return nullptr;
618 }
619 if ( mData )
620 {
621 return reinterpret_cast< const char * >( mData ) + index * mTypeSize;
622 }
623 if ( mImage )
624 {
625 if ( const uchar *data = mImage->constBits() )
626 {
627 return reinterpret_cast< const char * >( data + index * 4 );
628 }
629 }
630
631 return nullptr;
632}
633
634char *QgsRasterBlock::bits( int row, int column )
635{
636 return bits( static_cast< qgssize >( row ) * mWidth + column );
637}
638
640{
641 if ( mData )
642 {
643 return reinterpret_cast< char * >( mData );
644 }
645 if ( mImage )
646 {
647 if ( uchar *data = mImage->bits() )
648 {
649 return reinterpret_cast< char * >( data );
650 }
651 }
652
653 return nullptr;
654}
655
656const char *QgsRasterBlock::constBits() const
657{
658 if ( mData )
659 {
660 return reinterpret_cast< const char * >( mData );
661 }
662 if ( mImage )
663 {
664 if ( const uchar *data = mImage->constBits() )
665 {
666 return reinterpret_cast< const char * >( data );
667 }
668 }
669
670 return nullptr;
671}
672
674{
675 if ( isEmpty() ) return false;
676 if ( destDataType == mDataType ) return true;
677
678 if ( typeIsNumeric( mDataType ) && typeIsNumeric( destDataType ) )
679 {
680 void *data = convert( mData, mDataType, destDataType, static_cast< qgssize >( mWidth ) * static_cast< qgssize >( mHeight ) );
681
682 if ( !data )
683 {
684 QgsDebugError( u"Cannot convert raster block"_s );
685 return false;
686 }
687 qgsFree( mData );
688 mData = data;
689 mDataType = destDataType;
690 mTypeSize = typeSize( mDataType );
691 }
692 else if ( typeIsColor( mDataType ) && typeIsColor( destDataType ) )
693 {
694 const QImage::Format format = imageFormat( destDataType );
695 const QImage image = mImage->convertToFormat( format );
696 *mImage = image;
697 mDataType = destDataType;
698 mTypeSize = typeSize( mDataType );
699 }
700 else
701 {
702 return false;
703 }
704
705 return true;
706}
707
708void QgsRasterBlock::applyScaleOffset( double scale, double offset )
709{
710 if ( isEmpty() ) return;
711 if ( !typeIsNumeric( mDataType ) ) return;
712 if ( scale == 1.0 && offset == 0.0 ) return;
713
714 const qgssize size = static_cast< qgssize >( mWidth ) * mHeight;
715 for ( qgssize i = 0; i < size; ++i )
716 {
717 if ( !isNoData( i ) ) setValue( i, value( i ) * scale + offset );
718 }
719}
720
722{
723 if ( rangeList.isEmpty() )
724 {
725 return;
726 }
727
728 const qgssize size = static_cast< qgssize >( mWidth ) * static_cast< qgssize >( mHeight );
729 for ( qgssize i = 0; i < size; ++i )
730 {
731 const double val = value( i );
732 if ( QgsRasterRange::contains( val, rangeList ) )
733 {
734 //setValue( i, mNoDataValue );
735 setIsNoData( i );
736 }
737 }
738}
739
741{
742 if ( mImage )
743 {
744 return QImage( *mImage );
745 }
746 return QImage();
747}
748
749bool QgsRasterBlock::setImage( const QImage *image )
750{
751 qgsFree( mData );
752 mData = nullptr;
753 delete mImage;
754 mImage = nullptr;
755 mImage = new QImage( *image );
756 mWidth = mImage->width();
757 mHeight = mImage->height();
758 mDataType = dataType( mImage->format() );
759 mTypeSize = QgsRasterBlock::typeSize( mDataType );
760 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
761 return true;
762}
763
764QString QgsRasterBlock::printValue( double value, bool localized )
765{
766 /*
767 * IEEE 754 double has 15-17 significant digits. It specifies:
768 *
769 * "If a decimal string with at most 15 significant decimal is converted to
770 * IEEE 754 double precision and then converted back to the same number of
771 * significant decimal, then the final string should match the original;
772 * and if an IEEE 754 double precision is converted to a decimal string with at
773 * least 17 significant decimal and then converted back to double, then the final
774 * number must match the original."
775 *
776 * If printing only 15 digits, some precision could be lost. Printing 17 digits may
777 * add some confusing digits.
778 *
779 * Default 'g' precision on linux is 6 digits, not all significant digits like
780 * some sprintf manuals say.
781 *
782 * We need to ensure that the number printed and used in QLineEdit or XML will
783 * give the same number when parsed.
784 *
785 * Is there a better solution?
786 */
787
788 QString s;
789
790 for ( int i = 15; i <= 17; i++ )
791 {
792 s.setNum( value, 'g', i );
793 const double doubleValue { s.toDouble( ) };
794 if ( qgsDoubleNear( doubleValue, value ) )
795 {
796 if ( localized )
797 {
798 return QLocale().toString( doubleValue, 'g', i );
799 }
800 return s;
801 }
802 }
803 // Should not happen
804 QgsDebugError( u"Cannot correctly parse printed value"_s );
805 return s;
806}
807
808QString QgsRasterBlock::printValue( float value, bool localized )
809{
810 /*
811 * IEEE 754 double has 6-9 significant digits. See printValue(double)
812 */
813
814 QString s;
815
816 for ( int i = 6; i <= 9; i++ )
817 {
818 s.setNum( value, 'g', i );
819 const float floatValue { s.toFloat() };
820 if ( qgsFloatNear( floatValue, value ) )
821 {
822 if ( localized )
823 {
824 return QLocale().toString( floatValue, 'g', i );
825 }
826 return s;
827 }
828 }
829 // Should not happen
830 QgsDebugError( u"Cannot correctly parse printed value"_s );
831 return s;
832}
833
834void *QgsRasterBlock::convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size )
835{
836 const int destDataTypeSize = typeSize( destDataType );
837 void *destData = qgsMalloc( destDataTypeSize * size );
838 for ( qgssize i = 0; i < size; i++ )
839 {
840 const double value = readValue( srcData, srcDataType, i );
841 writeValue( destData, destDataType, i, value );
842 //double newValue = readValue( destData, destDataType, i );
843 //QgsDebugMsgLevel( u"convert %1 type %2 to %3: %4 -> %5"_s.arg(i).arg(srcDataType).arg(destDataType).arg( value ).arg( newValue ), 2 );
844 }
845 return destData;
846}
847
849{
851 QByteArray ba;
852 ba.resize( static_cast< int >( size ) );
853 char *data = ba.data();
854 quint8 uc;
855 quint16 us;
856 qint16 s;
857 quint32 ui;
858 qint32 i;
859 float f;
860 double d;
861 switch ( dataType )
862 {
864 uc = static_cast< quint8 >( value );
865 memcpy( data, &uc, size );
866 break;
868 {
869 const qint8 myint8 = static_cast< qint8 >( value );
870 memcpy( data, &myint8, size );
871 break;
872 }
874 us = static_cast< quint16 >( value );
875 memcpy( data, &us, size );
876 break;
878 s = static_cast< qint16 >( value );
879 memcpy( data, &s, size );
880 break;
882 ui = static_cast< quint32 >( value );
883 memcpy( data, &ui, size );
884 break;
886 i = static_cast< qint32 >( value );
887 memcpy( data, &i, size );
888 break;
890 f = static_cast< float >( value );
891 memcpy( data, &f, size );
892 break;
894 d = static_cast< double >( value );
895 memcpy( data, &d, size );
896 break;
904 QgsDebugError( u"Data type is not supported"_s );
905 }
906 return ba;
907}
908
909bool QgsRasterBlock::createNoDataBitmap()
910{
911 mNoDataBitmapWidth = mWidth / 8 + 1;
912 mNoDataBitmapSize = static_cast< qgssize >( mNoDataBitmapWidth ) * mHeight;
913 QgsDebugMsgLevel( u"allocate %1 bytes"_s.arg( mNoDataBitmapSize ), 4 );
914 mNoDataBitmap = reinterpret_cast< char * >( qgsMalloc( mNoDataBitmapSize ) );
915 if ( !mNoDataBitmap )
916 {
917 QgsDebugError( u"Couldn't allocate no data memory of %1 bytes"_s.arg( mNoDataBitmapSize ) );
918 return false;
919 }
920 memset( mNoDataBitmap, 0, mNoDataBitmapSize );
921 return true;
922}
923
925{
926 return u"dataType = %1 width = %2 height = %3"_s
927 .arg( qgsEnumValueToKey< Qgis::DataType >( mDataType ) ).arg( mWidth ).arg( mHeight );
928}
929
930QRect QgsRasterBlock::subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent )
931{
932 QgsDebugMsgLevel( "theExtent = " + extent.toString(), 4 );
933 QgsDebugMsgLevel( "theSubExtent = " + subExtent.toString(), 4 );
934 const double xRes = extent.width() / width;
935 const double yRes = extent.height() / height;
936
937 QgsDebugMsgLevel( u"theWidth = %1 height = %2 xRes = %3 yRes = %4"_s.arg( width ).arg( height ).arg( xRes ).arg( yRes ), 4 );
938
939 int top = 0;
940 int bottom = height - 1;
941 int left = 0;
942 int right = width - 1;
943
944 if ( subExtent.yMaximum() < extent.yMaximum() )
945 {
946 top = std::round( ( extent.yMaximum() - subExtent.yMaximum() ) / yRes );
947 }
948 if ( subExtent.yMinimum() > extent.yMinimum() )
949 {
950 bottom = std::round( ( extent.yMaximum() - subExtent.yMinimum() ) / yRes ) - 1;
951 }
952
953 if ( subExtent.xMinimum() > extent.xMinimum() )
954 {
955 left = std::round( ( subExtent.xMinimum() - extent.xMinimum() ) / xRes );
956 }
957 if ( subExtent.xMaximum() < extent.xMaximum() )
958 {
959 right = std::round( ( subExtent.xMaximum() - extent.xMinimum() ) / xRes ) - 1;
960 }
961 QRect subRect = QRect( left, top, right - left + 1, bottom - top + 1 );
962 QgsDebugMsgLevel( u"subRect: %1 %2 %3 %4"_s.arg( subRect.x() ).arg( subRect.y() ).arg( subRect.width() ).arg( subRect.height() ), 4 );
963 return subRect;
964}
965
966bool QgsRasterBlock::minimum( double &minimum, int &row, int &column ) const
967{
968 if ( !mData )
969 {
970 minimum = std::numeric_limits<double>::quiet_NaN();
971 return false;
972 }
973
974 const std::size_t offset = qgis_gdal::min_element( mData, static_cast<std::size_t>( mWidth ) * static_cast< std::size_t>( mHeight ),
975 QgsGdalUtils::gdalDataTypeFromQgisDataType( mDataType ), mHasNoDataValue, mNoDataValue );
976
977 row = static_cast< int >( offset / mWidth );
978 column = static_cast< int >( offset % mWidth );
979 minimum = value( offset );
980
981 return true;
982}
983
984bool QgsRasterBlock::maximum( double &maximum SIP_OUT, int &row SIP_OUT, int &column SIP_OUT ) const
985{
986 if ( !mData )
987 {
988 maximum = std::numeric_limits<double>::quiet_NaN();
989 return false;
990 }
991 const std::size_t offset = qgis_gdal::max_element( mData, static_cast<std::size_t>( mWidth ) * static_cast< std::size_t>( mHeight ),
992 QgsGdalUtils::gdalDataTypeFromQgisDataType( mDataType ), mHasNoDataValue, mNoDataValue );
993
994 row = static_cast< int >( offset / mWidth );
995 column = static_cast< int >( offset % mWidth );
996 maximum = value( offset );
997
998 return true;
999}
1000
1001bool QgsRasterBlock::minimumMaximum( double &minimum, int &minimumRow, int &minimumColumn, double &maximum, int &maximumRow, int &maximumColumn ) const
1002{
1003 if ( !mData )
1004 {
1005 minimum = std::numeric_limits<double>::quiet_NaN();
1006 maximum = std::numeric_limits<double>::quiet_NaN();
1007 return false;
1008 }
1009
1010 const auto [minOffset, maxOffset] = qgis_gdal::minmax_element( mData, static_cast<std::size_t>( mWidth ) * static_cast< std::size_t>( mHeight ),
1011 QgsGdalUtils::gdalDataTypeFromQgisDataType( mDataType ), mHasNoDataValue, mNoDataValue );
1012
1013 minimumRow = static_cast< int >( minOffset / mWidth );
1014 minimumColumn = static_cast< int >( minOffset % mWidth );
1015 minimum = value( minOffset );
1016
1017 maximumRow = static_cast< int >( maxOffset / mWidth );
1018 maximumColumn = static_cast< int >( maxOffset % mWidth );
1019 maximum = value( maxOffset );
1020
1021 return true;
1022}
DataType
Raster data types.
Definition qgis.h:379
@ CInt32
Complex Int32.
Definition qgis.h:390
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:387
@ CFloat64
Complex Float64.
Definition qgis.h:392
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:384
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:394
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30).
Definition qgis.h:382
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:383
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:381
@ UnknownDataType
Unknown or unspecified type.
Definition qgis.h:380
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition qgis.h:393
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:386
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:388
@ CFloat32
Complex Float32.
Definition qgis.h:391
@ CInt16
Complex Int16.
Definition qgis.h:389
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:385
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 a 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".
static bool typeIsComplex(Qgis::DataType type)
Returns true if a data type is a complex number type.
bool fill(double value)
Fills the whole block with a constant value.
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 a data type is a color type.
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.
Q_INVOKABLE 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
double yMinimum
double xMaximum
double yMaximum
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:112
void qgsFree(void *ptr)
Frees the memory space pointed to by ptr.
Definition qgis.cpp:134
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7091
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:7423
bool qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference).
Definition qgis.h:6911
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6900
#define SIP_OUT
Definition qgis_sip.h:58
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
void fillTypedData(double value, void *data, std::size_t count)
QList< QgsRasterRange > QgsRasterRangeList