28 #include <QDomDocument>
29 #include <QDomElement>
34 #include <QRegularExpression>
35 #include <QTextStream>
37 const int QgsPalettedRasterRenderer::MAX_FLOAT_CLASSES = 65536;
42 , mClassData( classes )
50 if ( mSourceColorRamp )
64 const int bandNumber = elem.attribute( QStringLiteral(
"band" ), QStringLiteral(
"-1" ) ).toInt();
67 const QDomElement paletteElem = elem.firstChildElement( QStringLiteral(
"colorPalette" ) );
68 if ( !paletteElem.isNull() )
70 const QDomNodeList paletteEntries = paletteElem.elementsByTagName( QStringLiteral(
"paletteEntry" ) );
72 QDomElement entryElem;
75 for (
int i = 0; i < paletteEntries.size(); ++i )
79 entryElem = paletteEntries.at( i ).toElement();
80 value = entryElem.attribute( QStringLiteral(
"value" ), QStringLiteral(
"0" ) ).toDouble();
81 color = QColor( entryElem.attribute( QStringLiteral(
"color" ), QStringLiteral(
"#000000" ) ) );
82 color.setAlpha( entryElem.attribute( QStringLiteral(
"alpha" ), QStringLiteral(
"255" ) ).toInt() );
83 label = entryElem.attribute( QStringLiteral(
"label" ) );
84 QgsDebugMsgLevel( QStringLiteral(
"Value: %1, label: %2, color: %3" ).arg( value ).arg(
label, entryElem.attribute( QStringLiteral(
"color" ) ) ), 4 );
93 QDomElement sourceColorRampElem = elem.firstChildElement( QStringLiteral(
"colorramp" ) );
94 if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( QStringLiteral(
"name" ) ) == QLatin1String(
"[source]" ) )
109 const auto constMClassData = mClassData;
110 for (
const Class &
c : constMClassData )
112 if (
c.value == idx )
121 ClassData::iterator cIt = mClassData.begin();
122 for ( ; cIt != mClassData.end(); ++cIt )
124 if ( cIt->value == idx )
134 std::unique_ptr< QgsRasterBlock > outputBlock(
new QgsRasterBlock() );
135 if ( !
mInput || mClassData.isEmpty() )
137 return outputBlock.release();
140 const std::shared_ptr< QgsRasterBlock > inputBlock(
mInput->
block( mBand,
extent, width, height, feedback ) );
142 if ( !inputBlock || inputBlock->isEmpty() )
144 QgsDebugMsg( QStringLiteral(
"No raster data!" ) );
145 return outputBlock.release();
153 std::shared_ptr< QgsRasterBlock > alphaBlock;
158 if ( !alphaBlock || alphaBlock->isEmpty() )
160 return outputBlock.release();
165 alphaBlock = inputBlock;
170 return outputBlock.release();
177 Q_ASSERT( outputBlock );
178 unsigned int *outputData = (
unsigned int * )( outputBlock->bits() );
181 bool isNoData =
false;
182 for (
qgssize i = 0; i < rasterSize; ++i )
184 const double value = inputBlock->valueAndNoData( i, isNoData );
187 outputData[i] = myDefaultColor;
190 if ( !mColors.contains( value ) )
192 outputData[i] = myDefaultColor;
196 if ( !hasTransparency )
198 outputData[i] = mColors.value( value );
209 currentOpacity *= alphaBlock->value( i ) / 255.0;
212 const QRgb
c = mColors.value( value );
213 outputData[i] = qRgba( currentOpacity * qRed(
c ), currentOpacity * qGreen(
c ), currentOpacity * qBlue(
c ), currentOpacity * qAlpha(
c ) );
217 return outputBlock.release();
222 if ( parentElem.isNull() )
227 QDomElement rasterRendererElem = doc.createElement( QStringLiteral(
"rasterrenderer" ) );
230 rasterRendererElem.setAttribute( QStringLiteral(
"band" ), mBand );
231 QDomElement colorPaletteElem = doc.createElement( QStringLiteral(
"colorPalette" ) );
232 ClassData::const_iterator it = mClassData.constBegin();
233 for ( ; it != mClassData.constEnd(); ++it )
235 const QColor color = it->color;
236 QDomElement colorElem = doc.createElement( QStringLiteral(
"paletteEntry" ) );
237 colorElem.setAttribute( QStringLiteral(
"value" ), it->value );
238 colorElem.setAttribute( QStringLiteral(
"color" ), color.name() );
239 colorElem.setAttribute( QStringLiteral(
"alpha" ), color.alpha() );
240 if ( !it->label.isEmpty() )
242 colorElem.setAttribute( QStringLiteral(
"label" ), it->label );
244 colorPaletteElem.appendChild( colorElem );
246 rasterRendererElem.appendChild( colorPaletteElem );
249 if ( mSourceColorRamp )
252 rasterRendererElem.appendChild( colorRampElem );
255 parentElem.appendChild( rasterRendererElem );
264 const QDomNodeList elements = element.elementsByTagName( QStringLiteral(
"sld:RasterSymbolizer" ) );
265 if ( elements.size() == 0 )
269 QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
272 QDomElement channelSelectionElem = doc.createElement( QStringLiteral(
"sld:ChannelSelection" ) );
273 rasterSymbolizerElem.appendChild( channelSelectionElem );
276 QDomElement channelElem = doc.createElement( QStringLiteral(
"sld:GrayChannel" ) );
277 channelSelectionElem.appendChild( channelElem );
280 QDomElement sourceChannelNameElem = doc.createElement( QStringLiteral(
"sld:SourceChannelName" ) );
281 sourceChannelNameElem.appendChild( doc.createTextNode( QString::number(
band() ) ) );
282 channelElem.appendChild( sourceChannelNameElem );
285 QDomElement colorMapElem = doc.createElement( QStringLiteral(
"sld:ColorMap" ) );
286 colorMapElem.setAttribute( QStringLiteral(
"type" ), QStringLiteral(
"values" ) );
287 if ( this->
classes().size() >= 255 )
288 colorMapElem.setAttribute( QStringLiteral(
"extended" ), QStringLiteral(
"true" ) );
289 rasterSymbolizerElem.appendChild( colorMapElem );
293 const QList<QgsPalettedRasterRenderer::Class>
classes = this->
classes();
294 QList<QgsPalettedRasterRenderer::Class>::const_iterator classDataIt =
classes.constBegin();
295 for ( ; classDataIt !=
classes.constEnd(); ++classDataIt )
297 QDomElement colorMapEntryElem = doc.createElement( QStringLiteral(
"sld:ColorMapEntry" ) );
298 colorMapElem.appendChild( colorMapEntryElem );
301 colorMapEntryElem.setAttribute( QStringLiteral(
"color" ), classDataIt->color.name() );
302 colorMapEntryElem.setAttribute( QStringLiteral(
"quantity" ), QString::number( classDataIt->value ) );
303 colorMapEntryElem.setAttribute( QStringLiteral(
"label" ), classDataIt->label );
304 if ( classDataIt->color.alphaF() != 1.0 )
306 colorMapEntryElem.setAttribute( QStringLiteral(
"opacity" ), QString::number( classDataIt->color.alphaF() ) );
313 if ( mSourceColorRamp )
325 QList< QPair< QString, QColor > > symbolItems;
328 const QString lab = classData.label.isEmpty() ? QString::number( classData.value ) : classData.label;
329 symbolItems << qMakePair( lab, classData.color );
337 QList<QgsLayerTreeModelLegendNode *> res;
340 if ( !name.isEmpty() )
346 res.reserve( res.size() + items.size() );
347 for (
const QPair< QString, QColor > &item : items )
368 mSourceColorRamp.reset( ramp );
373 return mSourceColorRamp.get();
378 QList<QgsColorRampShader::ColorRampItem>::const_iterator colorIt = table.constBegin();
380 for ( ; colorIt != table.constEnd(); ++colorIt )
391 const QRegularExpression linePartRx( QStringLiteral(
"[\\s,:]+" ) );
393 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
394 const QStringList parts =
string.split(
'\n', QString::SkipEmptyParts );
396 const QStringList parts =
string.split(
'\n', Qt::SkipEmptyParts );
398 for (
const QString &part : parts )
400 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
401 const QStringList lineParts = part.split( linePartRx, QString::SkipEmptyParts );
403 const QStringList lineParts = part.split( linePartRx, Qt::SkipEmptyParts );
406 switch ( lineParts.count() )
410 const int value = lineParts.at( 0 ).toInt( &ok );
420 const int value = lineParts.at( 0 ).toInt( &ok );
424 const QColor
c( lineParts.at( 1 ) );
432 if ( lineParts.count() < 4 )
435 const int value = lineParts.at( 0 ).toInt( &ok );
440 const double r = lineParts.at( 1 ).toDouble( &rOk );
442 const double g = lineParts.at( 2 ).toDouble( &gOk );
444 const double b = lineParts.at( 3 ).toDouble( &bOk );
447 if ( rOk && gOk && bOk )
449 c = QColor( r, g, b );
452 if ( lineParts.count() >= 5 )
454 const double alpha = lineParts.at( 4 ).toDouble( &ok );
460 if ( lineParts.count() > 5 )
462 label = lineParts.mid( 5 ).join(
' ' );
476 QFile inputFile( path );
478 if ( inputFile.open( QIODevice::ReadOnly ) )
480 QTextStream in( &inputFile );
481 input = in.readAll();
492 std::sort( cd.begin(), cd.end(), [](
const Class & a,
const Class & b ) ->
bool
494 return a.value < b.value;
497 const auto constCd = cd;
498 for (
const Class &
c : constCd )
500 out << QStringLiteral(
"%1 %2 %3 %4 %5 %6" ).arg(
c.value ).arg(
c.color.red() )
501 .arg(
c.color.green() ).arg(
c.color.blue() ).arg(
c.color.alpha() ).arg(
c.label );
503 return out.join(
'\n' );
513 if ( bandNumber > 0 && bandNumber <= raster->
bandCount() )
515 qlonglong numClasses = 0;
529 std::set<double> values;
537 const int nbBlocksWidth =
static_cast< int >( std::ceil( 1.0 * raster->
xSize() / maxWidth ) );
538 const int nbBlocksHeight =
static_cast< int >( std::ceil( 1.0 * raster->
ySize() / maxHeight ) );
539 const int nbBlocks = nbBlocksWidth * nbBlocksHeight;
545 std::unique_ptr< QgsRasterBlock > rasterBlock;
547 bool isNoData =
false;
548 while ( iter.
readNextRasterPart( bandNumber, iterCols, iterRows, rasterBlock, iterLeft, iterTop, &blockExtent ) )
551 feedback->
setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
556 for (
int row = 0; row < iterRows; row++ )
561 for (
int column = 0; column < iterCols; column++ )
566 const double currentValue = rasterBlock->valueAndNoData( row, column, isNoData );
567 if ( numClasses >= MAX_FLOAT_CLASSES )
569 QgsMessageLog::logMessage( QStringLiteral(
"Number of classes exceeded maximum (%1)." ).arg( MAX_FLOAT_CLASSES ), QStringLiteral(
"Raster" ) );
572 if ( !isNoData && values.find( currentValue ) == values.end() )
574 values.insert( currentValue );
575 data.push_back(
Class( currentValue, QColor(), QLocale().toString( currentValue ) ) );
582 std::sort( data.begin(), data.end(), [](
const Class & a,
const Class & b ) ->
bool
584 return a.value < b.value;
597 const int bins = std::ceil( max - min ) + 1;
612 data <<
Class( currentValue, QColor(), QLocale().toString( currentValue ) );
615 currentValue += interval;
620 if ( ramp && numClasses > 0 )
628 randomRamp->setTotalColorCount( data.count() );
631 if ( numClasses > 1 )
634 QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin();
635 for ( ; cIt != data.end(); ++cIt )
640 feedback->
setProgress( std::max<int>( 1, 100 * ( i + 1 ) / numClasses ) );
642 cIt->color = ramp->
color( i /
static_cast<double>( numClasses ) );
650 void QgsPalettedRasterRenderer::updateArrays()
653 ClassData::const_iterator it = mClassData.constBegin();
654 for ( ; it != mClassData.constEnd(); ++it )
656 mColors[it->value] = qPremultiply( it->color.rgba() );
@ 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 SIP_HOLDGIL
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)
Adds a message to the log instance (and creates it if necessary).
Renderer for paletted raster images.
int band() const
Returns the raster band used for rendering the raster.
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.
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.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
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...
static QgsPalettedRasterRenderer::ClassData classDataFromFile(const QString &path)
Opens a color table file and returns corresponding paletted renderer class data.
static QgsRasterRenderer * create(const QDomElement &elem, QgsRasterInterface *input)
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.
static QString classDataToString(const QgsPalettedRasterRenderer::ClassData &classes)
Converts classes to a string representation, using the .clr/gdal color table file format.
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.
Totally random color ramp.
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.
The QgsRasterHistogram is a container for histogram of a single raster band.
double minimum
The minimum histogram value.
double maximum
The maximum histogram value.
QgsRasterHistogram::HistogramVector histogramVector
Stores the histogram for a given layer.
int binCount
Number of bins (intervals,buckets) in histogram.
Base class for processing filters like renderers, reprojector, resampler etc.
virtual QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr)=0
Read block of data using given extent and size.
virtual Qgis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
virtual QgsRasterInterface * input() const
Current input.
virtual int xSize() const
Gets raster size.
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, 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 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.
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.
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.
QgsRasterTransparency * mRasterTransparency
Raster transparency per color or value. Overwrites global alpha value.
bool usesTransparency() const
void copyCommonProperties(const QgsRasterRenderer *other, bool copyMinMaxOrigin=true)
Copies common properties like opacity / transparency data from other renderer.
virtual 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.
int alphaValue(double value, int globalTransparency=255) const
Returns the transparency value for a single value pixel.
A rectangle specified with double values.
Implementation of legend node interface for displaying arbitrary label with icon.
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 QgsColorRamp * loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
static QDomElement saveColorRamp(const QString &name, 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)
Properties of a single value class.
Contains information relating to the style entity currently being visited.