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