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