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