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