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