29#define GDAL_MINMAXELT_NS qgis_gdal
30#include "gdal_minmax_element.hpp"
33const QRgb QgsRasterBlock::NO_DATA_COLOR = qRgba( 0, 0, 0, 0 );
36 : mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
41 : mDataType( dataType )
44 , mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
46 ( void )
reset( mDataType, mWidth, mHeight );
51 QgsDebugMsgLevel( QStringLiteral(
"mData = %1" ).arg(
reinterpret_cast< quint64
>( mData ) ), 4 );
66 mNoDataBitmap =
nullptr;
71 mHasNoDataValue =
false;
72 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
90 const QImage::Format format = imageFormat(
dataType );
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 );
109QImage::Format QgsRasterBlock::imageFormat(
Qgis::DataType dataType )
113 return QImage::Format_ARGB32;
117 return QImage::Format_ARGB32_Premultiplied;
119 return QImage::Format_Invalid;
124 if ( format == QImage::Format_ARGB32 )
128 else if ( format == QImage::Format_ARGB32_Premultiplied )
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 ||
245 *
noDataValue = std::numeric_limits<double>::max() * -1.0;
258 QgsDebugMsgLevel( QStringLiteral(
"newDataType = %1 noDataValue = %2" ).arg( qgsEnumValueToKey< Qgis::DataType >( newDataType ) ).arg( *
noDataValue ), 4 );
264 mHasNoDataValue =
true;
270 mHasNoDataValue =
false;
271 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
279 if ( mHasNoDataValue )
281 return fill( mNoDataValue );
286 if ( !mNoDataBitmap )
288 if ( !createNoDataBitmap() )
294 memset( mNoDataBitmap, 0xff, mNoDataBitmapSize );
312 mImage->fill( NO_DATA_COLOR );
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 ) );
332 if ( mHasNoDataValue )
336 QgsDebugError( QStringLiteral(
"Data block not allocated" ) );
341 QByteArray noDataByteArray =
valueBytes( mDataType, mNoDataValue );
343 char *nodata = noDataByteArray.data();
345 for (
int c = 0;
c < mWidth;
c++ )
351 for (
int r = 0; r < mHeight; r++ )
353 if ( r >= top && r <= bottom )
continue;
358 for (
int r = top; r <= bottom; r++ )
365 const int w = mWidth - right - 1;
373 if ( !mNoDataBitmap )
375 if ( !createNoDataBitmap() )
387 char *nodataRow =
new char[mNoDataBitmapWidth];
389 memset( nodataRow, 0, mNoDataBitmapWidth );
390 for (
int c = 0;
c < mWidth;
c ++ )
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 );
399 for (
int r = 0; r < mHeight; r++ )
401 if ( r >= top && r <= bottom )
continue;
402 const qgssize i =
static_cast< qgssize >( r ) * mNoDataBitmapWidth;
403 memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
406 memset( nodataRow, 0, mNoDataBitmapWidth );
407 for (
int c = 0;
c < mWidth;
c ++ )
409 if (
c >= left &&
c <= right )
continue;
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 );
415 for (
int r = top; r <= bottom; r++ )
417 const qgssize i =
static_cast< qgssize >( r ) * mNoDataBitmapWidth;
418 memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
433 if ( mImage->width() != mWidth || mImage->height() != mHeight )
435 QgsDebugError( QStringLiteral(
"Image and block size differ" ) );
439 QgsDebugMsgLevel( QStringLiteral(
"Fill image depth = %1" ).arg( mImage->depth() ), 4 );
442 if ( mImage->depth() != 32 )
444 QgsDebugError( QStringLiteral(
"Unsupported image depth" ) );
448 const QRgb nodataRgba = NO_DATA_COLOR;
449 QRgb *nodataRow =
new QRgb[mWidth];
450 const int rgbSize =
sizeof( QRgb );
451 for (
int c = 0;
c < mWidth;
c ++ )
453 nodataRow[
c] = nodataRgba;
457 for (
int r = 0; r < mHeight; r++ )
459 if ( r >= top && r <= bottom )
continue;
461 memcpy(
reinterpret_cast< void *
>( mImage->bits() + rgbSize * i ), nodataRow, rgbSize *
static_cast< qgssize >( mWidth ) );
464 for (
int r = top; r <= bottom; r++ )
470 memcpy(
reinterpret_cast< void *
>( mImage->bits() + rgbSize * i ), nodataRow, rgbSize *
static_cast< qgssize >( left - 1 ) );
474 const int w = mWidth - right - 1;
475 memcpy(
reinterpret_cast< void *
>( mImage->bits() + rgbSize * i ), nodataRow, rgbSize *
static_cast< qgssize >( w ) );
485 std::fill_n(
static_cast<T *
>( data ), count,
static_cast<T
>( value ) );
493 QgsDebugError( QStringLiteral(
"Cannot fill image block" ) );
499 QgsDebugError( QStringLiteral(
"Data block not allocated" ) );
504 const std::size_t valueCount =
static_cast<size_t>( mWidth ) * mHeight;
505 const std::size_t totalSize = valueCount *
dataTypeSize;
512 memset( mData, 0, totalSize );
519 fillTypedData<quint8>(
value, mData, valueCount );
522 fillTypedData<qint8>(
value, mData, valueCount );
525 fillTypedData<quint16>(
value, mData, valueCount );
528 fillTypedData<qint16>(
value, mData, valueCount );
531 fillTypedData<quint32>(
value, mData, valueCount );
534 fillTypedData<qint32>(
value, mData, valueCount );
537 fillTypedData<float>(
value, mData, valueCount );
540 fillTypedData<double>(
value, mData, valueCount );
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() );
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 );
577 else if ( mImage && mImage->constBits() )
579 const qsizetype len = std::min(
static_cast< qsizetype
>(
data.size() ), mImage->sizeInBytes() - offset );
580 ::memcpy( mImage->bits() + offset,
data.constData(), len );
587 if ( index >=
static_cast< qgssize >( mWidth )*mHeight )
589 QgsDebugMsgLevel( QStringLiteral(
"Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ), 4 );
594 return reinterpret_cast< char *
>( mData ) + index * mTypeSize;
598 if ( uchar *
data = mImage->bits() )
600 return reinterpret_cast< char *
>(
data + index * 4 );
610 if ( index >=
static_cast< qgssize >( mWidth )*mHeight )
612 QgsDebugMsgLevel( QStringLiteral(
"Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ), 4 );
617 return reinterpret_cast< const char *
>( mData ) + index * mTypeSize;
621 if (
const uchar *
data = mImage->constBits() )
623 return reinterpret_cast< const char *
>(
data + index * 4 );
632 return bits(
static_cast< qgssize >( row ) * mWidth + column );
639 return reinterpret_cast< char *
>( mData );
643 if ( uchar *
data = mImage->bits() )
645 return reinterpret_cast< char *
>(
data );
656 return reinterpret_cast< const char *
>( mData );
660 if (
const uchar *
data = mImage->constBits() )
662 return reinterpret_cast< const char *
>(
data );
672 if ( destDataType == mDataType )
return true;
676 void *
data =
convert( mData, mDataType, destDataType,
static_cast< qgssize >( mWidth ) *
static_cast< qgssize >( mHeight ) );
680 QgsDebugError( QStringLiteral(
"Cannot convert raster block" ) );
685 mDataType = destDataType;
690 const QImage::Format format = imageFormat( destDataType );
691 const QImage
image = mImage->convertToFormat( format );
693 mDataType = destDataType;
708 if ( scale == 1.0 && offset == 0.0 )
return;
711 for (
qgssize i = 0; i < size; ++i )
719 if ( rangeList.isEmpty() )
725 for (
qgssize i = 0; i < size; ++i )
727 const double val =
value( i );
740 return QImage( *mImage );
751 mImage =
new QImage( *
image );
752 mWidth = mImage->width();
753 mHeight = mImage->height();
754 mDataType =
dataType( mImage->format() );
756 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
786 for (
int i = 15; i <= 17; i++ )
788 s.setNum(
value,
'g', i );
789 const double doubleValue { s.toDouble( ) };
794 return QLocale().toString( doubleValue,
'g', i );
800 QgsDebugError( QStringLiteral(
"Cannot correctly parse printed value" ) );
812 for (
int i = 6; i <= 9; i++ )
814 s.setNum(
value,
'g', i );
815 const float floatValue { s.toFloat() };
820 return QLocale().toString( floatValue,
'g', i );
826 QgsDebugError( QStringLiteral(
"Cannot correctly parse printed value" ) );
832 const int destDataTypeSize =
typeSize( destDataType );
833 void *destData =
qgsMalloc( destDataTypeSize * size );
834 for (
qgssize i = 0; i < size; i++ )
848 ba.resize(
static_cast< int >( size ) );
849 char *
data = ba.data();
860 uc =
static_cast< quint8
>(
value );
861 memcpy(
data, &uc, size );
865 const qint8 myint8 =
static_cast< qint8
>(
value );
866 memcpy(
data, &myint8, size );
870 us =
static_cast< quint16
>(
value );
871 memcpy(
data, &us, size );
874 s =
static_cast< qint16
>(
value );
875 memcpy(
data, &s, size );
878 ui =
static_cast< quint32
>(
value );
879 memcpy(
data, &ui, size );
882 i =
static_cast< qint32
>(
value );
883 memcpy(
data, &i, size );
886 f =
static_cast< float >(
value );
887 memcpy(
data, &f, size );
890 d =
static_cast< double >(
value );
891 memcpy(
data, &d, size );
900 QgsDebugError( QStringLiteral(
"Data type is not supported" ) );
905bool QgsRasterBlock::createNoDataBitmap()
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 )
913 QgsDebugError( QStringLiteral(
"Couldn't allocate no data memory of %1 bytes" ).arg( mNoDataBitmapSize ) );
916 memset( mNoDataBitmap, 0, mNoDataBitmapSize );
922 return QStringLiteral(
"dataType = %1 width = %2 height = %3" )
923 .arg( qgsEnumValueToKey< Qgis::DataType >( mDataType ) ).arg( mWidth ).arg( mHeight );
933 QgsDebugMsgLevel( QStringLiteral(
"theWidth = %1 height = %2 xRes = %3 yRes = %4" ).arg(
width ).arg(
height ).arg( xRes ).arg( yRes ), 4 );
938 int right =
width - 1;
946 bottom = std::round( ( extent.
yMaximum() - subExtent.
yMinimum() ) / yRes ) - 1;
955 right = std::round( ( subExtent.
xMaximum() - extent.
xMinimum() ) / xRes ) - 1;
957 QRect
subRect = QRect( left, top, right - left + 1, bottom - top + 1 );
966 minimum = std::numeric_limits<double>::quiet_NaN();
970 const std::size_t offset = qgis_gdal::min_element( mData,
static_cast<std::size_t
>( mWidth ) *
static_cast< std::size_t
>( mHeight ),
973 row =
static_cast< int >( offset / mWidth );
974 column =
static_cast< int >( offset % mWidth );
984 maximum = std::numeric_limits<double>::quiet_NaN();
987 const std::size_t offset = qgis_gdal::max_element( mData,
static_cast<std::size_t
>( mWidth ) *
static_cast< std::size_t
>( mHeight ),
990 row =
static_cast< int >( offset / mWidth );
991 column =
static_cast< int >( offset % mWidth );
1001 minimum = std::numeric_limits<double>::quiet_NaN();
1002 maximum = std::numeric_limits<double>::quiet_NaN();
1006 const auto [minOffset, maxOffset] = qgis_gdal::minmax_element( mData,
static_cast<std::size_t
>( mWidth ) *
static_cast< std::size_t
>( mHeight ),
1009 minimumRow =
static_cast< int >( minOffset / mWidth );
1010 minimumColumn =
static_cast< int >( minOffset % mWidth );
1013 maximumRow =
static_cast< int >( maxOffset / mWidth );
1014 maximumColumn =
static_cast< int >( maxOffset % mWidth );
DataType
Raster data types.
@ 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.
@ 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.
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...
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.
void qgsFree(void *ptr)
Frees the memory space pointed to by ptr.
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
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...
bool qgsFloatNear(float a, float b, float epsilon=4 *FLT_EPSILON)
Compare two floats (but allow some difference)
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
void fillTypedData(double value, void *data, std::size_t count)
QList< QgsRasterRange > QgsRasterRangeList