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