34#include <QDomDocument>
37#include <QRegularExpression>
42using namespace Qt::StringLiterals;
44const int QgsPalettedRasterRenderer::MAX_FLOAT_CLASSES = 65536;
51 QHash<QString, QHash<QColor, QVector<QVariant>>> classData;
53 classData.reserve(
classes.size() );
55 QVector<QString> labels;
56 labels.reserve(
classes.size() );
60 if ( !classData.contains( klass.label ) )
62 labels.push_back( klass.label );
64 classData[klass.label][klass.color].push_back( klass.value );
67 mMultiValueClassData.reserve( classData.size() );
69 for (
auto labelIt = labels.constBegin(); labelIt != labels.constEnd(); ++labelIt )
71 for ( auto colorIt = classData[*labelIt].constBegin(); colorIt != classData[*labelIt].constEnd(); ++colorIt )
73 mMultiValueClassData.push_back( MultiValueClass{ colorIt.value(), colorIt.key(), *labelIt } );
83 , mMultiValueClassData( classes )
91 auto renderer = std::make_unique< QgsPalettedRasterRenderer >(
nullptr, mBand, mMultiValueClassData );
93 if ( mSourceColorRamp )
94 renderer->setSourceColorRamp( mSourceColorRamp->clone() );
96 renderer->copyCommonProperties(
this );
98 return renderer.release();
113 const int bandNumber = elem.attribute( u
"band"_s, u
"-1"_s ).toInt();
116 const QDomElement paletteElem = elem.firstChildElement( u
"colorPalette"_s );
117 if ( !paletteElem.isNull() )
119 const QDomNodeList paletteEntries = paletteElem.elementsByTagName( u
"paletteEntry"_s );
121 QDomElement entryElem;
124 for (
int i = 0; i < paletteEntries.size(); ++i )
128 entryElem = paletteEntries.at( i ).toElement();
129 value = entryElem.attribute( u
"value"_s, u
"0"_s ).toDouble();
130 color = QColor( entryElem.attribute( u
"color"_s, u
"#000000"_s ) );
131 color.setAlpha( entryElem.attribute( u
"alpha"_s, u
"255"_s ).toInt() );
132 label = entryElem.attribute( u
"label"_s );
133 QgsDebugMsgLevel( u
"Value: %1, label: %2, color: %3"_s.arg( value ).arg(
label, entryElem.attribute( u
"color"_s ) ), 4 );
142 QDomElement sourceColorRampElem = elem.firstChildElement( u
"colorramp"_s );
143 if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( u
"name"_s ) ==
"[source]"_L1 )
158 return mMultiValueClassData;
163 mMultiValueClassData =
classes;
169 if ( ! mMultiValueClassData.isEmpty() )
171 const auto constMClassData = mMultiValueClassData;
174 if (
c.values.contains( idx ) )
184 MultiValueClassData::iterator cMvIt = mMultiValueClassData.begin();
185 for ( ; cMvIt != mMultiValueClassData.end(); ++cMvIt )
187 if ( cMvIt->values.contains( idx ) )
189 cMvIt->label =
label;
217 auto outputBlock = std::make_unique<QgsRasterBlock>();
218 if ( !
mInput || mMultiValueClassData.isEmpty() )
220 return outputBlock.release();
223 const std::shared_ptr< QgsRasterBlock > inputBlock(
mInput->block( mBand,
extent, width, height, feedback ) );
225 if ( !inputBlock || inputBlock->isEmpty() )
228 return outputBlock.release();
234 std::shared_ptr< QgsRasterBlock > alphaBlock;
239 if ( !alphaBlock || alphaBlock->isEmpty() )
241 return outputBlock.release();
246 alphaBlock = inputBlock;
251 return outputBlock.release();
258 Q_ASSERT( outputBlock );
259 unsigned int *outputData = (
unsigned int * )( outputBlock->bits() );
262 bool isNoData =
false;
263 for (
qgssize i = 0; i < rasterSize; ++i )
265 const double value = inputBlock->valueAndNoData( i, isNoData );
268 outputData[i] = myDefaultColor;
271 if ( !mColors.contains( value ) )
273 outputData[i] = myDefaultColor;
277 if ( !hasTransparency )
279 outputData[i] = mColors.value( value );
290 const double alpha = alphaBlock->value( i );
293 outputBlock->setColor( i, myDefaultColor );
298 currentOpacity *= alpha / 255.0;
302 const QRgb
c = mColors.value( value );
303 outputData[i] = qRgba( currentOpacity * qRed(
c ), currentOpacity * qGreen(
c ), currentOpacity * qBlue(
c ), currentOpacity * qAlpha(
c ) );
307 return outputBlock.release();
312 return mMultiValueClassData.size();
317 if ( parentElem.isNull() )
322 QDomElement rasterRendererElem = doc.createElement( u
"rasterrenderer"_s );
325 rasterRendererElem.setAttribute( u
"band"_s, mBand );
326 QDomElement colorPaletteElem = doc.createElement( u
"colorPalette"_s );
327 const ClassData klassData { classData() };
328 ClassData::const_iterator it = klassData.constBegin();
329 for ( ; it != klassData.constEnd(); ++it )
331 const QColor color = it->color;
332 QDomElement colorElem = doc.createElement( u
"paletteEntry"_s );
333 colorElem.setAttribute( u
"value"_s, it->value );
334 colorElem.setAttribute( u
"color"_s, color.name() );
335 colorElem.setAttribute( u
"alpha"_s, color.alpha() );
336 if ( !it->label.isEmpty() )
338 colorElem.setAttribute( u
"label"_s, it->label );
340 colorPaletteElem.appendChild( colorElem );
342 rasterRendererElem.appendChild( colorPaletteElem );
345 if ( mSourceColorRamp )
348 rasterRendererElem.appendChild( colorRampElem );
351 parentElem.appendChild( rasterRendererElem );
358 toSld( doc, element, context );
367 const QDomNodeList elements = element.elementsByTagName( u
"sld:RasterSymbolizer"_s );
368 if ( elements.size() == 0 )
372 QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
375 QDomElement channelSelectionElem = doc.createElement( u
"sld:ChannelSelection"_s );
376 rasterSymbolizerElem.appendChild( channelSelectionElem );
379 QDomElement channelElem = doc.createElement( u
"sld:GrayChannel"_s );
380 channelSelectionElem.appendChild( channelElem );
383 QDomElement sourceChannelNameElem = doc.createElement( u
"sld:SourceChannelName"_s );
384 sourceChannelNameElem.appendChild( doc.createTextNode( QString::number( mBand ) ) );
385 channelElem.appendChild( sourceChannelNameElem );
388 QDomElement colorMapElem = doc.createElement( u
"sld:ColorMap"_s );
389 colorMapElem.setAttribute( u
"type"_s, u
"values"_s );
390 if ( this->
classes().size() >= 255 )
391 colorMapElem.setAttribute( u
"extended"_s, u
"true"_s );
392 rasterSymbolizerElem.appendChild( colorMapElem );
396 const QList<QgsPalettedRasterRenderer::Class>
classes = this->
classes();
397 QList<QgsPalettedRasterRenderer::Class>::const_iterator classDataIt =
classes.constBegin();
398 for ( ; classDataIt !=
classes.constEnd(); ++classDataIt )
400 QDomElement colorMapEntryElem = doc.createElement( u
"sld:ColorMapEntry"_s );
401 colorMapElem.appendChild( colorMapEntryElem );
404 colorMapEntryElem.setAttribute( u
"color"_s, classDataIt->color.name() );
405 colorMapEntryElem.setAttribute( u
"quantity"_s, QString::number( classDataIt->value ) );
406 colorMapEntryElem.setAttribute( u
"label"_s, classDataIt->label );
407 if ( classDataIt->color.alphaF() != 1.0 )
409 colorMapEntryElem.setAttribute( u
"opacity"_s, QString::number( classDataIt->color.alphaF() ) );
417 if ( mSourceColorRamp )
429 QList< QPair< QString, QColor > > symbolItems;
432 QString lab { classData.label };
436 for (
const QVariant &val : std::as_const( classData.values ) )
440 const double numericValue { val.toDouble( &ok ) };
443 values.push_back( QLocale().toString( numericValue ) );
447 values.push_back( val.toString() );
450 lab = values.join( QChar(
' ' ) );
452 symbolItems << qMakePair( lab, classData.color );
460 QList<QgsLayerTreeModelLegendNode *> res;
463 if ( !name.isEmpty() )
469 res.reserve( res.size() + items.size() );
470 for (
const QPair< QString, QColor > &item : items )
491 mSourceColorRamp.reset( ramp );
496 return mSourceColorRamp.get();
501 QList<QgsColorRampShader::ColorRampItem>::const_iterator colorIt = table.constBegin();
503 for ( ; colorIt != table.constEnd(); ++colorIt )
512 if ( ! attributeTable || ! attributeTable->
isValid() )
519 const QList<QgsRasterAttributeTable::MinMaxClass> minMaxClasses { attributeTable->
minMaxClasses( classificationColumn ) };
520 if ( minMaxClasses.empty() )
525 QVector<QVariant> values;
526 for (
const double val : std::as_const( minMaxClass.minMaxValues ) )
528 values.push_back( QVariant( val ) );
530 classData.push_back( { values, minMaxClass.color, minMaxClass.name } );
533 int numClasses {
static_cast<int>( classData.count( ) ) };
536 if ( ramp && numClasses > 0 )
544 randomRamp->setTotalColorCount( numClasses );
547 if ( numClasses > 1 )
550 QgsPalettedRasterRenderer::MultiValueClassData::iterator cIt = classData.begin();
551 for ( ; cIt != classData.end(); ++cIt )
553 cIt->color = ramp->
color( i /
static_cast<double>( numClasses ) );
565 const thread_local QRegularExpression linePartRx( u
"[\\s,:]+"_s );
567 const QStringList parts =
string.split(
'\n', Qt::SkipEmptyParts );
568 for (
const QString &part : parts )
570 const QStringList lineParts = part.split( linePartRx, Qt::SkipEmptyParts );
572 switch ( lineParts.count() )
576 const int value = lineParts.at( 0 ).toInt( &ok );
586 const int value = lineParts.at( 0 ).toInt( &ok );
590 const QColor
c( lineParts.at( 1 ) );
598 if ( lineParts.count() < 4 )
601 const int value = lineParts.at( 0 ).toInt( &ok );
606 const double r = lineParts.at( 1 ).toDouble( &rOk );
608 const double g = lineParts.at( 2 ).toDouble( &gOk );
610 const double b = lineParts.at( 3 ).toDouble( &bOk );
613 if ( rOk && gOk && bOk )
615 c = QColor( r, g, b );
618 if ( lineParts.count() >= 5 )
620 const double alpha = lineParts.at( 4 ).toDouble( &ok );
626 if ( lineParts.count() > 5 )
628 label = lineParts.mid( 5 ).join(
' ' );
642 QFile inputFile( path );
644 if ( inputFile.open( QIODevice::ReadOnly ) )
646 QTextStream in( &inputFile );
647 input = in.readAll();
658 std::sort( cd.begin(), cd.end(), [](
const Class & a,
const Class & b ) ->
bool
660 return a.value < b.value;
663 const auto constCd = cd;
664 for (
const Class &
c : constCd )
666 out << u
"%1 %2 %3 %4 %5 %6"_s.arg(
c.value ).arg(
c.color.red() )
667 .arg(
c.color.green() ).arg(
c.color.blue() ).arg(
c.color.alpha() ).arg(
c.label );
669 return out.join(
'\n' );
679 if ( bandNumber > 0 && bandNumber <= raster->
bandCount() )
681 qlonglong numClasses = 0;
695 std::set<double> values;
703 const int nbBlocksWidth =
static_cast< int >( std::ceil( 1.0 * raster->
xSize() / maxWidth ) );
704 const int nbBlocksHeight =
static_cast< int >( std::ceil( 1.0 * raster->
ySize() / maxHeight ) );
705 const int nbBlocks = nbBlocksWidth * nbBlocksHeight;
711 std::unique_ptr< QgsRasterBlock > rasterBlock;
713 bool isNoData =
false;
714 while ( iter.
readNextRasterPart( bandNumber, iterCols, iterRows, rasterBlock, iterLeft, iterTop, &blockExtent ) )
717 feedback->
setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
722 for (
int row = 0; row < iterRows; row++ )
727 for (
int column = 0; column < iterCols; column++ )
732 const double currentValue = rasterBlock->valueAndNoData( row, column, isNoData );
733 if ( numClasses >= MAX_FLOAT_CLASSES )
738 if ( !isNoData && values.find( currentValue ) == values.end() )
740 values.insert( currentValue );
741 data.push_back(
Class( currentValue, QColor(), QLocale().toString( currentValue ) ) );
748 std::sort( data.begin(), data.end(), [](
const Class & a,
const Class & b ) ->
bool
750 return a.value < b.value;
763 const int bins = std::ceil( max - min ) + 1;
776 for (
int idx = 0; idx <
histogram.binCount; ++idx )
778 const int count =
histogram.histogramVector.at( idx );
781 data <<
Class( currentValue, QColor(), QLocale().toString( currentValue ) );
784 currentValue += interval;
795 if ( ramp && numClasses > 0 )
803 randomRamp->setTotalColorCount( data.count() );
806 if ( numClasses > 1 )
809 QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin();
810 for ( ; cIt != data.end(); ++cIt )
815 feedback->
setProgress( std::max<int>( 1, 100 * ( i + 1 ) / numClasses ) );
817 cIt->color = ramp->
color( i /
static_cast<double>( numClasses ) );
828 for (
const MultiValueClass &klass : std::as_const( mMultiValueClassData ) )
830 for (
const QVariant &entry : std::as_const( klass.values ) )
833 const double value { entry.toDouble( &ok )};
836 data.push_back( { value, klass.color, klass.label } );
840 QgsDebugMsgLevel( u
"Could not convert class value '%1' to double when creating classes."_s.arg( entry.toString() ), 2 );
847void QgsPalettedRasterRenderer::updateArrays()
851 MultiValueClassData::const_iterator it = mMultiValueClassData.constBegin();
852 for ( ; it != mMultiValueClassData.constEnd(); ++it )
854 for (
const QVariant &entry : std::as_const( it->values ) )
857 const double value { entry.toDouble( &ok )};
860 mColors[value] = qPremultiply( it->color.rgba() );
864 QgsDebugMsgLevel( u
"Could not convert class value '%1' to double for color lookup."_s.arg( entry.toString() ), 2 );
QFlags< RasterRendererFlag > RasterRendererFlags
Flags which control behavior of raster renderers.
@ InternalLayerOpacityHandling
The renderer internally handles the raster layer's opacity, so the default layer level opacity handli...
@ Float32
Thirty two bit floating point (float).
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ Float64
Sixty four bit floating point (double).
Abstract base class for color ramps.
virtual QColor color(double value) const =0
Returns the color corresponding to a specified value.
bool isCanceled() const
Tells whether the operation has been canceled already.
void setProgress(double progress)
Sets the current progress for the feedback object.
Layer tree node points to a map layer.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
Properties of a multi value class: a class that contains multiple values.
QColor color
Color to render values.
MultiValueClass(const QVector< QVariant > &values, const QColor &color=QColor(), const QString &label=QString())
Constructor for MultiValueClass from a list of values.
QVector< QVariant > values
Values.
QString label
Label for values.
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
QList< QgsLayerTreeModelLegendNode * > createLegendNodes(QgsLayerTreeLayer *nodeLayer) override
Creates a set of legend nodes representing the renderer.
static QgsPalettedRasterRenderer::MultiValueClassData rasterAttributeTableToClassData(const QgsRasterAttributeTable *attributeTable, int classificationColumn=-1, QgsColorRamp *ramp=nullptr)
Reads and returns classes from the Raster Attribute Table attributeTable, optionally classifying the ...
QgsColorRamp * sourceColorRamp() const
Gets the source color ramp.
static QgsPalettedRasterRenderer::ClassData classDataFromString(const QString &string)
Converts a string containing a color table or class data to to paletted renderer class data.
QString label(double idx) const
Returns optional category label.
void setSourceColorRamp(QgsColorRamp *ramp)
Set the source color ramp.
QList< int > usesBands() const override
Returns a list of band numbers used by the renderer.
bool canCreateRasterAttributeTable() const override
Returns true if the renderer is suitable for attribute table creation.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
Q_DECL_DEPRECATED int band() const
Returns the raster band used for rendering the raster.
QList< QgsPalettedRasterRenderer::Class > ClassData
Map of value to class properties.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
Qgis::RasterRendererFlags flags() const override
Returns flags which dictate renderer behavior.
int nColors() const
Returns number of colors.
QList< QgsPalettedRasterRenderer::MultiValueClass > MultiValueClassData
Map of multi value to class properties.
static QgsPalettedRasterRenderer::ClassData classDataFromFile(const QString &path)
Opens a color table file and returns corresponding paletted renderer class data.
bool setInputBand(int band) override
Attempts to set the input band for the renderer.
static QgsRasterRenderer * create(const QDomElement &elem, QgsRasterInterface *input)
void setMultiValueClasses(const MultiValueClassData &classes)
Sets the multi value classes to setMultiValueClasses.
static QgsPalettedRasterRenderer::ClassData colorTableToClassData(const QList< QgsColorRampShader::ColorRampItem > &table)
Converts a raster color table to paletted renderer class data.
ClassData classes() const
Returns a map of value to classes (colors) used by the renderer.
QList< QPair< QString, QColor > > legendSymbologyItems() const override
Returns symbology items if provided by renderer.
QgsPalettedRasterRenderer * clone() const override
Clone itself, create deep copy.
void setLabel(double idx, const QString &label)
Set category label.
static QgsPalettedRasterRenderer::ClassData classDataFromRaster(QgsRasterInterface *raster, int bandNumber, QgsColorRamp *ramp=nullptr, QgsRasterBlockFeedback *feedback=nullptr)
Generates class data from a raster, for the specified bandNumber.
int inputBand() const override
Returns the input band for the renderer, or -1 if no input band is available.
static QString classDataToString(const QgsPalettedRasterRenderer::ClassData &classes)
Converts classes to a string representation, using the .clr/gdal color table file format.
MultiValueClassData multiValueClasses() const
Returns a map of multi value to classes (colors) used by the renderer.
Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props=QVariantMap()) const override
Used from subclasses to create SLD Rule elements following SLD v1.0 specs.
QgsPalettedRasterRenderer(QgsRasterInterface *input, int bandNumber, const ClassData &classes)
Constructor for QgsPalettedRasterRenderer.
A color ramp consisting of random colors, constrained within component ranges.
The Field class represents a Raster Attribute Table classification entry for a thematic Raster Attrib...
Represents a Raster Attribute Table (RAT).
QList< QgsRasterAttributeTable::MinMaxClass > minMaxClasses(const int classificationColumn=-1) const
Returns the classes for a thematic Raster Attribute Table, classified by classificationColumn,...
bool isValid(QString *errorMessage=nullptr) const
Returns true if the Raster Attribute Table is valid, optionally reporting validity checks results in ...
The RasterBandStats struct is a container for statistics about a single raster band.
double minimumValue
The minimum cell value in the raster band.
double maximumValue
The maximum cell value in the raster band.
Feedback object tailored for raster block reading.
A container for a histogram of a single raster band.
Base class for processing filters like renderers, reprojector, resampler etc.
virtual Qgis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
virtual int xSize() const
Gets raster size.
QgsRasterInterface(QgsRasterInterface *input=nullptr)
Q_DECL_DEPRECATED QgsRasterBandStats bandStatistics(int bandNo, int stats, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
QString displayBandName(int bandNumber) const
Generates a friendly, descriptive name for the specified bandNumber.
QgsRasterInterface * mInput
virtual int ySize() const
virtual QgsRectangle extent() const
Gets the extent of the interface.
virtual QgsRasterInterface * input() const
Current input.
virtual QgsRasterHistogram histogram(int bandNo, int binCount=0, double minimum=std::numeric_limits< double >::quiet_NaN(), double maximum=std::numeric_limits< double >::quiet_NaN(), const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, bool includeOutOfRange=false, QgsRasterBlockFeedback *feedback=nullptr)
Returns a band histogram.
Iterator for sequentially processing raster cells.
static const int DEFAULT_MAXIMUM_TILE_WIDTH
Default maximum tile width.
bool readNextRasterPart(int bandNumber, int &nCols, int &nRows, QgsRasterBlock **block, int &topLeftCol, int &topLeftRow)
Fetches next part of raster data, caller takes ownership of the block and caller should delete the bl...
static const int DEFAULT_MAXIMUM_TILE_HEIGHT
Default maximum tile height.
void startRasterRead(int bandNumber, qgssize nCols, qgssize nRows, const QgsRectangle &extent, QgsRasterBlockFeedback *feedback=nullptr)
Start reading of raster band.
Raster renderer pipe that applies colors to a raster.
QgsRasterRenderer(QgsRasterInterface *input=nullptr, const QString &type=QString())
Constructor for QgsRasterRenderer.
double mOpacity
Global alpha value (0-1).
int mAlphaBand
Read alpha value from band.
QRgb renderColorForNodataPixel() const
Returns the color for the renderer to use to represent nodata pixels.
std::unique_ptr< QgsRasterTransparency > mRasterTransparency
Raster transparency per color or value. Overwrites global alpha value.
void _writeXml(QDomDocument &doc, QDomElement &rasterRendererElem) const
Write upper class info into rasterrenderer element (called by writeXml method of subclasses).
int bandCount() const override
Gets number of bands.
bool usesTransparency() const
virtual Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props=QVariantMap()) const
Used from subclasses to create SLD Rule elements following SLD v1.0 specs.
void readXml(const QDomElement &rendererElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
Implementation of legend node interface for displaying raster legend entries.
A rectangle specified with double values.
Implementation of legend node interface for displaying arbitrary labels with icons.
Holds SLD export options and other information related to SLD export of a QGIS layer style.
void setExtraProperties(const QVariantMap &properties)
Sets the open ended set of properties that can drive/inform the SLD encoding.
A color ramp entity for QgsStyle databases.
An interface for classes which can visit style entity (e.g.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
static std::unique_ptr< QgsColorRamp > loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
static QDomElement saveColorRamp(const QString &name, const QgsColorRamp *ramp, QDomDocument &doc)
Encodes a color ramp's settings to an XML element.
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
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...
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
Properties of a single value class.
Contains information relating to the style entity currently being visited.