QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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 delete mImage;
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 delete mImage;
67 mImage = nullptr;
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 = new 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;
107 QgsDebugMsgLevel( u"mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5"_s.arg( mWidth ).arg( mHeight ).arg( static_cast< int>( mDataType ) ).arg( reinterpret_cast< quint64 >( mData ) ).arg( reinterpret_cast< quint64 >( mImage ) ), 4 );
108 return true;
109}
110
111QImage::Format QgsRasterBlock::imageFormat( Qgis::DataType dataType )
112{
114 {
115 return QImage::Format_ARGB32;
116 }
118 {
119 return QImage::Format_ARGB32_Premultiplied;
120 }
121 return QImage::Format_Invalid;
122}
123
124Qgis::DataType QgsRasterBlock::dataType( QImage::Format format )
125{
126 if ( format == QImage::Format_ARGB32 )
127 {
129 }
130 else if ( format == QImage::Format_ARGB32_Premultiplied )
131 {
133 }
135}
136
138{
139 QgsDebugMsgLevel( u"mWidth= %1 mHeight = %2 mDataType = %3 mData = %4 mImage = %5"_s.arg( mWidth ).arg( mHeight ).arg( qgsEnumValueToKey( mDataType ) ).arg( reinterpret_cast< quint64 >( mData ) ).arg( reinterpret_cast< quint64 >( mImage ) ), 4 );
140 return mWidth == 0 || mHeight == 0 || ( typeIsNumeric( mDataType ) && !mData ) || ( typeIsColor( mDataType ) && !mImage );
141}
142
168
170{
171 switch ( type )
172 {
184 return false;
185
190 return true;
191 }
192 return false;
193}
194
220
222{
224
225 switch ( dataType )
226 {
229 *noDataValue = -32768.0;
230 newDataType = Qgis::DataType::Int16;
231 break;
233 *noDataValue = -2147483648.0;
234 newDataType = Qgis::DataType::Int32;
235 break;
237 *noDataValue = -2147483648.0;
238 newDataType = Qgis::DataType::Int32;
239 break;
244 *noDataValue = std::numeric_limits<double>::max() * -1.0;
245 newDataType = Qgis::DataType::Float64;
246 break;
254 QgsDebugError( u"Unknown data type %1"_s.arg( static_cast< int >( dataType ) ) );
256 }
257 QgsDebugMsgLevel( u"newDataType = %1 noDataValue = %2"_s.arg( qgsEnumValueToKey< Qgis::DataType >( newDataType ) ).arg( *noDataValue ), 4 );
258 return newDataType;
259}
260
262{
263 mHasNoDataValue = true;
264 mNoDataValue = noDataValue;
265}
266
268{
269 mHasNoDataValue = false;
270 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
271}
272
274{
275 QgsDebugMsgLevel( u"Entered"_s, 4 );
276 if ( typeIsNumeric( mDataType ) )
277 {
278 if ( mHasNoDataValue )
279 {
280 return fill( mNoDataValue );
281 }
282 else
283 {
284 // use bitmap
285 if ( !mNoDataBitmap )
286 {
287 if ( !createNoDataBitmap() )
288 {
289 return false;
290 }
291 }
292 QgsDebugMsgLevel( u"set mNoDataBitmap to 1"_s, 4 );
293 memset( mNoDataBitmap, 0xff, mNoDataBitmapSize );
294 const size_t dataTypeSize = typeSize( mDataType );
295 if ( mData )
296 {
297 memset( mData, 0, dataTypeSize * mWidth * mHeight );
298 }
299 }
300 return true;
301 }
302 else
303 {
304 // image
305 if ( !mImage )
306 {
307 QgsDebugError( u"Image not allocated"_s );
308 return false;
309 }
310 QgsDebugMsgLevel( u"Fill image"_s, 4 );
311 mImage->fill( NO_DATA_COLOR );
312 return true;
313 }
314}
315
316bool QgsRasterBlock::setIsNoDataExcept( QRect exceptRect )
317{
318 int top = exceptRect.top();
319 int bottom = exceptRect.bottom();
320 int left = exceptRect.left();
321 int right = exceptRect.right();
322 top = std::min( std::max( top, 0 ), mHeight - 1 );
323 left = std::min( std::max( left, 0 ), mWidth - 1 );
324 bottom = std::max( 0, std::min( bottom, mHeight - 1 ) );
325 right = std::max( 0, std::min( right, mWidth - 1 ) );
326
327 QgsDebugMsgLevel( u"Entered"_s, 4 );
328 if ( typeIsNumeric( mDataType ) )
329 {
330 const size_t dataTypeSize = typeSize( mDataType );
331 if ( mHasNoDataValue )
332 {
333 if ( !mData )
334 {
335 QgsDebugError( u"Data block not allocated"_s );
336 return false;
337 }
338
339 QgsDebugMsgLevel( u"set mData to mNoDataValue"_s, 4 );
340 QByteArray noDataByteArray = valueBytes( mDataType, mNoDataValue );
341
342 char *nodata = noDataByteArray.data();
343 char *nodataRow = new char[mWidth * dataTypeSize]; // full row of no data
344 for ( int c = 0; c < mWidth; c++ )
345 {
346 memcpy( nodataRow + c * dataTypeSize, nodata, dataTypeSize );
347 }
348
349 // top and bottom
350 for ( int r = 0; r < mHeight; r++ )
351 {
352 if ( r >= top && r <= bottom )
353 continue; // middle
354 const qgssize i = static_cast< qgssize >( r ) * mWidth;
355 memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( mWidth ) );
356 }
357 // middle
358 for ( int r = top; r <= bottom; r++ )
359 {
360 qgssize i = static_cast< qgssize >( r ) * mWidth;
361 // middle left
362 memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( left ) );
363 // middle right
364 i += right + 1;
365 const int w = mWidth - right - 1;
366 memcpy( reinterpret_cast< char * >( mData ) + i * dataTypeSize, nodataRow, dataTypeSize * static_cast< qgssize >( w ) );
367 }
368 delete[] nodataRow;
369 }
370 else
371 {
372 // use bitmap
373 if ( !mNoDataBitmap )
374 {
375 if ( !createNoDataBitmap() )
376 {
377 return false;
378 }
379 }
380 QgsDebugMsgLevel( u"set mNoDataBitmap to 1"_s, 4 );
381
382 if ( mData )
383 {
384 memset( mData, 0, dataTypeSize * mWidth * mHeight );
385 }
386
387 char *nodataRow = new char[mNoDataBitmapWidth]; // full row of no data
388 // TODO: we can simply set all bytes to 11111111 (~0) I think
389 memset( nodataRow, 0, mNoDataBitmapWidth );
390 for ( int c = 0; c < mWidth; c++ )
391 {
392 const int byte = c / 8;
393 const int bit = c % 8;
394 const char nodata = 0x80 >> bit;
395 memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
396 }
397
398 // top and bottom
399 for ( int r = 0; r < mHeight; r++ )
400 {
401 if ( r >= top && r <= bottom )
402 continue; // middle
403 const qgssize i = static_cast< qgssize >( r ) * mNoDataBitmapWidth;
404 memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
405 }
406 // middle
407 memset( nodataRow, 0, mNoDataBitmapWidth );
408 for ( int c = 0; c < mWidth; c++ )
409 {
410 if ( c >= left && c <= right )
411 continue; // middle
412 const int byte = c / 8;
413 const int bit = c % 8;
414 const char nodata = 0x80 >> bit;
415 memset( nodataRow + byte, nodataRow[byte] | nodata, 1 );
416 }
417 for ( int r = top; r <= bottom; r++ )
418 {
419 const qgssize i = static_cast< qgssize >( r ) * mNoDataBitmapWidth;
420 memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
421 }
422 delete[] nodataRow;
423 }
424 return true;
425 }
426 else
427 {
428 // image
429 if ( !mImage )
430 {
431 QgsDebugError( u"Image not allocated"_s );
432 return false;
433 }
434
435 if ( mImage->width() != mWidth || mImage->height() != mHeight )
436 {
437 QgsDebugError( u"Image and block size differ"_s );
438 return false;
439 }
440
441 QgsDebugMsgLevel( u"Fill image depth = %1"_s.arg( mImage->depth() ), 4 );
442
443 // TODO: support different depths
444 if ( mImage->depth() != 32 )
445 {
446 QgsDebugError( u"Unsupported image depth"_s );
447 return false;
448 }
449
450 const QRgb nodataRgba = NO_DATA_COLOR;
451 QRgb *nodataRow = new QRgb[mWidth]; // full row of no data
452 const int rgbSize = sizeof( QRgb );
453 for ( int c = 0; c < mWidth; c++ )
454 {
455 nodataRow[c] = nodataRgba;
456 }
457
458 // top and bottom
459 for ( int r = 0; r < mHeight; r++ )
460 {
461 if ( r >= top && r <= bottom )
462 continue; // middle
463 const qgssize i = static_cast< qgssize >( r ) * mWidth;
464 memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( mWidth ) );
465 }
466 // middle
467 for ( int r = top; r <= bottom; r++ )
468 {
469 qgssize i = static_cast< qgssize >( r ) * mWidth;
470 // middle left
471 if ( left > 0 )
472 {
473 memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( left - 1 ) );
474 }
475 // middle right
476 i += right + 1;
477 const int w = mWidth - right - 1;
478 memcpy( reinterpret_cast< void * >( mImage->bits() + rgbSize * i ), nodataRow, rgbSize * static_cast< qgssize >( w ) );
479 }
480 delete[] nodataRow;
481 return true;
482 }
483}
484
485template<typename T> void fillTypedData( double value, void *data, std::size_t count )
486{
487 std::fill_n( static_cast<T *>( data ), count, static_cast<T>( value ) );
488};
489
491{
492 QgsDebugMsgLevel( u"Entered"_s, 4 );
493 if ( !typeIsNumeric( mDataType ) )
494 {
495 QgsDebugError( u"Cannot fill image block"_s );
496 return false;
497 }
498
499 if ( !mData )
500 {
501 QgsDebugError( u"Data block not allocated"_s );
502 return false;
503 }
504
505 const std::size_t dataTypeSize = typeSize( mDataType );
506 const std::size_t valueCount = static_cast<size_t>( mWidth ) * mHeight;
507 const std::size_t totalSize = valueCount * dataTypeSize;
508
509 QgsDebugMsgLevel( u"set mData to %1"_s.arg( value ), 4 );
510
511 // special fast case for zero values
512 if ( value == 0 )
513 {
514 memset( mData, 0, totalSize );
515 return true;
516 }
517
518 switch ( mDataType )
519 {
521 fillTypedData<quint8>( value, mData, valueCount );
522 break;
524 fillTypedData<qint8>( value, mData, valueCount );
525 break;
527 fillTypedData<quint16>( value, mData, valueCount );
528 break;
530 fillTypedData<qint16>( value, mData, valueCount );
531 break;
533 fillTypedData<quint32>( value, mData, valueCount );
534 break;
536 fillTypedData<qint32>( value, mData, valueCount );
537 break;
539 fillTypedData<float>( value, mData, valueCount );
540 break;
542 fillTypedData<double>( value, mData, valueCount );
543 break;
544
552 // not supported
553 return false;
554 }
555
556 return true;
557}
558
559QByteArray QgsRasterBlock::data() const
560{
561 if ( mData )
562 return QByteArray::fromRawData( static_cast<const char *>( mData ), typeSize( mDataType ) * mWidth * mHeight );
563 else if ( mImage && mImage->constBits() )
564 return QByteArray::fromRawData( reinterpret_cast<const char *>( mImage->constBits() ), mImage->sizeInBytes() );
565 else
566 return QByteArray();
567}
568
569void QgsRasterBlock::setData( const QByteArray &data, int offset )
570{
571 if ( offset < 0 )
572 return; // negative offsets not allowed
573
574 if ( mData )
575 {
576 const int len = std::min( static_cast<int>( data.size() ), typeSize( mDataType ) * mWidth * mHeight - offset );
577 ::memcpy( static_cast<char *>( mData ) + offset, data.constData(), len );
578 }
579 else if ( mImage && mImage->constBits() )
580 {
581 const qsizetype len = std::min( static_cast< qsizetype >( data.size() ), mImage->sizeInBytes() - offset );
582 ::memcpy( mImage->bits() + offset, data.constData(), len );
583 }
584}
585
587{
588 // Not testing type to avoid too much overhead because this method is called per pixel
589 if ( index >= static_cast< qgssize >( mWidth ) * mHeight )
590 {
591 QgsDebugMsgLevel( u"Index %1 out of range (%2 x %3)"_s.arg( index ).arg( mWidth ).arg( mHeight ), 4 );
592 return nullptr;
593 }
594 if ( mData )
595 {
596 return reinterpret_cast< char * >( mData ) + index * mTypeSize;
597 }
598 if ( mImage )
599 {
600 if ( uchar *data = mImage->bits() )
601 {
602 return reinterpret_cast< char * >( data + index * 4 );
603 }
604 }
605
606 return nullptr;
607}
608
609const char *QgsRasterBlock::constBits( qgssize index ) const
610{
611 // Not testing type to avoid too much overhead because this method is called per pixel
612 if ( index >= static_cast< qgssize >( mWidth ) * mHeight )
613 {
614 QgsDebugMsgLevel( u"Index %1 out of range (%2 x %3)"_s.arg( index ).arg( mWidth ).arg( mHeight ), 4 );
615 return nullptr;
616 }
617 if ( mData )
618 {
619 return reinterpret_cast< const char * >( mData ) + index * mTypeSize;
620 }
621 if ( mImage )
622 {
623 if ( const uchar *data = mImage->constBits() )
624 {
625 return reinterpret_cast< const char * >( data + index * 4 );
626 }
627 }
628
629 return nullptr;
630}
631
632char *QgsRasterBlock::bits( int row, int column )
633{
634 return bits( static_cast< qgssize >( row ) * mWidth + column );
635}
636
638{
639 if ( mData )
640 {
641 return reinterpret_cast< char * >( mData );
642 }
643 if ( mImage )
644 {
645 if ( uchar *data = mImage->bits() )
646 {
647 return reinterpret_cast< char * >( data );
648 }
649 }
650
651 return nullptr;
652}
653
654const char *QgsRasterBlock::constBits() const
655{
656 if ( mData )
657 {
658 return reinterpret_cast< const char * >( mData );
659 }
660 if ( mImage )
661 {
662 if ( const uchar *data = mImage->constBits() )
663 {
664 return reinterpret_cast< const char * >( data );
665 }
666 }
667
668 return nullptr;
669}
670
672{
673 if ( isEmpty() )
674 return false;
675 if ( destDataType == mDataType )
676 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() )
711 return;
712 if ( !typeIsNumeric( mDataType ) )
713 return;
714 if ( scale == 1.0 && offset == 0.0 )
715 return;
716
717 const qgssize size = static_cast< qgssize >( mWidth ) * mHeight;
718 for ( qgssize i = 0; i < size; ++i )
719 {
720 if ( !isNoData( i ) )
721 setValue( i, value( i ) * scale + offset );
722 }
723}
724
726{
727 if ( rangeList.isEmpty() )
728 {
729 return;
730 }
731
732 const qgssize size = static_cast< qgssize >( mWidth ) * static_cast< qgssize >( mHeight );
733 for ( qgssize i = 0; i < size; ++i )
734 {
735 const double val = value( i );
736 if ( QgsRasterRange::contains( val, rangeList ) )
737 {
738 //setValue( i, mNoDataValue );
739 setIsNoData( i );
740 }
741 }
742}
743
745{
746 if ( mImage )
747 {
748 return QImage( *mImage );
749 }
750 return QImage();
751}
752
753bool QgsRasterBlock::setImage( const QImage *image )
754{
755 qgsFree( mData );
756 mData = nullptr;
757 delete mImage;
758 mImage = nullptr;
759 mImage = new QImage( *image );
760 mWidth = mImage->width();
761 mHeight = mImage->height();
762 mDataType = dataType( mImage->format() );
763 mTypeSize = QgsRasterBlock::typeSize( mDataType );
764 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
765 return true;
766}
767
768QString QgsRasterBlock::printValue( double value, bool localized )
769{
770 /*
771 * IEEE 754 double has 15-17 significant digits. It specifies:
772 *
773 * "If a decimal string with at most 15 significant decimal is converted to
774 * IEEE 754 double precision and then converted back to the same number of
775 * significant decimal, then the final string should match the original;
776 * and if an IEEE 754 double precision is converted to a decimal string with at
777 * least 17 significant decimal and then converted back to double, then the final
778 * number must match the original."
779 *
780 * If printing only 15 digits, some precision could be lost. Printing 17 digits may
781 * add some confusing digits.
782 *
783 * Default 'g' precision on linux is 6 digits, not all significant digits like
784 * some sprintf manuals say.
785 *
786 * We need to ensure that the number printed and used in QLineEdit or XML will
787 * give the same number when parsed.
788 *
789 * Is there a better solution?
790 */
791
792 QString s;
793
794 for ( int i = 15; i <= 17; i++ )
795 {
796 s.setNum( value, 'g', i );
797 const double doubleValue { s.toDouble() };
798 if ( qgsDoubleNear( doubleValue, value ) )
799 {
800 if ( localized )
801 {
802 return QLocale().toString( doubleValue, 'g', i );
803 }
804 return s;
805 }
806 }
807 // Should not happen
808 QgsDebugError( u"Cannot correctly parse printed value"_s );
809 return s;
810}
811
812QString QgsRasterBlock::printValue( float value, bool localized )
813{
814 /*
815 * IEEE 754 double has 6-9 significant digits. See printValue(double)
816 */
817
818 QString s;
819
820 for ( int i = 6; i <= 9; i++ )
821 {
822 s.setNum( value, 'g', i );
823 const float floatValue { s.toFloat() };
824 if ( qgsFloatNear( floatValue, value ) )
825 {
826 if ( localized )
827 {
828 return QLocale().toString( floatValue, 'g', i );
829 }
830 return s;
831 }
832 }
833 // Should not happen
834 QgsDebugError( u"Cannot correctly parse printed value"_s );
835 return s;
836}
837
838void *QgsRasterBlock::convert( void *srcData, Qgis::DataType srcDataType, Qgis::DataType destDataType, qgssize size )
839{
840 const int destDataTypeSize = typeSize( destDataType );
841 void *destData = qgsMalloc( destDataTypeSize * size );
842 for ( qgssize i = 0; i < size; i++ )
843 {
844 const double value = readValue( srcData, srcDataType, i );
845 writeValue( destData, destDataType, i, value );
846 //double newValue = readValue( destData, destDataType, i );
847 //QgsDebugMsgLevel( u"convert %1 type %2 to %3: %4 -> %5"_s.arg(i).arg(srcDataType).arg(destDataType).arg( value ).arg( newValue ), 2 );
848 }
849 return destData;
850}
851
853{
855 QByteArray ba;
856 ba.resize( static_cast< int >( size ) );
857 char *data = ba.data();
858 quint8 uc;
859 quint16 us;
860 qint16 s;
861 quint32 ui;
862 qint32 i;
863 float f;
864 double d;
865 switch ( dataType )
866 {
868 uc = static_cast< quint8 >( value );
869 memcpy( data, &uc, size );
870 break;
872 {
873 const qint8 myint8 = static_cast< qint8 >( value );
874 memcpy( data, &myint8, size );
875 break;
876 }
878 us = static_cast< quint16 >( value );
879 memcpy( data, &us, size );
880 break;
882 s = static_cast< qint16 >( value );
883 memcpy( data, &s, size );
884 break;
886 ui = static_cast< quint32 >( value );
887 memcpy( data, &ui, size );
888 break;
890 i = static_cast< qint32 >( value );
891 memcpy( data, &i, size );
892 break;
894 f = static_cast< float >( value );
895 memcpy( data, &f, size );
896 break;
898 d = static_cast< double >( value );
899 memcpy( data, &d, size );
900 break;
908 QgsDebugError( u"Data type is not supported"_s );
909 }
910 return ba;
911}
912
913bool QgsRasterBlock::createNoDataBitmap()
914{
915 mNoDataBitmapWidth = mWidth / 8 + 1;
916 mNoDataBitmapSize = static_cast< qgssize >( mNoDataBitmapWidth ) * mHeight;
917 QgsDebugMsgLevel( u"allocate %1 bytes"_s.arg( mNoDataBitmapSize ), 4 );
918 mNoDataBitmap = reinterpret_cast< char * >( qgsMalloc( mNoDataBitmapSize ) );
919 if ( !mNoDataBitmap )
920 {
921 QgsDebugError( u"Couldn't allocate no data memory of %1 bytes"_s.arg( mNoDataBitmapSize ) );
922 return false;
923 }
924 memset( mNoDataBitmap, 0, mNoDataBitmapSize );
925 return true;
926}
927
929{
930 return u"dataType = %1 width = %2 height = %3"_s.arg( qgsEnumValueToKey< Qgis::DataType >( mDataType ) ).arg( mWidth ).arg( mHeight );
931}
932
933QRect QgsRasterBlock::subRect( const QgsRectangle &extent, int width, int height, const QgsRectangle &subExtent )
934{
935 QgsDebugMsgLevel( "theExtent = " + extent.toString(), 4 );
936 QgsDebugMsgLevel( "theSubExtent = " + subExtent.toString(), 4 );
937 const double xRes = extent.width() / width;
938 const double yRes = extent.height() / height;
939
940 QgsDebugMsgLevel( u"theWidth = %1 height = %2 xRes = %3 yRes = %4"_s.arg( width ).arg( height ).arg( xRes ).arg( yRes ), 4 );
941
942 int top = 0;
943 int bottom = height - 1;
944 int left = 0;
945 int right = width - 1;
946
947 if ( subExtent.yMaximum() < extent.yMaximum() )
948 {
949 top = std::round( ( extent.yMaximum() - subExtent.yMaximum() ) / yRes );
950 }
951 if ( subExtent.yMinimum() > extent.yMinimum() )
952 {
953 bottom = std::round( ( extent.yMaximum() - subExtent.yMinimum() ) / yRes ) - 1;
954 }
955
956 if ( subExtent.xMinimum() > extent.xMinimum() )
957 {
958 left = std::round( ( subExtent.xMinimum() - extent.xMinimum() ) / xRes );
959 }
960 if ( subExtent.xMaximum() < extent.xMaximum() )
961 {
962 right = std::round( ( subExtent.xMaximum() - extent.xMinimum() ) / xRes ) - 1;
963 }
964 QRect subRect = QRect( left, top, right - left + 1, bottom - top + 1 );
965 QgsDebugMsgLevel( u"subRect: %1 %2 %3 %4"_s.arg( subRect.x() ).arg( subRect.y() ).arg( subRect.width() ).arg( subRect.height() ), 4 );
966 return subRect;
967}
968
969bool QgsRasterBlock::minimum( double &minimum, int &row, int &column ) const
970{
971 if ( !mData )
972 {
973 minimum = std::numeric_limits<double>::quiet_NaN();
974 return false;
975 }
976
977 const std::size_t offset
978 = qgis_gdal::min_element( mData, static_cast<std::size_t>( mWidth ) * static_cast< std::size_t>( mHeight ), QgsGdalUtils::gdalDataTypeFromQgisDataType( mDataType ), mHasNoDataValue, mNoDataValue );
979
980 row = static_cast< int >( offset / mWidth );
981 column = static_cast< int >( offset % mWidth );
982 minimum = value( offset );
983
984 return true;
985}
986
987bool QgsRasterBlock::maximum( double &maximum SIP_OUT, int &row SIP_OUT, int &column SIP_OUT ) const
988{
989 if ( !mData )
990 {
991 maximum = std::numeric_limits<double>::quiet_NaN();
992 return false;
993 }
994 const std::size_t offset
995 = qgis_gdal::max_element( mData, static_cast<std::size_t>( mWidth ) * static_cast< std::size_t>( mHeight ), QgsGdalUtils::gdalDataTypeFromQgisDataType( mDataType ), mHasNoDataValue, mNoDataValue );
996
997 row = static_cast< int >( offset / mWidth );
998 column = static_cast< int >( offset % mWidth );
999 maximum = value( offset );
1000
1001 return true;
1002}
1003
1004bool QgsRasterBlock::minimumMaximum( double &minimum, int &minimumRow, int &minimumColumn, double &maximum, int &maximumRow, int &maximumColumn ) const
1005{
1006 if ( !mData )
1007 {
1008 minimum = std::numeric_limits<double>::quiet_NaN();
1009 maximum = std::numeric_limits<double>::quiet_NaN();
1010 return false;
1011 }
1012
1013 const auto [minOffset, maxOffset]
1014 = qgis_gdal::minmax_element( mData, static_cast<std::size_t>( mWidth ) * static_cast< std::size_t>( mHeight ), QgsGdalUtils::gdalDataTypeFromQgisDataType( mDataType ), mHasNoDataValue, mNoDataValue );
1015
1016 minimumRow = static_cast< int >( minOffset / mWidth );
1017 minimumColumn = static_cast< int >( minOffset % mWidth );
1018 minimum = value( minOffset );
1019
1020 maximumRow = static_cast< int >( maxOffset / mWidth );
1021 maximumColumn = static_cast< int >( maxOffset % mWidth );
1022 maximum = value( maxOffset );
1023
1024 return true;
1025}
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:7157
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:7485
bool qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference).
Definition qgis.h:6986
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975
#define SIP_OUT
Definition qgis_sip.h:57
#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