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 ||
219 *
noDataValue = std::numeric_limits<double>::max() * -1.0;
232 QgsDebugMsgLevel( QStringLiteral(
"newDataType = %1 noDataValue = %2" ).arg( qgsEnumValueToKey< Qgis::DataType >( newDataType ) ).arg( *
noDataValue ), 4 );
238 mHasNoDataValue =
true;
244 mHasNoDataValue =
false;
245 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
254 if ( mHasNoDataValue )
258 QgsDebugError( QStringLiteral(
"Data block not allocated" ) );
263 QByteArray noDataByteArray =
valueBytes( mDataType, mNoDataValue );
264 if ( mNoDataValue == 0 )
270 const char *nodata = noDataByteArray.data();
271 for (
qgssize i = 0; i < static_cast< qgssize >( mWidth )*mHeight; i++ )
280 if ( !mNoDataBitmap )
282 if ( !createNoDataBitmap() )
288 memset( mNoDataBitmap, 0xff, mNoDataBitmapSize );
305 mImage->fill( NO_DATA_COLOR );
312 int top = exceptRect.top();
313 int bottom = exceptRect.bottom();
314 int left = exceptRect.left();
315 int right = exceptRect.right();
316 top = std::min( std::max( top, 0 ), mHeight - 1 );
317 left = std::min( std::max( left, 0 ), mWidth - 1 );
318 bottom = std::max( 0, std::min( bottom, mHeight - 1 ) );
319 right = std::max( 0, std::min( right, mWidth - 1 ) );
325 if ( mHasNoDataValue )
329 QgsDebugError( QStringLiteral(
"Data block not allocated" ) );
334 QByteArray noDataByteArray =
valueBytes( mDataType, mNoDataValue );
336 char *nodata = noDataByteArray.data();
338 for (
int c = 0;
c < mWidth;
c++ )
344 for (
int r = 0; r < mHeight; r++ )
346 if ( r >= top && r <= bottom )
continue;
351 for (
int r = top; r <= bottom; r++ )
358 const int w = mWidth - right - 1;
366 if ( !mNoDataBitmap )
368 if ( !createNoDataBitmap() )
380 char *nodataRow =
new char[mNoDataBitmapWidth];
382 memset( nodataRow, 0, mNoDataBitmapWidth );
383 for (
int c = 0;
c < mWidth;
c ++ )
385 const int byte =
c / 8;
386 const int bit =
c % 8;
387 const char nodata = 0x80 >> bit;
388 memset( nodataRow +
byte, nodataRow[
byte] | nodata, 1 );
392 for (
int r = 0; r < mHeight; r++ )
394 if ( r >= top && r <= bottom )
continue;
395 const qgssize i =
static_cast< qgssize >( r ) * mNoDataBitmapWidth;
396 memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
399 memset( nodataRow, 0, mNoDataBitmapWidth );
400 for (
int c = 0;
c < mWidth;
c ++ )
402 if (
c >= left &&
c <= right )
continue;
403 const int byte =
c / 8;
404 const int bit =
c % 8;
405 const char nodata = 0x80 >> bit;
406 memset( nodataRow +
byte, nodataRow[
byte] | nodata, 1 );
408 for (
int r = top; r <= bottom; r++ )
410 const qgssize i =
static_cast< qgssize >( r ) * mNoDataBitmapWidth;
411 memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
426 if ( mImage->width() != mWidth || mImage->height() != mHeight )
428 QgsDebugError( QStringLiteral(
"Image and block size differ" ) );
432 QgsDebugMsgLevel( QStringLiteral(
"Fill image depth = %1" ).arg( mImage->depth() ), 4 );
435 if ( mImage->depth() != 32 )
437 QgsDebugError( QStringLiteral(
"Unsupported image depth" ) );
441 const QRgb nodataRgba = NO_DATA_COLOR;
442 QRgb *nodataRow =
new QRgb[mWidth];
443 const int rgbSize =
sizeof( QRgb );
444 for (
int c = 0;
c < mWidth;
c ++ )
446 nodataRow[
c] = nodataRgba;
450 for (
int r = 0; r < mHeight; r++ )
452 if ( r >= top && r <= bottom )
continue;
454 memcpy(
reinterpret_cast< void *
>( mImage->bits() + rgbSize * i ), nodataRow, rgbSize *
static_cast< qgssize >( mWidth ) );
457 for (
int r = top; r <= bottom; r++ )
463 memcpy(
reinterpret_cast< void *
>( mImage->bits() + rgbSize * i ), nodataRow, rgbSize *
static_cast< qgssize >( left - 1 ) );
467 const int w = mWidth - right - 1;
468 memcpy(
reinterpret_cast< void *
>( mImage->bits() + rgbSize * i ), nodataRow, rgbSize *
static_cast< qgssize >( w ) );
478 return QByteArray::fromRawData(
static_cast<const char *
>( mData ),
typeSize( mDataType ) * mWidth * mHeight );
479 else if ( mImage && mImage->constBits() )
480 return QByteArray::fromRawData(
reinterpret_cast<const char *
>( mImage->constBits() ), mImage->sizeInBytes() );
492 const int len = std::min(
static_cast<int>(
data.size() ),
typeSize( mDataType ) * mWidth * mHeight - offset );
493 ::memcpy(
static_cast<char *
>( mData ) + offset,
data.constData(), len );
495 else if ( mImage && mImage->constBits() )
497 const qsizetype len = std::min(
static_cast< qsizetype
>(
data.size() ), mImage->sizeInBytes() - offset );
498 ::memcpy( mImage->bits() + offset,
data.constData(), len );
505 if ( index >=
static_cast< qgssize >( mWidth )*mHeight )
507 QgsDebugMsgLevel( QStringLiteral(
"Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ), 4 );
512 return reinterpret_cast< char *
>( mData ) + index * mTypeSize;
516 if ( uchar *
data = mImage->bits() )
518 return reinterpret_cast< char *
>(
data + index * 4 );
528 if ( index >=
static_cast< qgssize >( mWidth )*mHeight )
530 QgsDebugMsgLevel( QStringLiteral(
"Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ), 4 );
535 return reinterpret_cast< const char *
>( mData ) + index * mTypeSize;
539 if (
const uchar *
data = mImage->constBits() )
541 return reinterpret_cast< const char *
>(
data + index * 4 );
550 return bits(
static_cast< qgssize >( row ) * mWidth + column );
557 return reinterpret_cast< char *
>( mData );
561 if ( uchar *
data = mImage->bits() )
563 return reinterpret_cast< char *
>(
data );
574 return reinterpret_cast< const char *
>( mData );
578 if (
const uchar *
data = mImage->constBits() )
580 return reinterpret_cast< const char *
>(
data );
590 if ( destDataType == mDataType )
return true;
594 void *
data =
convert( mData, mDataType, destDataType,
static_cast< qgssize >( mWidth ) *
static_cast< qgssize >( mHeight ) );
598 QgsDebugError( QStringLiteral(
"Cannot convert raster block" ) );
603 mDataType = destDataType;
608 const QImage::Format format = imageFormat( destDataType );
609 const QImage
image = mImage->convertToFormat( format );
611 mDataType = destDataType;
626 if ( scale == 1.0 && offset == 0.0 )
return;
629 for (
qgssize i = 0; i < size; ++i )
637 if ( rangeList.isEmpty() )
643 for (
qgssize i = 0; i < size; ++i )
645 const double val =
value( i );
658 return QImage( *mImage );
669 mImage =
new QImage( *
image );
670 mWidth = mImage->width();
671 mHeight = mImage->height();
672 mDataType =
dataType( mImage->format() );
674 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
704 for (
int i = 15; i <= 17; i++ )
706 s.setNum(
value,
'g', i );
707 const double doubleValue { s.toDouble( ) };
712 return QLocale().toString( doubleValue,
'g', i );
718 QgsDebugError( QStringLiteral(
"Cannot correctly parse printed value" ) );
730 for (
int i = 6; i <= 9; i++ )
732 s.setNum(
value,
'g', i );
733 const float floatValue { s.toFloat() };
738 return QLocale().toString( floatValue,
'g', i );
744 QgsDebugError( QStringLiteral(
"Cannot correctly parse printed value" ) );
750 const int destDataTypeSize =
typeSize( destDataType );
751 void *destData =
qgsMalloc( destDataTypeSize * size );
752 for (
qgssize i = 0; i < size; i++ )
766 ba.resize(
static_cast< int >( size ) );
767 char *
data = ba.data();
778 uc =
static_cast< quint8
>(
value );
779 memcpy(
data, &uc, size );
783 const qint8 myint8 =
static_cast< qint8
>(
value );
784 memcpy(
data, &myint8, size );
788 us =
static_cast< quint16
>(
value );
789 memcpy(
data, &us, size );
792 s =
static_cast< qint16
>(
value );
793 memcpy(
data, &s, size );
796 ui =
static_cast< quint32
>(
value );
797 memcpy(
data, &ui, size );
800 i =
static_cast< qint32
>(
value );
801 memcpy(
data, &i, size );
804 f =
static_cast< float >(
value );
805 memcpy(
data, &f, size );
808 d =
static_cast< double >(
value );
809 memcpy(
data, &d, size );
818 QgsDebugError( QStringLiteral(
"Data type is not supported" ) );
823bool QgsRasterBlock::createNoDataBitmap()
825 mNoDataBitmapWidth = mWidth / 8 + 1;
826 mNoDataBitmapSize =
static_cast< qgssize >( mNoDataBitmapWidth ) * mHeight;
827 QgsDebugMsgLevel( QStringLiteral(
"allocate %1 bytes" ).arg( mNoDataBitmapSize ), 4 );
828 mNoDataBitmap =
reinterpret_cast< char *
>(
qgsMalloc( mNoDataBitmapSize ) );
829 if ( !mNoDataBitmap )
831 QgsDebugError( QStringLiteral(
"Couldn't allocate no data memory of %1 bytes" ).arg( mNoDataBitmapSize ) );
834 memset( mNoDataBitmap, 0, mNoDataBitmapSize );
840 return QStringLiteral(
"dataType = %1 width = %2 height = %3" )
841 .arg( qgsEnumValueToKey< Qgis::DataType >( mDataType ) ).arg( mWidth ).arg( mHeight );
851 QgsDebugMsgLevel( QStringLiteral(
"theWidth = %1 height = %2 xRes = %3 yRes = %4" ).arg(
width ).arg(
height ).arg( xRes ).arg( yRes ), 4 );
856 int right =
width - 1;
864 bottom = std::round( ( extent.
yMaximum() - subExtent.
yMinimum() ) / yRes ) - 1;
873 right = std::round( ( subExtent.
xMaximum() - extent.
xMinimum() ) / xRes ) - 1;
875 QRect
subRect = QRect( left, top, right - left + 1, bottom - top + 1 );
884 minimum = std::numeric_limits<double>::quiet_NaN();
888 const std::size_t offset = qgis_gdal::min_element( mData,
static_cast<std::size_t
>( mWidth ) *
static_cast< std::size_t
>( mHeight ),
891 row =
static_cast< int >( offset / mWidth );
892 column =
static_cast< int >( offset % mWidth );
902 maximum = std::numeric_limits<double>::quiet_NaN();
905 const std::size_t offset = qgis_gdal::max_element( mData,
static_cast<std::size_t
>( mWidth ) *
static_cast< std::size_t
>( mHeight ),
908 row =
static_cast< int >( offset / mWidth );
909 column =
static_cast< int >( offset % mWidth );
919 minimum = std::numeric_limits<double>::quiet_NaN();
920 maximum = std::numeric_limits<double>::quiet_NaN();
924 const auto [minOffset, maxOffset] = qgis_gdal::minmax_element( mData,
static_cast<std::size_t
>( mWidth ) *
static_cast< std::size_t
>( mHeight ),
927 minimumRow =
static_cast< int >( minOffset / mWidth );
928 minimumColumn =
static_cast< int >( minOffset % mWidth );
931 maximumRow =
static_cast< int >( maxOffset / mWidth );
932 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 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".
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 data type is color.
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)
QList< QgsRasterRange > QgsRasterRangeList