30#define GDAL_MINMAXELT_NS qgis_gdal
31#include "gdal_minmax_element.hpp"
34const QRgb QgsRasterBlock::NO_DATA_COLOR = qRgba( 0, 0, 0, 0 );
37 : mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
45 , mNoDataValue( std::numeric_limits<double>::quiet_NaN() )
47 ( void )
reset( mDataType, mWidth, mHeight );
52 QgsDebugMsgLevel( QStringLiteral(
"mData = %1" ).arg(
reinterpret_cast< quint64
>( mData ) ), 4 );
67 mNoDataBitmap =
nullptr;
72 mHasNoDataValue =
false;
73 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
91 const QImage::Format format = imageFormat(
dataType );
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 );
110QImage::Format QgsRasterBlock::imageFormat(
Qgis::DataType dataType )
114 return QImage::Format_ARGB32;
118 return QImage::Format_ARGB32_Premultiplied;
120 return QImage::Format_Invalid;
125 if ( format == QImage::Format_ARGB32 )
129 else if ( format == QImage::Format_ARGB32_Premultiplied )
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 ||
246 *
noDataValue = std::numeric_limits<double>::max() * -1.0;
265 mHasNoDataValue =
true;
271 mHasNoDataValue =
false;
272 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
280 if ( mHasNoDataValue )
282 return fill( mNoDataValue );
287 if ( !mNoDataBitmap )
289 if ( !createNoDataBitmap() )
295 memset( mNoDataBitmap, 0xff, mNoDataBitmapSize );
313 mImage->fill( NO_DATA_COLOR );
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 ) );
333 if ( mHasNoDataValue )
337 QgsDebugError( QStringLiteral(
"Data block not allocated" ) );
342 QByteArray noDataByteArray =
valueBytes( mDataType, mNoDataValue );
344 char *nodata = noDataByteArray.data();
346 for (
int c = 0;
c < mWidth;
c++ )
352 for (
int r = 0; r < mHeight; r++ )
354 if ( r >= top && r <= bottom )
continue;
359 for (
int r = top; r <= bottom; r++ )
366 const int w = mWidth - right - 1;
374 if ( !mNoDataBitmap )
376 if ( !createNoDataBitmap() )
388 char *nodataRow =
new char[mNoDataBitmapWidth];
390 memset( nodataRow, 0, mNoDataBitmapWidth );
391 for (
int c = 0;
c < mWidth;
c ++ )
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 );
400 for (
int r = 0; r < mHeight; r++ )
402 if ( r >= top && r <= bottom )
continue;
403 const qgssize i =
static_cast< qgssize >( r ) * mNoDataBitmapWidth;
404 memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
407 memset( nodataRow, 0, mNoDataBitmapWidth );
408 for (
int c = 0;
c < mWidth;
c ++ )
410 if (
c >= left &&
c <= right )
continue;
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 );
416 for (
int r = top; r <= bottom; r++ )
418 const qgssize i =
static_cast< qgssize >( r ) * mNoDataBitmapWidth;
419 memcpy( mNoDataBitmap + i, nodataRow, mNoDataBitmapWidth );
434 if ( mImage->width() != mWidth || mImage->height() != mHeight )
436 QgsDebugError( QStringLiteral(
"Image and block size differ" ) );
440 QgsDebugMsgLevel( QStringLiteral(
"Fill image depth = %1" ).arg( mImage->depth() ), 4 );
443 if ( mImage->depth() != 32 )
445 QgsDebugError( QStringLiteral(
"Unsupported image depth" ) );
449 const QRgb nodataRgba = NO_DATA_COLOR;
450 QRgb *nodataRow =
new QRgb[mWidth];
451 const int rgbSize =
sizeof( QRgb );
452 for (
int c = 0;
c < mWidth;
c ++ )
454 nodataRow[
c] = nodataRgba;
458 for (
int r = 0; r < mHeight; r++ )
460 if ( r >= top && r <= bottom )
continue;
462 memcpy(
reinterpret_cast< void *
>( mImage->bits() + rgbSize * i ), nodataRow, rgbSize *
static_cast< qgssize >( mWidth ) );
465 for (
int r = top; r <= bottom; r++ )
471 memcpy(
reinterpret_cast< void *
>( mImage->bits() + rgbSize * i ), nodataRow, rgbSize *
static_cast< qgssize >( left - 1 ) );
475 const int w = mWidth - right - 1;
476 memcpy(
reinterpret_cast< void *
>( mImage->bits() + rgbSize * i ), nodataRow, rgbSize *
static_cast< qgssize >( w ) );
486 std::fill_n(
static_cast<T *
>( data ), count,
static_cast<T
>( value ) );
494 QgsDebugError( QStringLiteral(
"Cannot fill image block" ) );
500 QgsDebugError( QStringLiteral(
"Data block not allocated" ) );
505 const std::size_t valueCount =
static_cast<size_t>( mWidth ) * mHeight;
506 const std::size_t totalSize = valueCount *
dataTypeSize;
513 memset( mData, 0, totalSize );
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() );
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 );
578 else if ( mImage && mImage->constBits() )
580 const qsizetype len = std::min(
static_cast< qsizetype
>(
data.size() ), mImage->sizeInBytes() - offset );
581 ::memcpy( mImage->bits() + offset,
data.constData(), len );
588 if ( index >=
static_cast< qgssize >( mWidth )*mHeight )
590 QgsDebugMsgLevel( QStringLiteral(
"Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ), 4 );
595 return reinterpret_cast< char *
>( mData ) + index * mTypeSize;
599 if ( uchar *
data = mImage->bits() )
601 return reinterpret_cast< char *
>(
data + index * 4 );
611 if ( index >=
static_cast< qgssize >( mWidth )*mHeight )
613 QgsDebugMsgLevel( QStringLiteral(
"Index %1 out of range (%2 x %3)" ).arg( index ).arg( mWidth ).arg( mHeight ), 4 );
618 return reinterpret_cast< const char *
>( mData ) + index * mTypeSize;
622 if (
const uchar *
data = mImage->constBits() )
624 return reinterpret_cast< const char *
>(
data + index * 4 );
633 return bits(
static_cast< qgssize >( row ) * mWidth + column );
640 return reinterpret_cast< char *
>( mData );
644 if ( uchar *
data = mImage->bits() )
646 return reinterpret_cast< char *
>(
data );
657 return reinterpret_cast< const char *
>( mData );
661 if (
const uchar *
data = mImage->constBits() )
663 return reinterpret_cast< const char *
>(
data );
673 if ( destDataType == mDataType )
return true;
677 void *
data =
convert( mData, mDataType, destDataType,
static_cast< qgssize >( mWidth ) *
static_cast< qgssize >( mHeight ) );
681 QgsDebugError( QStringLiteral(
"Cannot convert raster block" ) );
686 mDataType = destDataType;
691 const QImage::Format format = imageFormat( destDataType );
692 const QImage
image = mImage->convertToFormat( format );
694 mDataType = destDataType;
709 if ( scale == 1.0 && offset == 0.0 )
return;
712 for (
qgssize i = 0; i < size; ++i )
720 if ( rangeList.isEmpty() )
726 for (
qgssize i = 0; i < size; ++i )
728 const double val =
value( i );
741 return QImage( *mImage );
752 mImage =
new QImage( *
image );
753 mWidth = mImage->width();
754 mHeight = mImage->height();
755 mDataType =
dataType( mImage->format() );
757 mNoDataValue = std::numeric_limits<double>::quiet_NaN();
787 for (
int i = 15; i <= 17; i++ )
789 s.setNum(
value,
'g', i );
790 const double doubleValue { s.toDouble( ) };
795 return QLocale().toString( doubleValue,
'g', i );
801 QgsDebugError( QStringLiteral(
"Cannot correctly parse printed value" ) );
813 for (
int i = 6; i <= 9; i++ )
815 s.setNum(
value,
'g', i );
816 const float floatValue { s.toFloat() };
821 return QLocale().toString( floatValue,
'g', i );
827 QgsDebugError( QStringLiteral(
"Cannot correctly parse printed value" ) );
833 const int destDataTypeSize =
typeSize( destDataType );
834 void *destData =
qgsMalloc( destDataTypeSize * size );
835 for (
qgssize i = 0; i < size; i++ )
849 ba.resize(
static_cast< int >( size ) );
850 char *
data = ba.data();
861 uc =
static_cast< quint8
>(
value );
862 memcpy(
data, &uc, size );
866 const qint8 myint8 =
static_cast< qint8
>(
value );
867 memcpy(
data, &myint8, size );
871 us =
static_cast< quint16
>(
value );
872 memcpy(
data, &us, size );
875 s =
static_cast< qint16
>(
value );
876 memcpy(
data, &s, size );
879 ui =
static_cast< quint32
>(
value );
880 memcpy(
data, &ui, size );
883 i =
static_cast< qint32
>(
value );
884 memcpy(
data, &i, size );
887 f =
static_cast< float >(
value );
888 memcpy(
data, &f, size );
891 d =
static_cast< double >(
value );
892 memcpy(
data, &d, size );
901 QgsDebugError( QStringLiteral(
"Data type is not supported" ) );
906bool QgsRasterBlock::createNoDataBitmap()
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 )
914 QgsDebugError( QStringLiteral(
"Couldn't allocate no data memory of %1 bytes" ).arg( mNoDataBitmapSize ) );
917 memset( mNoDataBitmap, 0, mNoDataBitmapSize );
923 return QStringLiteral(
"dataType = %1 width = %2 height = %3" )
934 QgsDebugMsgLevel( QStringLiteral(
"theWidth = %1 height = %2 xRes = %3 yRes = %4" ).arg(
width ).arg(
height ).arg( xRes ).arg( yRes ), 4 );
939 int right =
width - 1;
947 bottom = std::round( ( extent.
yMaximum() - subExtent.
yMinimum() ) / yRes ) - 1;
956 right = std::round( ( subExtent.
xMaximum() - extent.
xMinimum() ) / xRes ) - 1;
958 QRect
subRect = QRect( left, top, right - left + 1, bottom - top + 1 );
967 minimum = std::numeric_limits<double>::quiet_NaN();
971 const std::size_t offset = qgis_gdal::min_element( mData,
static_cast<std::size_t
>( mWidth ) *
static_cast< std::size_t
>( mHeight ),
974 row =
static_cast< int >( offset / mWidth );
975 column =
static_cast< int >( offset % mWidth );
985 maximum = std::numeric_limits<double>::quiet_NaN();
988 const std::size_t offset = qgis_gdal::max_element( mData,
static_cast<std::size_t
>( mWidth ) *
static_cast< std::size_t
>( mHeight ),
991 row =
static_cast< int >( offset / mWidth );
992 column =
static_cast< int >( offset % mWidth );
1002 minimum = std::numeric_limits<double>::quiet_NaN();
1003 maximum = std::numeric_limits<double>::quiet_NaN();
1007 const auto [minOffset, maxOffset] = qgis_gdal::minmax_element( mData,
static_cast<std::size_t
>( mWidth ) *
static_cast< std::size_t
>( mHeight ),
1010 minimumRow =
static_cast< int >( minOffset / mWidth );
1011 minimumColumn =
static_cast< int >( minOffset % mWidth );
1014 maximumRow =
static_cast< int >( maxOffset / mWidth );
1015 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