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