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