QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgspalettedrasterrenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspalettedrasterrenderer.cpp
3 -----------------------------
4 begin : December 2011
5 copyright : (C) 2011 by Marco Hugentobler
6 email : marco at sourcepole dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
20#include "qgsrasterviewport.h"
21#include "qgssymbollayerutils.h"
23#include "qgsmessagelog.h"
24#include "qgsrasteriterator.h"
26#include "qgscolorrampimpl.h"
28
29#include <QColor>
30#include <QDomDocument>
31#include <QDomElement>
32#include <QImage>
33#include <QVector>
34#include <memory>
35#include <set>
36#include <QRegularExpression>
37#include <QTextStream>
38
39const int QgsPalettedRasterRenderer::MAX_FLOAT_CLASSES = 65536;
40
42 : QgsRasterRenderer( input, QStringLiteral( "paletted" ) )
43 , mBand( bandNumber )
44{
45 for ( const Class &klass : std::as_const( classes ) )
46 {
47 MultiValueClassData::iterator it = std::find_if( mMultiValueClassData.begin(), mMultiValueClassData.end(), [&klass]( const MultiValueClass & val ) -> bool
48 {
49 return val.label == klass.label && val.color == klass.color ;
50 } );
51 if ( it != mMultiValueClassData.end() )
52 {
53 it->values.push_back( klass.value );
54 }
55 else
56 {
57 mMultiValueClassData.push_back( MultiValueClass{ { klass.value }, klass.color, klass.label } );
58 }
59 }
60 updateArrays();
61}
62
63QgsPalettedRasterRenderer::QgsPalettedRasterRenderer( QgsRasterInterface *input, int bandNumber, const MultiValueClassData &classes )
64 : QgsRasterRenderer( input, QStringLiteral( "paletted" ) )
65 , mBand( bandNumber )
66 , mMultiValueClassData( classes )
67{
68 updateArrays();
69}
70
72{
73
74 std::unique_ptr< QgsPalettedRasterRenderer > renderer = std::make_unique< QgsPalettedRasterRenderer >( nullptr, mBand, mMultiValueClassData );
75
76 if ( mSourceColorRamp )
77 renderer->setSourceColorRamp( mSourceColorRamp->clone() );
78
79 renderer->copyCommonProperties( this );
80
81 return renderer.release();
82}
83
84Qgis::RasterRendererFlags QgsPalettedRasterRenderer::flags() const
85{
87}
88
90{
91 if ( elem.isNull() )
92 {
93 return nullptr;
94 }
95
96 const int bandNumber = elem.attribute( QStringLiteral( "band" ), QStringLiteral( "-1" ) ).toInt();
97 ClassData classData;
98
99 const QDomElement paletteElem = elem.firstChildElement( QStringLiteral( "colorPalette" ) );
100 if ( !paletteElem.isNull() )
101 {
102 const QDomNodeList paletteEntries = paletteElem.elementsByTagName( QStringLiteral( "paletteEntry" ) );
103
104 QDomElement entryElem;
105 double value;
106
107 for ( int i = 0; i < paletteEntries.size(); ++i )
108 {
109 QColor color;
110 QString label;
111 entryElem = paletteEntries.at( i ).toElement();
112 value = entryElem.attribute( QStringLiteral( "value" ), QStringLiteral( "0" ) ).toDouble();
113 color = QColor( entryElem.attribute( QStringLiteral( "color" ), QStringLiteral( "#000000" ) ) );
114 color.setAlpha( entryElem.attribute( QStringLiteral( "alpha" ), QStringLiteral( "255" ) ).toInt() );
115 label = entryElem.attribute( QStringLiteral( "label" ) );
116 QgsDebugMsgLevel( QStringLiteral( "Value: %1, label: %2, color: %3" ).arg( value ).arg( label, entryElem.attribute( QStringLiteral( "color" ) ) ), 4 );
117 classData << Class( value, color, label );
118 }
119 }
120
121 QgsPalettedRasterRenderer *r = new QgsPalettedRasterRenderer( input, bandNumber, classData );
122 r->readXml( elem );
123
124 // try to load color ramp (optional)
125 QDomElement sourceColorRampElem = elem.firstChildElement( QStringLiteral( "colorramp" ) );
126 if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "[source]" ) )
127 {
128 r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ) );
129 }
130
131 return r;
132}
133
135{
136 return classData();
137}
138
140{
141 return mMultiValueClassData;
142}
143
145{
146 mMultiValueClassData = classes;
147 updateArrays();
148}
149
150QString QgsPalettedRasterRenderer::label( double idx ) const
151{
152 if ( ! mMultiValueClassData.isEmpty() )
153 {
154 const auto constMClassData = mMultiValueClassData;
155 for ( const MultiValueClass &c : std::as_const( constMClassData ) )
156 {
157 if ( c.values.contains( idx ) )
158 return c.label;
159 }
160 }
161
162 return QString();
163}
164
165void QgsPalettedRasterRenderer::setLabel( double idx, const QString &label )
166{
167 MultiValueClassData::iterator cMvIt = mMultiValueClassData.begin();
168 for ( ; cMvIt != mMultiValueClassData.end(); ++cMvIt )
169 {
170 if ( cMvIt->values.contains( idx ) )
171 {
172 cMvIt->label = label;
173 return;
174 }
175 }
176}
177
178QgsRasterBlock *QgsPalettedRasterRenderer::block( int, QgsRectangle const &extent, int width, int height, QgsRasterBlockFeedback *feedback )
179{
180 std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() );
181 if ( !mInput || mMultiValueClassData.isEmpty() )
182 {
183 return outputBlock.release();
184 }
185
186 const std::shared_ptr< QgsRasterBlock > inputBlock( mInput->block( mBand, extent, width, height, feedback ) );
187
188 if ( !inputBlock || inputBlock->isEmpty() )
189 {
190 QgsDebugMsg( QStringLiteral( "No raster data!" ) );
191 return outputBlock.release();
192 }
193
194 //rendering is faster without considering user-defined transparency
195 const bool hasTransparency = usesTransparency();
196
197 std::shared_ptr< QgsRasterBlock > alphaBlock;
198
199 if ( mAlphaBand > 0 && mAlphaBand != mBand )
200 {
201 alphaBlock.reset( mInput->block( mAlphaBand, extent, width, height, feedback ) );
202 if ( !alphaBlock || alphaBlock->isEmpty() )
203 {
204 return outputBlock.release();
205 }
206 }
207 else if ( mAlphaBand == mBand )
208 {
209 alphaBlock = inputBlock;
210 }
211
212 if ( !outputBlock->reset( Qgis::DataType::ARGB32_Premultiplied, width, height ) )
213 {
214 return outputBlock.release();
215 }
216
217 const QRgb myDefaultColor = renderColorForNodataPixel();
218
219 //use direct data access instead of QgsRasterBlock::setValue
220 //because of performance
221 Q_ASSERT( outputBlock ); // to make cppcheck happy
222 unsigned int *outputData = ( unsigned int * )( outputBlock->bits() );
223
224 const qgssize rasterSize = ( qgssize )width * height;
225 bool isNoData = false;
226 for ( qgssize i = 0; i < rasterSize; ++i )
227 {
228 const double value = inputBlock->valueAndNoData( i, isNoData );
229 if ( isNoData )
230 {
231 outputData[i] = myDefaultColor;
232 continue;
233 }
234 if ( !mColors.contains( value ) )
235 {
236 outputData[i] = myDefaultColor;
237 continue;
238 }
239
240 if ( !hasTransparency )
241 {
242 outputData[i] = mColors.value( value );
243 }
244 else
245 {
246 double currentOpacity = mOpacity;
248 {
249 currentOpacity = mRasterTransparency->alphaValue( value, mOpacity * 255 ) / 255.0;
250 }
251 if ( mAlphaBand > 0 )
252 {
253 const double alpha = alphaBlock->value( i );
254 if ( alpha == 0 )
255 {
256 outputBlock->setColor( i, myDefaultColor );
257 continue;
258 }
259 else
260 {
261 currentOpacity *= alpha / 255.0;
262 }
263 }
264
265 const QRgb c = mColors.value( value );
266 outputData[i] = qRgba( currentOpacity * qRed( c ), currentOpacity * qGreen( c ), currentOpacity * qBlue( c ), currentOpacity * qAlpha( c ) );
267 }
268 }
269
270 return outputBlock.release();
271}
272
274{
275 return mMultiValueClassData.size();
276}
277
278void QgsPalettedRasterRenderer::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
279{
280 if ( parentElem.isNull() )
281 {
282 return;
283 }
284
285 QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterrenderer" ) );
286 _writeXml( doc, rasterRendererElem );
287
288 rasterRendererElem.setAttribute( QStringLiteral( "band" ), mBand );
289 QDomElement colorPaletteElem = doc.createElement( QStringLiteral( "colorPalette" ) );
290 const ClassData klassData { classData() };
291 ClassData::const_iterator it = klassData.constBegin();
292 for ( ; it != klassData.constEnd(); ++it )
293 {
294 const QColor color = it->color;
295 QDomElement colorElem = doc.createElement( QStringLiteral( "paletteEntry" ) );
296 colorElem.setAttribute( QStringLiteral( "value" ), it->value );
297 colorElem.setAttribute( QStringLiteral( "color" ), color.name() );
298 colorElem.setAttribute( QStringLiteral( "alpha" ), color.alpha() );
299 if ( !it->label.isEmpty() )
300 {
301 colorElem.setAttribute( QStringLiteral( "label" ), it->label );
302 }
303 colorPaletteElem.appendChild( colorElem );
304 }
305 rasterRendererElem.appendChild( colorPaletteElem );
306
307 // save source color ramp
308 if ( mSourceColorRamp )
309 {
310 const QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( QStringLiteral( "[source]" ), mSourceColorRamp.get(), doc );
311 rasterRendererElem.appendChild( colorRampElem );
312 }
313
314 parentElem.appendChild( rasterRendererElem );
315}
316
317void QgsPalettedRasterRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
318{
319 // create base structure
320 QgsRasterRenderer::toSld( doc, element, props );
321
322 // look for RasterSymbolizer tag
323 const QDomNodeList elements = element.elementsByTagName( QStringLiteral( "sld:RasterSymbolizer" ) );
324 if ( elements.size() == 0 )
325 return;
326
327 // there SHOULD be only one
328 QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
329
330 // add Channel Selection tags
331 QDomElement channelSelectionElem = doc.createElement( QStringLiteral( "sld:ChannelSelection" ) );
332 rasterSymbolizerElem.appendChild( channelSelectionElem );
333
334 // for the mapped band
335 QDomElement channelElem = doc.createElement( QStringLiteral( "sld:GrayChannel" ) );
336 channelSelectionElem.appendChild( channelElem );
337
338 // set band
339 QDomElement sourceChannelNameElem = doc.createElement( QStringLiteral( "sld:SourceChannelName" ) );
340 sourceChannelNameElem.appendChild( doc.createTextNode( QString::number( band() ) ) );
341 channelElem.appendChild( sourceChannelNameElem );
342
343 // add ColorMap tag
344 QDomElement colorMapElem = doc.createElement( QStringLiteral( "sld:ColorMap" ) );
345 colorMapElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "values" ) );
346 if ( this->classes().size() >= 255 )
347 colorMapElem.setAttribute( QStringLiteral( "extended" ), QStringLiteral( "true" ) );
348 rasterSymbolizerElem.appendChild( colorMapElem );
349
350 // for each color set a ColorMapEntry tag nested into "sld:ColorMap" tag
351 // e.g. <ColorMapEntry color="#EEBE2F" quantity="-300" label="label" opacity="0"/>
352 const QList<QgsPalettedRasterRenderer::Class> classes = this->classes();
353 QList<QgsPalettedRasterRenderer::Class>::const_iterator classDataIt = classes.constBegin();
354 for ( ; classDataIt != classes.constEnd(); ++classDataIt )
355 {
356 QDomElement colorMapEntryElem = doc.createElement( QStringLiteral( "sld:ColorMapEntry" ) );
357 colorMapElem.appendChild( colorMapEntryElem );
358
359 // set colorMapEntryElem attributes
360 colorMapEntryElem.setAttribute( QStringLiteral( "color" ), classDataIt->color.name() );
361 colorMapEntryElem.setAttribute( QStringLiteral( "quantity" ), QString::number( classDataIt->value ) );
362 colorMapEntryElem.setAttribute( QStringLiteral( "label" ), classDataIt->label );
363 if ( classDataIt->color.alphaF() != 1.0 )
364 {
365 colorMapEntryElem.setAttribute( QStringLiteral( "opacity" ), QString::number( classDataIt->color.alphaF() ) );
366 }
367 }
368}
369
371{
372 if ( mSourceColorRamp )
373 {
374 QgsStyleColorRampEntity entity( mSourceColorRamp.get() );
375 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
376 return false;
377 }
378
379 return true;
380}
381
382QList< QPair< QString, QColor > > QgsPalettedRasterRenderer::legendSymbologyItems() const
383{
384 QList< QPair< QString, QColor > > symbolItems;
385 for ( const QgsPalettedRasterRenderer::MultiValueClass &classData : mMultiValueClassData )
386 {
387 QString lab { classData.label };
388 if ( lab.isEmpty() )
389 {
390 QStringList values;
391 for ( const QVariant &val : std::as_const( classData.values ) )
392 {
393 // Be tolerant here: if we can convert it to double use locale, if not just pass through.
394 bool ok;
395 const double numericValue { val.toDouble( &ok ) };
396 if ( ok )
397 {
398 values.push_back( QLocale().toString( numericValue ) );
399 }
400 else
401 {
402 values.push_back( val.toString() );
403 }
404 }
405 lab = values.join( QChar( ' ' ) );
406 }
407 symbolItems << qMakePair( lab, classData.color );
408 }
409 return symbolItems;
410}
411
412
413QList<QgsLayerTreeModelLegendNode *> QgsPalettedRasterRenderer::createLegendNodes( QgsLayerTreeLayer *nodeLayer )
414{
415 QList<QgsLayerTreeModelLegendNode *> res;
416
417 const QString name = displayBandName( mBand );
418 if ( !name.isEmpty() )
419 {
420 res << new QgsSimpleLegendNode( nodeLayer, name );
421 }
422
423 const QList< QPair< QString, QColor > > items = legendSymbologyItems();
424 res.reserve( res.size() + items.size() );
425 for ( const QPair< QString, QColor > &item : items )
426 {
427 res << new QgsRasterSymbolLegendNode( nodeLayer, item.second, item.first );
428 }
429
430 return res;
431}
432
433
435{
436 QList<int> bandList;
437 if ( mBand != -1 )
438 {
439 bandList << mBand;
440 }
441 return bandList;
442}
443
445{
446 mSourceColorRamp.reset( ramp );
447}
448
450{
451 return mSourceColorRamp.get();
452}
453
455{
456 QList<QgsColorRampShader::ColorRampItem>::const_iterator colorIt = table.constBegin();
458 for ( ; colorIt != table.constEnd(); ++colorIt )
459 {
460 classes << QgsPalettedRasterRenderer::Class( colorIt->value, colorIt->color, colorIt->label );
461 }
462 return classes;
463}
464
466{
467 if ( ! attributeTable || ! attributeTable->isValid() )
468 {
470 }
471
473
474 const QList<QgsRasterAttributeTable::MinMaxClass> minMaxClasses { attributeTable->minMaxClasses( classificationColumn ) };
475 if ( minMaxClasses.empty() )
477
478 for ( const QgsRasterAttributeTable::MinMaxClass &minMaxClass : std::as_const( minMaxClasses ) )
479 {
480 QVector<QVariant> values;
481 for ( const double val : std::as_const( minMaxClass.minMaxValues ) )
482 {
483 values.push_back( QVariant( val ) );
484 }
485 classData.push_back( { values, minMaxClass.color, minMaxClass.name } );
486 }
487
488 int numClasses { static_cast<int>( classData.count( ) ) };
489
490 // assign colors from ramp
491 if ( ramp && numClasses > 0 )
492 {
493 int i = 0;
494
495 if ( QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp ) )
496 {
497 //ramp is a random colors ramp, so inform it of the total number of required colors
498 //this allows the ramp to pregenerate a set of visually distinctive colors
499 randomRamp->setTotalColorCount( numClasses );
500 }
501
502 if ( numClasses > 1 )
503 numClasses -= 1; //avoid duplicate first color
504
505 QgsPalettedRasterRenderer::MultiValueClassData::iterator cIt = classData.begin();
506 for ( ; cIt != classData.end(); ++cIt )
507 {
508 cIt->color = ramp->color( i / static_cast<double>( numClasses ) );
509 i++;
510 }
511 }
512
513 return classData;
514}
515
517{
519
520 const QRegularExpression linePartRx( QStringLiteral( "[\\s,:]+" ) );
521
522#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
523 const QStringList parts = string.split( '\n', QString::SkipEmptyParts );
524#else
525 const QStringList parts = string.split( '\n', Qt::SkipEmptyParts );
526#endif
527 for ( const QString &part : parts )
528 {
529#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
530 const QStringList lineParts = part.split( linePartRx, QString::SkipEmptyParts );
531#else
532 const QStringList lineParts = part.split( linePartRx, Qt::SkipEmptyParts );
533#endif
534 bool ok = false;
535 switch ( lineParts.count() )
536 {
537 case 1:
538 {
539 const int value = lineParts.at( 0 ).toInt( &ok );
540 if ( !ok )
541 continue;
542
543 classes << Class( value );
544 break;
545 }
546
547 case 2:
548 {
549 const int value = lineParts.at( 0 ).toInt( &ok );
550 if ( !ok )
551 continue;
552
553 const QColor c( lineParts.at( 1 ) );
554
555 classes << Class( value, c );
556 break;
557 }
558
559 default:
560 {
561 if ( lineParts.count() < 4 )
562 continue;
563
564 const int value = lineParts.at( 0 ).toInt( &ok );
565 if ( !ok )
566 continue;
567
568 bool rOk = false;
569 const double r = lineParts.at( 1 ).toDouble( &rOk );
570 bool gOk = false;
571 const double g = lineParts.at( 2 ).toDouble( &gOk );
572 bool bOk = false;
573 const double b = lineParts.at( 3 ).toDouble( &bOk );
574
575 QColor c;
576 if ( rOk && gOk && bOk )
577 {
578 c = QColor( r, g, b );
579 }
580
581 if ( lineParts.count() >= 5 )
582 {
583 const double alpha = lineParts.at( 4 ).toDouble( &ok );
584 if ( ok )
585 c.setAlpha( alpha );
586 }
587
588 QString label;
589 if ( lineParts.count() > 5 )
590 {
591 label = lineParts.mid( 5 ).join( ' ' );
592 }
593
594 classes << Class( value, c, label );
595 break;
596 }
597 }
598
599 }
600 return classes;
601}
602
604{
605 QFile inputFile( path );
606 QString input;
607 if ( inputFile.open( QIODevice::ReadOnly ) )
608 {
609 QTextStream in( &inputFile );
610 input = in.readAll();
611 inputFile.close();
612 }
613 return classDataFromString( input );
614}
615
617{
618 QStringList out;
619 // must be sorted
621 std::sort( cd.begin(), cd.end(), []( const Class & a, const Class & b ) -> bool
622 {
623 return a.value < b.value;
624 } );
625
626 const auto constCd = cd;
627 for ( const Class &c : constCd )
628 {
629 out << QStringLiteral( "%1 %2 %3 %4 %5 %6" ).arg( c.value ).arg( c.color.red() )
630 .arg( c.color.green() ).arg( c.color.blue() ).arg( c.color.alpha() ).arg( c.label );
631 }
632 return out.join( '\n' );
633}
634
636{
637 if ( !raster )
638 return ClassData();
639
640 ClassData data;
641
642 if ( bandNumber > 0 && bandNumber <= raster->bandCount() )
643 {
644 qlonglong numClasses = 0;
645
646 if ( feedback )
647 feedback->setProgress( 0 );
648
649 // Collect unique values for float rasters
650 if ( raster->dataType( bandNumber ) == Qgis::DataType::Float32 || raster->dataType( bandNumber ) == Qgis::DataType::Float64 )
651 {
652
653 if ( feedback && feedback->isCanceled() )
654 {
655 return data;
656 }
657
658 std::set<double> values;
659
662
663 QgsRasterIterator iter( raster );
664 iter.startRasterRead( bandNumber, raster->xSize(), raster->ySize(), raster->extent(), feedback );
665
666 const int nbBlocksWidth = static_cast< int >( std::ceil( 1.0 * raster->xSize() / maxWidth ) );
667 const int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * raster->ySize() / maxHeight ) );
668 const int nbBlocks = nbBlocksWidth * nbBlocksHeight;
669
670 int iterLeft = 0;
671 int iterTop = 0;
672 int iterCols = 0;
673 int iterRows = 0;
674 std::unique_ptr< QgsRasterBlock > rasterBlock;
675 QgsRectangle blockExtent;
676 bool isNoData = false;
677 while ( iter.readNextRasterPart( bandNumber, iterCols, iterRows, rasterBlock, iterLeft, iterTop, &blockExtent ) )
678 {
679 if ( feedback )
680 feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
681
682 if ( feedback && feedback->isCanceled() )
683 break;
684
685 for ( int row = 0; row < iterRows; row++ )
686 {
687 if ( feedback && feedback->isCanceled() )
688 break;
689
690 for ( int column = 0; column < iterCols; column++ )
691 {
692 if ( feedback && feedback->isCanceled() )
693 break;
694
695 const double currentValue = rasterBlock->valueAndNoData( row, column, isNoData );
696 if ( numClasses >= MAX_FLOAT_CLASSES )
697 {
698 QgsMessageLog::logMessage( QStringLiteral( "Number of classes exceeded maximum (%1)." ).arg( MAX_FLOAT_CLASSES ), QStringLiteral( "Raster" ) );
699 break;
700 }
701 if ( !isNoData && values.find( currentValue ) == values.end() )
702 {
703 values.insert( currentValue );
704 data.push_back( Class( currentValue, QColor(), QLocale().toString( currentValue ) ) );
705 numClasses++;
706 }
707 }
708 }
709 }
710 // must be sorted
711 std::sort( data.begin(), data.end(), []( const Class & a, const Class & b ) -> bool
712 {
713 return a.value < b.value;
714 } );
715 }
716 else
717 {
718 // get min and max value from raster
719 const QgsRasterBandStats stats = raster->bandStatistics( bandNumber, QgsRasterBandStats::Min | QgsRasterBandStats::Max, QgsRectangle(), 0, feedback );
720 if ( feedback && feedback->isCanceled() )
721 return ClassData();
722
723 const double min = stats.minimumValue;
724 const double max = stats.maximumValue;
725 // need count of every individual value
726 const int bins = std::ceil( max - min ) + 1;
727 if ( bins <= 0 )
728 return ClassData();
729
730 const QgsRasterHistogram histogram = raster->histogram( bandNumber, bins, min, max, QgsRectangle(), 0, false, feedback );
731 if ( feedback && feedback->isCanceled() )
732 return ClassData();
733
734 const double interval = ( histogram.maximum - histogram.minimum + 1 ) / histogram.binCount;
735 double currentValue = histogram.minimum;
736
737 if ( histogram.valid )
738 {
739 for ( int idx = 0; idx < histogram.binCount; ++idx )
740 {
741 const int count = histogram.histogramVector.at( idx );
742 if ( count > 0 )
743 {
744 data << Class( currentValue, QColor(), QLocale().toString( currentValue ) );
745 numClasses++;
746 }
747 currentValue += interval;
748 }
749 }
750 else if ( histogram.maximum == histogram.minimum && histogram.binCount == 1 ) // Constant raster
751 {
752 data << Class( histogram.maximum, QColor(), QLocale().toString( histogram.maximum ) );
753 }
754
755 }
756
757 // assign colors from ramp
758 if ( ramp && numClasses > 0 )
759 {
760 int i = 0;
761
762 if ( QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp ) )
763 {
764 //ramp is a random colors ramp, so inform it of the total number of required colors
765 //this allows the ramp to pregenerate a set of visually distinctive colors
766 randomRamp->setTotalColorCount( data.count() );
767 }
768
769 if ( numClasses > 1 )
770 numClasses -= 1; //avoid duplicate first color
771
772 QgsPalettedRasterRenderer::ClassData::iterator cIt = data.begin();
773 for ( ; cIt != data.end(); ++cIt )
774 {
775 if ( feedback )
776 {
777 // Show no less than 1%, then the max between class fill and real progress
778 feedback->setProgress( std::max<int>( 1, 100 * ( i + 1 ) / numClasses ) );
779 }
780 cIt->color = ramp->color( i / static_cast<double>( numClasses ) );
781 i++;
782 }
783 }
784 }
785 return data;
786}
787
788QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classData() const
789{
791 for ( const MultiValueClass &klass : std::as_const( mMultiValueClassData ) )
792 {
793 for ( const QVariant &entry : std::as_const( klass.values ) )
794 {
795 bool ok;
796 const double value { entry.toDouble( &ok )};
797 if ( ok )
798 {
799 data.push_back( { value, klass.color, klass.label } );
800 }
801 else
802 {
803 QgsDebugMsgLevel( QStringLiteral( "Could not convert class value '%1' to double when creating classes." ).arg( entry.toString() ), 2 );
804 }
805 }
806 }
807 return data;
808}
809
810void QgsPalettedRasterRenderer::updateArrays()
811{
812 mColors.clear();
813
814 MultiValueClassData::const_iterator it = mMultiValueClassData.constBegin();
815 for ( ; it != mMultiValueClassData.constEnd(); ++it )
816 {
817 for ( const QVariant &entry : std::as_const( it->values ) )
818 {
819 bool ok;
820 const double value { entry.toDouble( &ok )};
821 if ( ok )
822 {
823 mColors[value] = qPremultiply( it->color.rgba() );
824 }
825 else
826 {
827 QgsDebugMsgLevel( QStringLiteral( "Could not convert class value '%1' to double for color lookup." ).arg( entry.toString() ), 2 );
828 }
829 }
830 }
831}
832
834{
835 return true;
836}
837
838QgsPalettedRasterRenderer::MultiValueClass::MultiValueClass( const QVector< QVariant > &values, const QColor &color, const QString &label )
839 : values( values )
840 , color( color )
841 , label( label )
842{}
@ 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.
Definition: qgscolorramp.h:30
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.
Definition: qgsfeedback.h:54
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
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).
Properties of a multi value class: a class that contains multiple values.
MultiValueClass(const QVector< QVariant > &values, const QColor &color=QColor(), const QString &label=QString())
Constructor for MultiValueClass from a list of values.
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.
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.
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.
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.
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.
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 Field class represents a Raster Attribute Table classification entry for a thematic Raster Attrib...
The QgsRasterAttributeTable class 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.
Raster data container.
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.
bool valid
Histogram is valid.
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 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 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.
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
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.
Definition: qgsrectangle.h:42
Implementation of legend node interface for displaying arbitrary label with icon.
A color ramp entity for QgsStyle databases.
Definition: qgsstyle.h:1373
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...
Definition: qgis.h:4064
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Properties of a single value class.
Contains information relating to the style entity currently being visited.