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