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