QGIS API Documentation 4.3.0-Master (0cfde48c85b)
Loading...
Searching...
No Matches
qgsgraduatedsymbolrenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgraduatedsymbolrenderer.cpp
3 ---------------------
4 begin : November 2009
5 copyright : (C) 2009 by Martin Dobias
6 email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include <cmath>
19#include <ctime>
20#include <memory>
21
22#include "qgsapplication.h"
23#include "qgsattributes.h"
29#include "qgscolorramp.h"
30#include "qgscolorrampimpl.h"
32#include "qgsexpression.h"
34#include "qgsfeature.h"
36#include "qgslinesymbol.h"
37#include "qgsmarkersymbol.h"
38#include "qgspainteffect.h"
40#include "qgsproperty.h"
41#include "qgssldexportcontext.h"
43#include "qgssymbol.h"
44#include "qgssymbollayer.h"
45#include "qgssymbollayerutils.h"
47#include "qgsvectorlayer.h"
48#include "qgsvectorlayerutils.h"
49
50#include <QDomDocument>
51#include <QDomElement>
52#include <QString>
53
54using namespace Qt::StringLiterals;
55
57 : QgsFeatureRenderer( u"graduatedSymbol"_s )
58 , mAttrName( attrName )
59{
60 // TODO: check ranges for sanity (NULL symbols, invalid ranges)
61
62 //important - we need a deep copy of the ranges list, not a shared copy. This is required because
63 //QgsRendererRange::symbol() is marked const, and so retrieving the symbol via this method does not
64 //trigger a detachment and copy of mRanges BUT that same method CAN be used to modify a symbol in place
65 const auto constRanges = ranges;
66 for ( const QgsRendererRange &range : constRanges )
67 {
68 mRanges << range;
69 }
70
71 mClassificationMethod = std::make_shared<QgsClassificationCustom>();
72}
73
75{
76 mRanges.clear(); // should delete all the symbols
77}
78
80{
82 auto catIt = mRanges.constBegin();
83 for ( ; catIt != mRanges.constEnd(); ++catIt )
84 {
85 if ( QgsSymbol *catSymbol = catIt->symbol() )
86 {
87 if ( catSymbol->flags().testFlag( Qgis::SymbolFlag::AffectsLabeling ) )
89 }
90 }
91
92 return res;
93}
94
96{
97 for ( const QgsRendererRange &range : mRanges )
98 {
99 if ( range.lowerValue() <= value && range.upperValue() >= value )
100 {
101 if ( range.renderState() || mCounting )
102 return &range;
103 else
104 return nullptr;
105 }
106 }
107
108 // second chance -- use a bit of double tolerance to avoid floating point equality fuzziness
109 // if a value falls just outside of a range, but within acceptable double precision tolerance
110 // then we accept it anyway
111 for ( const QgsRendererRange &range : mRanges )
112 {
113 if ( qgsDoubleNear( range.lowerValue(), value ) || qgsDoubleNear( range.upperValue(), value ) )
114 {
115 if ( range.renderState() || mCounting )
116 return &range;
117 else
118 return nullptr;
119 }
120 }
121 // the value is out of the range: return NULL instead of symbol
122 return nullptr;
123}
124
126{
127 if ( const QgsRendererRange *range = rangeForValue( value ) )
128 return range->symbol();
129 return nullptr;
130}
131
133{
134 if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
135 return true;
136
137 return !valueCapturedByEarlierRange( rangeIndex, mRanges.at( rangeIndex ).lowerValue() );
138}
139
141{
142 if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
143 return true;
144
145 return !valueCapturedByEarlierRange( rangeIndex, mRanges.at( rangeIndex ).upperValue() );
146}
147
149{
150 if ( rangeIndex <= 0 || rangeIndex >= mRanges.size() )
151 return false;
152
153 const QgsRendererRange &target = mRanges.at( rangeIndex );
154 for ( int i = 0; i < rangeIndex; ++i )
155 {
156 // ranges which only touch at a shared boundary value are not considered as overlapping
157 if ( mRanges.at( i ).lowerValue() < target.upperValue() && target.lowerValue() < mRanges.at( i ).upperValue() )
158 return true;
159 }
160 return false;
161}
162
164{
165 if ( const QgsRendererRange *matchingRange = rangeForValue( value ) )
166 {
167 for ( const QgsRendererRange &range : mRanges )
168 {
169 if ( matchingRange == &range )
170 return range.uuid();
171 }
172 }
173 return QString();
174}
175
177{
178 return originalSymbolForFeature( feature, context );
179}
180
181QVariant QgsGraduatedSymbolRenderer::valueForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
182{
183 QgsAttributes attrs = feature.attributes();
184 QVariant value;
185 if ( mExpression )
186 {
187 value = mExpression->evaluate( &context.expressionContext() );
188 }
189 else
190 {
191 value = attrs.value( mAttrNum );
192 }
193
194 return value;
195}
196
198{
199 QVariant value = valueForFeature( feature, context );
200
201 // Null values should not be categorized
202 if ( QgsVariantUtils::isNull( value ) )
203 return nullptr;
204
205 // find the right category
206 return symbolForValue( value.toDouble() );
207}
208
210{
211 QgsFeatureRenderer::startRender( context, fields );
212
213 mCounting = context.rendererScale() == 0.0;
214
215 // find out classification attribute index from name
216 mAttrNum = fields.lookupField( mAttrName );
217
218 if ( mAttrNum == -1 )
219 {
220 mExpression = std::make_unique<QgsExpression>( mAttrName );
221 mExpression->prepare( &context.expressionContext() );
222 }
223
224 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
225 {
226 if ( !range.symbol() )
227 continue;
228
229 range.symbol()->startRender( context, fields );
230 }
231}
232
234{
236
237 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
238 {
239 if ( !range.symbol() )
240 continue;
241
242 range.symbol()->stopRender( context );
243 }
244}
245
247{
248 QSet<QString> attributes;
249
250 // mAttrName can contain either attribute name or an expression.
251 // Sometimes it is not possible to distinguish between those two,
252 // e.g. "a - b" can be both a valid attribute name or expression.
253 // Since we do not have access to fields here, try both options.
254 attributes << mAttrName;
255
256 QgsExpression testExpr( mAttrName );
257 if ( !testExpr.hasParserError() )
258 attributes.unite( testExpr.referencedColumns() );
259
260 QgsRangeList::const_iterator range_it = mRanges.constBegin();
261 for ( ; range_it != mRanges.constEnd(); ++range_it )
262 {
263 QgsSymbol *symbol = range_it->symbol();
264 if ( symbol )
265 {
266 attributes.unite( symbol->usedAttributes( context ) );
267 }
268 }
269 return attributes;
270}
271
273{
274 QgsExpression testExpr( mAttrName );
275 if ( !testExpr.hasParserError() )
276 {
277 QgsExpressionContext context;
278 context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) ); // unfortunately no layer access available!
279 testExpr.prepare( &context );
280 return testExpr.needsGeometry();
281 }
282 return false;
283}
284
286{
287 if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
288 return false;
289 mRanges[rangeIndex].setSymbol( symbol );
290 return true;
291}
292
293bool QgsGraduatedSymbolRenderer::updateRangeLabel( int rangeIndex, const QString &label )
294{
295 if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
296 return false;
297 mRanges[rangeIndex].setLabel( label );
298 return true;
299}
300
301bool QgsGraduatedSymbolRenderer::updateRangeUpperValue( int rangeIndex, double value )
302{
303 if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
304 return false;
305 QgsRendererRange &range = mRanges[rangeIndex];
307 if ( rangeIndex == 0 )
309 else if ( rangeIndex == mRanges.count() )
311
312 bool isDefaultLabel = mClassificationMethod->labelForRange( range, pos ) == range.label();
313 range.setUpperValue( value );
314 if ( isDefaultLabel )
315 range.setLabel( mClassificationMethod->labelForRange( range, pos ) );
316
317 return true;
318}
319
320bool QgsGraduatedSymbolRenderer::updateRangeLowerValue( int rangeIndex, double value )
321{
322 if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
323 return false;
324
325 QgsRendererRange &range = mRanges[rangeIndex];
327 if ( rangeIndex == 0 )
329 else if ( rangeIndex == mRanges.count() )
331
332 bool isDefaultLabel = mClassificationMethod->labelForRange( range, pos ) == range.label();
333 range.setLowerValue( value );
334 if ( isDefaultLabel )
335 range.setLabel( mClassificationMethod->labelForRange( range, pos ) );
336
337 return true;
338}
339
341{
342 if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
343 return false;
344 mRanges[rangeIndex].setRenderState( value );
345 return true;
346}
347
349{
350 QString s = u"GRADUATED: attr %1\n"_s.arg( mAttrName );
351 for ( int i = 0; i < mRanges.count(); i++ )
352 s += mRanges[i].dump();
353 return s;
354}
355
373
374void QgsGraduatedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
375{
376 QgsSldExportContext context;
377 context.setExtraProperties( props );
378 toSld( doc, element, context );
379}
380
381bool QgsGraduatedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
382{
383 const QVariantMap oldProps = context.extraProperties();
384 QVariantMap newProps = oldProps;
385 newProps[u"attribute"_s] = mAttrName;
386 newProps[u"method"_s] = graduatedMethodStr( mGraduatedMethod );
387 context.setExtraProperties( newProps );
388
389 // create a Rule for each range
390 bool first = true;
391 bool result = true;
392 for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); ++it )
393 {
394 if ( !it->toSld( doc, element, mAttrName, context, first ) )
395 result = false;
396 first = false;
397 }
398 context.setExtraProperties( oldProps );
399 return result;
400}
401
403{
404 Q_UNUSED( context )
405 QgsSymbolList lst;
406 lst.reserve( mRanges.count() );
407 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
408 {
409 lst.append( range.symbol() );
410 }
411 return lst;
412}
413
415{
416 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
417 {
418 QgsStyleSymbolEntity entity( range.symbol() );
419 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, u"%1 - %2"_s.arg( range.lowerValue() ).arg( range.upperValue() ), range.label() ) ) )
420 return false;
421 }
422
423 if ( mSourceColorRamp )
424 {
426 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
427 return false;
428 }
429
430 return true;
431}
432
437
438QList<double> QgsGraduatedSymbolRenderer::calcEqualIntervalBreaks( double minimum, double maximum, int classes, bool useSymmetricMode, double symmetryPoint, bool astride )
439{
442 QList<QgsClassificationRange> _classes = method.classes( minimum, maximum, classes );
444}
445
448 QgsVectorLayer *vlayer,
449 const QString &attrName,
450 int classes,
451 Mode mode,
452 QgsSymbol *symbol,
453 QgsColorRamp *ramp,
455 bool useSymmetricMode,
456 double symmetryPoint,
457 const QStringList &listForCboPrettyBreaks,
458 bool astride
459)
460{
461 Q_UNUSED( listForCboPrettyBreaks )
462
464 auto r = std::make_unique< QgsGraduatedSymbolRenderer >( attrName, ranges );
465 r->setSourceSymbol( symbol->clone() );
466 r->setSourceColorRamp( ramp->clone() );
467
468 QString methodId = methodIdFromMode( mode );
469 std::unique_ptr< QgsClassificationMethod > method = QgsApplication::classificationMethodRegistry()->method( methodId );
470
471 if ( method )
472 {
473 method->setSymmetricMode( useSymmetricMode, symmetryPoint, astride );
474 method->setLabelFormat( labelFormat.format() );
475 method->setLabelTrimTrailingZeroes( labelFormat.trimTrailingZeroes() );
476 method->setLabelPrecision( labelFormat.precision() );
477 }
478 r->setClassificationMethod( method.release() );
479
480 QString error;
481 r->updateClasses( vlayer, classes, error );
482 ( void ) error;
483
484 return r.release();
485}
487
489{
490 if ( mAttrName.isEmpty() )
491 return;
492
493 QString methodId = methodIdFromMode( mode );
494 std::unique_ptr< QgsClassificationMethod > method = QgsApplication::classificationMethodRegistry()->method( methodId );
495 method->setSymmetricMode( useSymmetricMode, symmetryPoint, astride );
496 setClassificationMethod( method.release() );
497
498 QString error;
499 updateClasses( vlayer, nclasses, error );
500 ( void ) error;
501}
502
503void QgsGraduatedSymbolRenderer::updateClasses( const QgsVectorLayer *vl, int nclasses, QString &error )
504{
505 Q_UNUSED( error )
507 return;
508
509 QList<QgsClassificationRange> classes = mClassificationMethod->classesV2( vl, mAttrName, nclasses, error );
510
512
513 for ( QList<QgsClassificationRange>::iterator it = classes.begin(); it != classes.end(); ++it )
514 {
515 QgsSymbol *newSymbol = mSourceSymbol ? mSourceSymbol->clone() : QgsSymbol::defaultSymbol( vl->geometryType() ).release();
516 addClass( QgsRendererRange( *it, newSymbol ) );
517 }
518 updateColorRamp( nullptr );
519}
520
527
528std::unique_ptr<QgsFeatureRenderer> QgsGraduatedSymbolRenderer::create( QDomElement &element, const QgsReadWriteContext &context )
529{
530 QDomElement symbolsElem = element.firstChildElement( u"symbols"_s );
531 if ( symbolsElem.isNull() )
532 return nullptr;
533
534 QDomElement rangesElem = element.firstChildElement( u"ranges"_s );
535 if ( rangesElem.isNull() )
536 return nullptr;
537
538 QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
540
541 QDomElement rangeElem = rangesElem.firstChildElement();
542 int i = 0;
543 QSet<QString> usedUuids;
544 while ( !rangeElem.isNull() )
545 {
546 if ( rangeElem.tagName() == "range"_L1 )
547 {
548 double lowerValue = rangeElem.attribute( u"lower"_s ).toDouble();
549 double upperValue = rangeElem.attribute( u"upper"_s ).toDouble();
550 QString symbolName = rangeElem.attribute( u"symbol"_s );
551 QString label = context.projectTranslator()->translate( u"project:layers:%1:legendsymbollabels"_s.arg( context.currentLayerId() ), rangeElem.attribute( u"label"_s ) );
552 QgsDebugMsgLevel( "context" + u"project:layers:%1:legendsymbollabels"_s.arg( context.currentLayerId() ) + " source " + rangeElem.attribute( u"label"_s ), 3 );
553
554 bool render = rangeElem.attribute( u"render"_s, u"true"_s ) != "false"_L1;
555 QString uuid = rangeElem.attribute( u"uuid"_s, QString::number( i++ ) );
556 while ( usedUuids.contains( uuid ) )
557 {
558 uuid = QUuid::createUuid().toString();
559 }
560 if ( symbolMap.contains( symbolName ) )
561 {
562 QgsSymbol *symbol = symbolMap.take( symbolName );
563 ranges.append( QgsRendererRange( lowerValue, upperValue, symbol, label, render, uuid ) );
564 usedUuids << uuid;
565 }
566 }
567 rangeElem = rangeElem.nextSiblingElement();
568 }
569
570 QString attrName = element.attribute( u"attr"_s );
571
572 auto r = std::make_unique< QgsGraduatedSymbolRenderer >( attrName, ranges );
573
574 QString attrMethod = element.attribute( u"graduatedMethod"_s );
575 if ( !attrMethod.isEmpty() )
576 {
578 r->setGraduatedMethod( Qgis::GraduatedMethod::Color );
579 else if ( attrMethod == graduatedMethodStr( Qgis::GraduatedMethod::Size ) )
580 r->setGraduatedMethod( Qgis::GraduatedMethod::Size );
581 }
582
583
584 // delete symbols if there are any more
586
587 // try to load source symbol (optional)
588 QDomElement sourceSymbolElem = element.firstChildElement( u"source-symbol"_s );
589 if ( !sourceSymbolElem.isNull() )
590 {
591 QgsSymbolMap sourceSymbolMap = QgsSymbolLayerUtils::loadSymbols( sourceSymbolElem, context );
592 if ( sourceSymbolMap.contains( u"0"_s ) )
593 {
594 r->setSourceSymbol( sourceSymbolMap.take( u"0"_s ) );
595 }
596 QgsSymbolLayerUtils::clearSymbolMap( sourceSymbolMap );
597 }
598
599 // try to load color ramp (optional)
600 QDomElement sourceColorRampElem = element.firstChildElement( u"colorramp"_s );
601 if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( u"name"_s ) == "[source]"_L1 )
602 {
603 r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ).release() );
604 }
605
606 // try to load mode
607
608 QDomElement modeElem = element.firstChildElement( u"mode"_s ); // old format, backward compatibility
609 QDomElement methodElem = element.firstChildElement( u"classificationMethod"_s );
610 std::unique_ptr< QgsClassificationMethod > method;
611
612 // TODO QGIS 5 Remove
613 // backward compatibility for QGIS project < 3.10
614 if ( !modeElem.isNull() )
615 {
616 QString modeString = modeElem.attribute( u"name"_s );
617 QString methodId;
618 // the strings saved in the project does not match with the old Mode enum
619 if ( modeString == "equal"_L1 )
620 methodId = u"EqualInterval"_s;
621 else if ( modeString == "quantile"_L1 )
622 methodId = u"Quantile"_s;
623 else if ( modeString == "jenks"_L1 )
624 methodId = u"Jenks"_s;
625 else if ( modeString == "stddev"_L1 )
626 methodId = u"StdDev"_s;
627 else if ( modeString == "pretty"_L1 )
628 methodId = u"Pretty"_s;
629
631
632 // symmetric mode
633 QDomElement symmetricModeElem = element.firstChildElement( u"symmetricMode"_s );
634 if ( !symmetricModeElem.isNull() )
635 {
636 // symmetry
637 QString symmetricEnabled = symmetricModeElem.attribute( u"enabled"_s );
638 QString symmetricPointString = symmetricModeElem.attribute( u"symmetryPoint"_s );
639 QString astrideEnabled = symmetricModeElem.attribute( u"astride"_s );
640 method->setSymmetricMode( symmetricEnabled == "true"_L1, symmetricPointString.toDouble(), astrideEnabled == "true"_L1 );
641 }
642 QDomElement labelFormatElem = element.firstChildElement( u"labelformat"_s );
643 if ( !labelFormatElem.isNull() )
644 {
645 // label format
646 QString format = labelFormatElem.attribute( u"format"_s, "%1" + u" - "_s + "%2" );
647 int precision = labelFormatElem.attribute( u"decimalplaces"_s, u"4"_s ).toInt();
648 bool trimTrailingZeroes = labelFormatElem.attribute( u"trimtrailingzeroes"_s, u"false"_s ) == "true"_L1;
649 method->setLabelFormat( format );
650 method->setLabelPrecision( precision );
651 method->setLabelTrimTrailingZeroes( trimTrailingZeroes );
652 }
653 // End of backward compatibility
654 }
655 else
656 {
657 // QGIS project 3.10+
658 method = QgsClassificationMethod::create( methodElem, context );
659 }
660
661 // apply the method
662 r->setClassificationMethod( method.release() );
663
664 QDomElement rotationElem = element.firstChildElement( u"rotation"_s );
665 if ( !rotationElem.isNull() && !rotationElem.attribute( u"field"_s ).isEmpty() )
666 {
667 for ( const QgsRendererRange &range : std::as_const( r->mRanges ) )
668 {
669 convertSymbolRotation( range.symbol(), rotationElem.attribute( u"field"_s ) );
670 }
671 if ( r->mSourceSymbol )
672 {
673 convertSymbolRotation( r->mSourceSymbol.get(), rotationElem.attribute( u"field"_s ) );
674 }
675 }
676 QDomElement sizeScaleElem = element.firstChildElement( u"sizescale"_s );
677 if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( u"field"_s ).isEmpty() )
678 {
679 for ( const QgsRendererRange &range : std::as_const( r->mRanges ) )
680 {
681 convertSymbolSizeScale( range.symbol(), QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( u"scalemethod"_s ) ), sizeScaleElem.attribute( u"field"_s ) );
682 }
683 if ( r->mSourceSymbol && r->mSourceSymbol->type() == Qgis::SymbolType::Marker )
684 {
685 convertSymbolSizeScale( r->mSourceSymbol.get(), QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( u"scalemethod"_s ) ), sizeScaleElem.attribute( u"field"_s ) );
686 }
687 }
688
689 QDomElement ddsLegendSizeElem = element.firstChildElement( u"data-defined-size-legend"_s );
690 if ( !ddsLegendSizeElem.isNull() )
691 {
692 r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
693 }
694 // TODO: symbol levels
695 return r;
696}
697
698QDomElement QgsGraduatedSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
699{
700 QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
701 rendererElem.setAttribute( u"type"_s, u"graduatedSymbol"_s );
702 rendererElem.setAttribute( u"attr"_s, mAttrName );
703 rendererElem.setAttribute( u"graduatedMethod"_s, graduatedMethodStr( mGraduatedMethod ) );
704
705 // ranges
706 int i = 0;
708 QDomElement rangesElem = doc.createElement( u"ranges"_s );
709 QgsRangeList::const_iterator it = mRanges.constBegin();
710 for ( ; it != mRanges.constEnd(); ++it )
711 {
712 const QgsRendererRange &range = *it;
713 QString symbolName = QString::number( i );
714 symbols.insert( symbolName, range.symbol() );
715
716 QDomElement rangeElem = doc.createElement( u"range"_s );
717 rangeElem.setAttribute( u"lower"_s, QString::number( range.lowerValue(), 'f', 15 ) );
718 rangeElem.setAttribute( u"upper"_s, QString::number( range.upperValue(), 'f', 15 ) );
719 rangeElem.setAttribute( u"symbol"_s, symbolName );
720 rangeElem.setAttribute( u"label"_s, range.label() );
721 rangeElem.setAttribute( u"render"_s, range.renderState() ? u"true"_s : u"false"_s );
722 rangeElem.setAttribute( u"uuid"_s, range.uuid() );
723 rangesElem.appendChild( rangeElem );
724 i++;
725 }
726
727 rendererElem.appendChild( rangesElem );
728
729 // save symbols
730 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, u"symbols"_s, doc, context );
731 rendererElem.appendChild( symbolsElem );
732
733 // save source symbol
734 if ( mSourceSymbol )
735 {
736 QgsSymbolMap sourceSymbols;
737 sourceSymbols.insert( u"0"_s, mSourceSymbol.get() );
738 QDomElement sourceSymbolElem = QgsSymbolLayerUtils::saveSymbols( sourceSymbols, u"source-symbol"_s, doc, context );
739 rendererElem.appendChild( sourceSymbolElem );
740 }
741
742 // save source color ramp
743 if ( mSourceColorRamp )
744 {
745 QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( u"[source]"_s, mSourceColorRamp.get(), doc );
746 rendererElem.appendChild( colorRampElem );
747 }
748
749 // save classification method
750 QDomElement classificationMethodElem = mClassificationMethod->save( doc, context );
751 rendererElem.appendChild( classificationMethodElem );
752
753 QDomElement rotationElem = doc.createElement( u"rotation"_s );
754 rendererElem.appendChild( rotationElem );
755
756 QDomElement sizeScaleElem = doc.createElement( u"sizescale"_s );
757 rendererElem.appendChild( sizeScaleElem );
758
760 {
761 QDomElement ddsLegendElem = doc.createElement( u"data-defined-size-legend"_s );
762 mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
763 rendererElem.appendChild( ddsLegendElem );
764 }
765
766 saveRendererData( doc, rendererElem, context );
767
768 return rendererElem;
769}
770
771QgsLegendSymbolList QgsGraduatedSymbolRenderer::baseLegendSymbolItems() const
772{
774 lst.reserve( mRanges.size() );
775 for ( const QgsRendererRange &range : mRanges )
776 {
777 lst << QgsLegendSymbolItem( range.symbol(), range.label(), range.uuid(), true );
778 }
779 return lst;
780}
781
783QString QgsGraduatedSymbolRenderer::methodIdFromMode( QgsGraduatedSymbolRenderer::Mode mode )
784{
785 switch ( mode )
786 {
787 case EqualInterval:
788 return u"EqualInterval"_s;
789 case Quantile:
790 return u"Quantile"_s;
791 case Jenks:
792 return u"Jenks"_s;
793 case StdDev:
794 return u"StdDev"_s;
795 case Pretty:
796 return u"Pretty"_s;
797 case Custom:
798 return QString();
799 }
800 return QString();
801}
802
803QgsGraduatedSymbolRenderer::Mode QgsGraduatedSymbolRenderer::modeFromMethodId( const QString &methodId )
804{
805 if ( methodId == "EqualInterval"_L1 )
806 return EqualInterval;
807 if ( methodId == "Quantile"_L1 )
808 return Quantile;
809 if ( methodId == "Jenks"_L1 )
810 return Jenks;
811 if ( methodId == "StdDev"_L1 )
812 return StdDev;
813 if ( methodId == "Pretty"_L1 )
814 return Pretty;
815 else
816 return Custom;
817}
819
821{
823 {
824 // check that all symbols that have the same size expression
825 QgsProperty ddSize;
826 for ( const QgsRendererRange &range : mRanges )
827 {
828 const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( range.symbol() );
829 if ( ddSize )
830 {
831 QgsProperty sSize( symbol->dataDefinedSize() );
832 if ( sSize && sSize != ddSize )
833 {
834 // no common size expression
835 return baseLegendSymbolItems();
836 }
837 }
838 else
839 {
840 ddSize = symbol->dataDefinedSize();
841 }
842 }
843
844 if ( ddSize && ddSize.isActive() )
845 {
847
849 ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSourceSymbol.get() ), ddSize );
850 lst += ddSizeLegend.legendSymbolList();
851
852 lst += baseLegendSymbolItems();
853 return lst;
854 }
855 }
856
857 return baseLegendSymbolItems();
858}
859
860QSet< QString > QgsGraduatedSymbolRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
861{
862 QVariant value = valueForFeature( feature, context );
863
864 // Null values should not be categorized
865 if ( QgsVariantUtils::isNull( value ) )
866 return QSet< QString >();
867
868 // find the right category
869 QString key = legendKeyForValue( value.toDouble() );
870 if ( !key.isNull() )
871 return QSet< QString >() << key;
872 else
873 return QSet< QString >();
874}
875
876QString QgsGraduatedSymbolRenderer::legendKeyToExpression( const QString &key, QgsVectorLayer *layer, bool &ok ) const
877{
878 ok = false;
879 int i = 0;
880 for ( i = 0; i < mRanges.size(); i++ )
881 {
882 if ( mRanges[i].uuid() == key )
883 {
884 ok = true;
885 break;
886 }
887 }
888
889 if ( !ok )
890 {
891 ok = false;
892 return QString();
893 }
894
895 const QString attributeComponent = QgsExpression::quoteFieldExpression( mAttrName, layer );
896 const QgsRendererRange &range = mRanges[i];
897
898 return u"(%1 >= %2) AND (%1 <= %3)"_s
899 .arg( attributeComponent, QgsExpression::quotedValue( range.lowerValue(), QMetaType::Type::Double ), QgsExpression::quotedValue( range.upperValue(), QMetaType::Type::Double ) );
900}
901
906
908{
909 return mSourceSymbol.get();
910}
911
916
921
926
928{
929 if ( ramp == mSourceColorRamp.get() )
930 return;
931
932 mSourceColorRamp.reset( ramp );
933}
934
936{
937 double min = std::numeric_limits<double>::max();
938 for ( int i = 0; i < mRanges.count(); i++ )
939 {
940 double sz = 0;
941 if ( mRanges[i].symbol()->type() == Qgis::SymbolType::Marker )
942 sz = static_cast< QgsMarkerSymbol * >( mRanges[i].symbol() )->size();
943 else if ( mRanges[i].symbol()->type() == Qgis::SymbolType::Line )
944 sz = static_cast< QgsLineSymbol * >( mRanges[i].symbol() )->width();
945 min = std::min( sz, min );
946 }
947 return min;
948}
949
951{
952 double max = std::numeric_limits<double>::min();
953 for ( int i = 0; i < mRanges.count(); i++ )
954 {
955 double sz = 0;
956 if ( mRanges[i].symbol()->type() == Qgis::SymbolType::Marker )
957 sz = static_cast< QgsMarkerSymbol * >( mRanges[i].symbol() )->size();
958 else if ( mRanges[i].symbol()->type() == Qgis::SymbolType::Line )
959 sz = static_cast< QgsLineSymbol * >( mRanges[i].symbol() )->width();
960 max = std::max( sz, max );
961 }
962 return max;
963}
964
965void QgsGraduatedSymbolRenderer::setSymbolSizes( double minSize, double maxSize )
966{
967 for ( int i = 0; i < mRanges.count(); i++ )
968 {
969 std::unique_ptr<QgsSymbol> symbol( mRanges.at( i ).symbol() ? mRanges.at( i ).symbol()->clone() : nullptr );
970 const double size = mRanges.count() > 1 ? minSize + i * ( maxSize - minSize ) / ( mRanges.count() - 1 ) : .5 * ( maxSize + minSize );
971 if ( symbol )
972 {
973 switch ( symbol->type() )
974 {
976 static_cast< QgsMarkerSymbol * >( symbol.get() )->setSize( size );
977 break;
979 static_cast< QgsLineSymbol * >( symbol.get() )->setWidth( size );
980 break;
983 break;
984 }
985 updateRangeSymbol( i, symbol.release() );
986 }
987 }
988}
989
991{
992 int i = 0;
993 if ( ramp )
994 {
995 setSourceColorRamp( ramp );
996 }
997
998 if ( mSourceColorRamp )
999 {
1000 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
1001 {
1002 QgsSymbol *symbol = range.symbol() ? range.symbol()->clone() : nullptr;
1003 if ( symbol )
1004 {
1005 double colorValue;
1006 colorValue = ( mRanges.count() > 1 ? static_cast< double >( i ) / ( mRanges.count() - 1 ) : 0 );
1007 symbol->setColor( mSourceColorRamp->color( colorValue ) );
1008 }
1009 updateRangeSymbol( i, symbol );
1010 ++i;
1011 }
1012 }
1013}
1014
1016{
1017 if ( !sym )
1018 return;
1019
1020 int i = 0;
1021 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
1022 {
1023 std::unique_ptr<QgsSymbol> symbol( sym->clone() );
1024 switch ( mGraduatedMethod )
1025 {
1027 {
1028 symbol->setColor( range.symbol()->color() );
1029 break;
1030 }
1032 {
1033 if ( symbol->type() == Qgis::SymbolType::Marker )
1034 static_cast<QgsMarkerSymbol *>( symbol.get() )->setSize( static_cast<QgsMarkerSymbol *>( range.symbol() )->size() );
1035 else if ( symbol->type() == Qgis::SymbolType::Line )
1036 static_cast<QgsLineSymbol *>( symbol.get() )->setWidth( static_cast<QgsLineSymbol *>( range.symbol() )->width() );
1037 break;
1038 }
1039 }
1040 updateRangeSymbol( i, symbol.release() );
1041 ++i;
1042 }
1043 setSourceSymbol( sym->clone() );
1044}
1045
1047{
1048 return true;
1049}
1050
1052{
1053 for ( const QgsRendererRange &range : std::as_const( mRanges ) )
1054 {
1055 if ( range.uuid() == key )
1056 {
1057 return range.renderState();
1058 }
1059 }
1060 return true;
1061}
1062
1063void QgsGraduatedSymbolRenderer::checkLegendSymbolItem( const QString &key, bool state )
1064{
1065 for ( int i = 0; i < mRanges.size(); i++ )
1066 {
1067 if ( mRanges[i].uuid() == key )
1068 {
1069 updateRangeRenderState( i, state );
1070 break;
1071 }
1072 }
1073}
1074
1076{
1077 bool ok = false;
1078 int i = 0;
1079 for ( i = 0; i < mRanges.size(); i++ )
1080 {
1081 if ( mRanges[i].uuid() == key )
1082 {
1083 ok = true;
1084 break;
1085 }
1086 }
1087
1088 if ( ok )
1089 updateRangeSymbol( i, symbol );
1090 else
1091 delete symbol;
1092}
1093
1094void QgsGraduatedSymbolRenderer::setLegendSymbolItemLabel( const QString &key, const QString &label )
1095{
1096 for ( int i = 0; i < mRanges.size(); i++ )
1097 {
1098 if ( mRanges[i].uuid() == key )
1099 {
1100 updateRangeLabel( i, label );
1101 break;
1102 }
1103 }
1104}
1105
1107{
1108 QgsSymbol *newSymbol = symbol->clone();
1109 QString label = u"0.0 - 0.0"_s;
1110 mRanges.insert( 0, QgsRendererRange( 0.0, 0.0, newSymbol, label ) );
1111}
1112
1113void QgsGraduatedSymbolRenderer::addClass( double lower, double upper )
1114{
1115 QgsSymbol *newSymbol = mSourceSymbol->clone();
1116 QString label = mClassificationMethod->labelForRange( lower, upper );
1117 mRanges.append( QgsRendererRange( lower, upper, newSymbol, label ) );
1118}
1119
1121{
1122 QMutableListIterator< QgsRendererRange > it( mRanges );
1123 while ( it.hasNext() )
1124 {
1125 QgsRendererRange range = it.next();
1126 if ( range.lowerValue() < breakValue && range.upperValue() > breakValue )
1127 {
1129 newRange.setLowerValue( breakValue );
1130 newRange.setUpperValue( range.upperValue() );
1131 newRange.setLabel( mClassificationMethod->labelForRange( newRange ) );
1132 newRange.setSymbol( mSourceSymbol->clone() );
1133
1134 //update old range
1135 bool isDefaultLabel = range.label() == mClassificationMethod->labelForRange( range );
1136 range.setUpperValue( breakValue );
1137 if ( isDefaultLabel )
1138 range.setLabel( mClassificationMethod->labelForRange( range.lowerValue(), breakValue ) );
1139 it.setValue( range );
1140
1141 it.insert( newRange );
1142 break;
1143 }
1144 }
1145
1146 if ( updateSymbols )
1147 {
1148 switch ( mGraduatedMethod )
1149 {
1152 break;
1155 break;
1156 }
1157 }
1158}
1159
1161{
1162 mRanges.append( range );
1163}
1164
1166{
1167 mRanges.removeAt( idx );
1168}
1169
1174
1177{
1178 mClassificationMethod->setLabelFormat( labelFormat.format() );
1179 mClassificationMethod->setLabelPrecision( labelFormat.precision() );
1180 mClassificationMethod->setLabelTrimTrailingZeroes( labelFormat.trimTrailingZeroes() );
1181
1182 if ( updateRanges )
1183 {
1185 }
1186}
1188
1190{
1191 for ( int i = 0; i < mRanges.count(); i++ )
1192 {
1194 if ( i == 0 )
1196 else if ( i == mRanges.count() - 1 )
1198 mRanges[i].setLabel( mClassificationMethod->labelForRange( mRanges[i], pos ) );
1199 }
1200}
1201
1203{
1204 // Find the minimum size of a class
1205 double minClassRange = 0.0;
1206 for ( const QgsRendererRange &rendererRange : std::as_const( mRanges ) )
1207 {
1208 double range = rendererRange.upperValue() - rendererRange.lowerValue();
1209 if ( range <= 0.0 )
1210 continue;
1211 if ( minClassRange == 0.0 || range < minClassRange )
1212 minClassRange = range;
1213 }
1214 if ( minClassRange <= 0.0 )
1215 return;
1216
1217 // Now set the number of decimal places to ensure no more than 20% error in
1218 // representing this range (up to 10% at upper and lower end)
1219
1220 int ndp = 10;
1221 double nextDpMinRange = 0.0000000099;
1222 while ( ndp > 0 && nextDpMinRange < minClassRange )
1223 {
1224 ndp--;
1225 nextDpMinRange *= 10.0;
1226 }
1227 mClassificationMethod->setLabelPrecision( ndp );
1228 if ( updateRanges )
1230}
1231
1233{
1234 if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() )
1235 return;
1236 mRanges.move( from, to );
1237}
1238
1240{
1241 return r1 < r2;
1242}
1243
1245{
1246 return !valueLessThan( r1, r2 );
1247}
1248
1250{
1251 if ( order == Qt::AscendingOrder )
1252 {
1253 std::sort( mRanges.begin(), mRanges.end(), valueLessThan );
1254 }
1255 else
1256 {
1257 std::sort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1258 }
1259}
1260
1262{
1263 QgsRangeList sortedRanges = mRanges;
1264 std::sort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1265
1266 QgsRangeList::const_iterator it = sortedRanges.constBegin();
1267 if ( it == sortedRanges.constEnd() )
1268 return false;
1269
1270 if ( ( *it ).upperValue() < ( *it ).lowerValue() )
1271 return true;
1272
1273 double prevMax = ( *it ).upperValue();
1274 ++it;
1275
1276 for ( ; it != sortedRanges.constEnd(); ++it )
1277 {
1278 if ( ( *it ).upperValue() < ( *it ).lowerValue() )
1279 return true;
1280
1281 if ( ( *it ).lowerValue() < prevMax )
1282 return true;
1283
1284 prevMax = ( *it ).upperValue();
1285 }
1286 return false;
1287}
1288
1290{
1291 QgsRangeList sortedRanges = mRanges;
1292 std::sort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1293
1294 QgsRangeList::const_iterator it = sortedRanges.constBegin();
1295 if ( it == sortedRanges.constEnd() )
1296 return false;
1297
1298 double prevMax = ( *it ).upperValue();
1299 ++it;
1300
1301 for ( ; it != sortedRanges.constEnd(); ++it )
1302 {
1303 if ( !qgsDoubleNear( ( *it ).lowerValue(), prevMax ) )
1304 return true;
1305
1306 prevMax = ( *it ).upperValue();
1307 }
1308 return false;
1309}
1310
1312{
1313 return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1314}
1315
1317{
1318 return !labelLessThan( r1, r2 );
1319}
1320
1322{
1323 if ( order == Qt::AscendingOrder )
1324 {
1325 std::sort( mRanges.begin(), mRanges.end(), labelLessThan );
1326 }
1327 else
1328 {
1329 std::sort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1330 }
1331}
1332
1337
1342
1344{
1345 QString methodId = methodIdFromMode( mode );
1346 std::unique_ptr< QgsClassificationMethod > method = QgsApplication::classificationMethodRegistry()->method( methodId );
1347 setClassificationMethod( method.release() );
1348}
1349
1354
1356{
1357 mClassificationMethod->setSymmetricMode( mClassificationMethod->symmetricModeEnabled(), symmetryPoint, mClassificationMethod->symmetryAstride() );
1358}
1359
1361{
1362 mClassificationMethod->setSymmetricMode( mClassificationMethod->symmetricModeEnabled(), mClassificationMethod->symmetryPoint(), astride );
1363}
1364
1365std::unique_ptr<QgsGraduatedSymbolRenderer> QgsGraduatedSymbolRenderer::convertFromRenderer( const QgsFeatureRenderer *renderer )
1366{
1367 std::unique_ptr< QgsGraduatedSymbolRenderer > r;
1368 if ( renderer->type() == "graduatedSymbol"_L1 )
1369 {
1370 r.reset( static_cast<QgsGraduatedSymbolRenderer *>( renderer->clone() ) );
1371 }
1372 else if ( renderer->type() == "categorizedSymbol"_L1 )
1373 {
1374 const QgsCategorizedSymbolRenderer *categorizedSymbolRenderer = dynamic_cast<const QgsCategorizedSymbolRenderer *>( renderer );
1375 if ( categorizedSymbolRenderer )
1376 {
1377 r = std::make_unique< QgsGraduatedSymbolRenderer >( QString(), QgsRangeList() );
1378 if ( categorizedSymbolRenderer->sourceSymbol() )
1379 r->setSourceSymbol( categorizedSymbolRenderer->sourceSymbol()->clone() );
1380 if ( categorizedSymbolRenderer->sourceColorRamp() )
1381 {
1382 bool isRandom = dynamic_cast<const QgsRandomColorRamp *>( categorizedSymbolRenderer->sourceColorRamp() )
1383 || dynamic_cast<const QgsLimitedRandomColorRamp *>( categorizedSymbolRenderer->sourceColorRamp() );
1384 if ( !isRandom )
1385 r->setSourceColorRamp( categorizedSymbolRenderer->sourceColorRamp()->clone() );
1386 }
1387 r->setClassAttribute( categorizedSymbolRenderer->classAttribute() );
1388 }
1389 }
1390 else if ( renderer->type() == "pointDisplacement"_L1 || renderer->type() == "pointCluster"_L1 )
1391 {
1392 const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
1393 if ( pointDistanceRenderer )
1394 r = convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
1395 }
1396 else if ( renderer->type() == "invertedPolygonRenderer"_L1 )
1397 {
1398 const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
1399 if ( invertedPolygonRenderer )
1400 r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1401 }
1402
1403 // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1404 // Could have applied this to specific renderer types (singleSymbol, graduatedSymbol)
1405
1406 if ( !r )
1407 {
1408 r = std::make_unique< QgsGraduatedSymbolRenderer >( QString(), QgsRangeList() );
1409 QgsRenderContext context;
1410 QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
1411 if ( !symbols.isEmpty() )
1412 {
1413 r->setSourceSymbol( symbols.at( 0 )->clone() );
1414 }
1415 }
1416
1417 renderer->copyRendererData( r.get() );
1418
1419 return r;
1420}
1421
1426
1431
1433{
1434 switch ( method )
1435 {
1437 return u"GraduatedColor"_s;
1439 return u"GraduatedSize"_s;
1440 }
1441 return QString();
1442}
1443
1444bool QgsGraduatedSymbolRenderer::valueCapturedByEarlierRange( const int rangeIndex, const double value ) const
1445{
1446 for ( int i = 0; i < rangeIndex; ++i )
1447 {
1448 if ( mRanges.at( i ).lowerValue() <= value && mRanges.at( i ).upperValue() >= value )
1449 return true;
1450 }
1451 return false;
1452}
QFlags< FeatureRendererFlag > FeatureRendererFlags
Flags controlling behavior of vector feature renderers.
Definition qgis.h:920
GraduatedMethod
Methods for modifying symbols by range in a graduated symbol renderer.
Definition qgis.h:3544
@ Size
Alter size of symbols.
Definition qgis.h:3546
@ Color
Alter color of symbols.
Definition qgis.h:3545
@ AffectsLabeling
If present, indicates that the renderer will participate in the map labeling problem.
Definition qgis.h:911
@ Marker
Marker symbol.
Definition qgis.h:672
@ Line
Line symbol.
Definition qgis.h:673
@ Fill
Fill symbol.
Definition qgis.h:674
@ Hybrid
Hybrid symbol.
Definition qgis.h:675
@ AffectsLabeling
If present, indicates that the symbol will participate in the map labeling problem.
Definition qgis.h:931
static QgsClassificationMethodRegistry * classificationMethodRegistry()
Returns the application's classification methods registry, used in graduated renderer.
A vector of attributes.
A feature renderer which represents features using a list of renderer categories.
QgsSymbol * sourceSymbol()
Returns the renderer's source symbol, which is the base symbol used for the each categories' symbol b...
QString classAttribute() const
Returns the class attribute for the renderer, which is the field name or expression string from the l...
QgsColorRamp * sourceColorRamp()
Returns the source color ramp, from which each categories' color is derived.
A classification method which uses equal width intervals.
std::unique_ptr< QgsClassificationMethod > method(const QString &id)
Returns a new instance of the method for the given id.
An abstract class for implementations of classification methods.
static void makeBreaksSymmetric(QList< double > &breaks, double symmetryPoint, bool astride)
Remove the breaks that are above the existing opposite sign classes to keep colors symmetrically bala...
static std::unique_ptr< QgsClassificationMethod > create(const QDomElement &element, const QgsReadWriteContext &context)
Reads the DOM element and return a new classification method from it.
Q_DECL_DEPRECATED QList< QgsClassificationRange > classes(const QgsVectorLayer *layer, const QString &expression, int nclasses)
This will calculate the classes for a given layer to define the classes.
void setSymmetricMode(bool enabled, double symmetryPoint=0, bool symmetryAstride=false)
Defines if the symmetric mode is enables and configures its parameters.
ClassPosition
Defines the class position.
@ LowerBound
The class is at the lower bound.
@ UpperBound
The class is at the upper bound.
@ Inner
The class is not at a bound.
static QList< double > rangesToBreaks(const QList< QgsClassificationRange > &classes)
Transforms a list of classes to a list of breaks.
Abstract base class for color ramps.
virtual QgsColorRamp * clone() const =0
Creates a clone of the color ramp.
Object that keeps configuration of appearance of marker symbol's data-defined size in legend.
static QgsDataDefinedSizeLegend * readXml(const QDomElement &elem, const QgsReadWriteContext &context) SIP_FACTORY
Creates instance from given element and returns it (caller takes ownership). Returns nullptr on error...
void updateFromSymbolAndProperty(const QgsMarkerSymbol *symbol, const QgsProperty &ddSize)
Updates the list of classes, source symbol and title label from given symbol and property.
QgsLegendSymbolList legendSymbolList() const
Generates legend symbol items according to the configuration.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
static QString quoteFieldExpression(const QString &expression, const QgsVectorLayer *layer)
Validate if the expression is a field in the layer and ensure it is quoted.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QgsFeatureRenderer(const QString &type)
virtual void stopRender(QgsRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
QString type() const
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
static void convertSymbolRotation(QgsSymbol *symbol, const QString &field)
Converts old rotation expressions to symbol level data defined angles.
void saveRendererData(QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context)
Saves generic renderer data into the specified element.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
static void convertSymbolSizeScale(QgsSymbol *symbol, Qgis::ScaleMethod method, const QString &field)
Converts old sizeScale expressions to symbol level data defined sizes.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsAttributes attributes
Definition qgsfeature.h:64
Container of fields for a vector layer.
Definition qgsfields.h:45
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
QgsLegendSymbolList legendSymbolItems() const override
Returns a list of symbology items for the legend.
QgsSymbol * sourceSymbol()
Returns the renderer's source symbol, which is the base symbol used for the each classes' symbol befo...
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
Stores renderer properties to an XML element.
void updateSymbols(QgsSymbol *sym)
Update all the symbols but leave breaks and colors.
const QgsRendererRange * rangeForValue(double value) const
Returns the renderer range matching the provided value, or nullptr if no range matches the value.
QgsColorRamp * sourceColorRamp()
Returns the source color ramp, from which each classes' color is derived.
bool updateRangeSymbol(int rangeIndex, QgsSymbol *symbol)
bool rangeOverlapsEarlierRange(int rangeIndex) const
Returns true if the range at the specified rangeIndex overlaps with any range positioned earlier in t...
void addBreak(double breakValue, bool updateSymbols=true)
Add a breakpoint by splitting existing classes so that the specified value becomes a break between tw...
Q_DECL_DEPRECATED void setAstride(bool astride)
Set if we want a central class astride the pivot value.
void setClassificationMethod(QgsClassificationMethod *method)
Defines the classification method This will take ownership of the method.
static Q_DECL_DEPRECATED QList< double > calcEqualIntervalBreaks(double minimum, double maximum, int classes, bool useSymmetricMode, double symmetryPoint, bool astride)
Compute the equal interval classification.
QgsGraduatedSymbolRenderer * clone() const override
Create a deep copy of this renderer.
std::unique_ptr< QgsDataDefinedSizeLegend > mDataDefinedSizeLegend
static Q_DECL_DEPRECATED void makeBreaksSymmetric(QList< double > &breaks, double symmetryPoint, bool astride)
Remove the breaks that are above the existing opposite sign classes to keep colors symmetrically bala...
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
double maxSymbolSize() const
Returns the max symbol size when graduated by size.
Q_DECL_DEPRECATED bool useSymmetricMode() const
Returns if we want to classify symmetric around a given value.
bool updateRangeLabel(int rangeIndex, const QString &label)
void addClass(QgsSymbol *symbol)
Adds a class to the renderer, with the specified symbol.
QgsClassificationMethod * classificationMethod() const
Returns the classification method.
QgsSymbol * originalSymbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns symbol for feature.
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
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.1 specs.
void checkLegendSymbolItem(const QString &key, bool state=true) override
Sets whether the legend symbology item with the specified ley should be checked.
std::unique_ptr< QgsSymbol > mSourceSymbol
void setLegendSymbolItem(const QString &key, QgsSymbol *symbol) override
Sets the symbol to be used for a legend symbol item.
Qgis::FeatureRendererFlags flags() const override
Returns flags associated with the renderer.
void setGraduatedMethod(Qgis::GraduatedMethod method)
Set the method used for graduation (either size or color).
Q_DECL_DEPRECATED void setMode(Mode mode)
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns legend keys matching a specified feature.
bool rangesOverlap() const
Tests whether classes assigned to the renderer have ranges which overlap.
static std::unique_ptr< QgsGraduatedSymbolRenderer > convertFromRenderer(const QgsFeatureRenderer *renderer)
creates a QgsGraduatedSymbolRenderer from an existing renderer.
std::shared_ptr< QgsClassificationMethod > mClassificationMethod
Q_DECL_DEPRECATED bool astride() const
Returns if we want to have a central class astride the pivot value.
Q_DECL_DEPRECATED void setLabelFormat(const QgsRendererRangeLabelFormat &labelFormat, bool updateRanges=false)
Set the label format used to generate default classification labels.
Q_DECL_DEPRECATED void updateClasses(QgsVectorLayer *vlayer, Mode mode, int nclasses, bool useSymmetricMode=false, double symmetryPoint=0.0, bool astride=false)
Recalculate classes for a layer.
QString dump() const override
Returns debug information about this renderer.
bool updateRangeUpperValue(int rangeIndex, double value)
QString legendKeyToExpression(const QString &key, QgsVectorLayer *layer, bool &ok) const override
Attempts to convert the specified legend rule key to a QGIS expression matching the features displaye...
QString legendKeyForValue(double value) const
Returns the matching legend key for a value.
void setSourceColorRamp(QgsColorRamp *ramp)
Sets the source color ramp.
void setSourceSymbol(QgsSymbol *sym)
Sets the source symbol for the renderer, which is the base symbol used for the each classes' symbol b...
bool rangeLowerBoundIsInclusive(int rangeIndex) const
Returns true if the lower bound of the range at the specified rangeIndex is inclusive,...
bool legendSymbolItemChecked(const QString &key) override
Returns true if the legend symbology item with the specified key is checked.
bool updateRangeLowerValue(int rangeIndex, double value)
void stopRender(QgsRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
bool legendSymbolItemsCheckable() const override
Returns true if symbology items in legend are checkable.
void moveClass(int from, int to)
Moves the category at index position from to index position to.
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns a list of attributes required by this renderer.
void updateRangeLabels()
Updates the labels of the ranges.
bool updateRangeRenderState(int rangeIndex, bool render)
QgsSymbol * symbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
To be overridden.
std::unique_ptr< QgsColorRamp > mSourceColorRamp
bool rangeUpperBoundIsInclusive(int rangeIndex) const
Returns true if the upper bound of the range at the specified rangeIndex is inclusive,...
QgsSymbolList symbols(QgsRenderContext &context) const override
Returns list of symbols used by the renderer.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration of appearance of legend when using data-defined size for marker symbols.
void updateColorRamp(QgsColorRamp *ramp=nullptr)
Update the color ramp used.
Q_DECL_DEPRECATED Mode mode() const
QgsGraduatedSymbolRenderer(const QString &attrName=QString(), const QgsRangeList &ranges=QgsRangeList())
void setLegendSymbolItemLabel(const QString &key, const QString &label) override
Sets the label for a legend symbol item.
double minSymbolSize() const
Returns the min symbol size when graduated by size.
Qgis::GraduatedMethod graduatedMethod() const
Returns the method used for graduation (either size or color).
static std::unique_ptr< QgsFeatureRenderer > create(QDomElement &element, const QgsReadWriteContext &context)
create renderer from XML element
bool filterNeedsGeometry() const override
Returns true if this renderer requires the geometry to apply the filter.
int mAttrNum
attribute index (derived from attribute name in startRender)
Q_DECL_DEPRECATED void setSymmetryPoint(double symmetryPoint)
Set the pivot point.
std::unique_ptr< QgsExpression > mExpression
static QString graduatedMethodStr(Qgis::GraduatedMethod method)
void setSymbolSizes(double minSize, double maxSize)
set varying symbol size for classes
void calculateLabelPrecision(bool updateRanges=true)
Reset the label decimal places to a numberbased on the minimum class interval.
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
const QgsRangeList & ranges() const
Returns a list of all ranges used in the classification.
Q_DECL_DEPRECATED QgsRendererRangeLabelFormat labelFormat() const
Returns the label format used to generate default classification labels.
static Q_DECL_DEPRECATED QgsGraduatedSymbolRenderer * createRenderer(QgsVectorLayer *vlayer, const QString &attrName, int classes, Mode mode, QgsSymbol *symbol, QgsColorRamp *ramp, const QgsRendererRangeLabelFormat &legendFormat=QgsRendererRangeLabelFormat(), bool useSymmetricMode=false, double symmetryPoint=0.0, const QStringList &listForCboPrettyBreaks=QStringList(), bool astride=false)
Creates a new graduated renderer.
Q_DECL_DEPRECATED double symmetryPoint() const
Returns the pivot value for symmetric classification.
Q_DECL_DEPRECATED void setUseSymmetricMode(bool useSymmetricMode)
Set if we want to classify symmetric around a given value.
QgsSymbol * symbolForValue(double value) const
Gets the symbol which is used to represent value.
bool rangesHaveGaps() const
Tests whether classes assigned to the renderer have gaps between the ranges.
A polygon-only feature renderer used to display features inverted.
Stores information about one class/rule of a vector layer renderer in a unified way that can be used ...
Constrained random color ramp, which returns random colors based on preset parameters.
A line symbol type, for rendering LineString and MultiLineString geometries.
void setWidth(double width) const
Sets the width for the whole line symbol.
double width() const
Returns the estimated width for the whole symbol, which is the maximum width of all marker symbol lay...
A marker symbol type, for rendering Point and MultiPoint geometries.
void setSize(double size) const
Sets the size for the whole symbol.
double size() const
Returns the estimated size for the whole symbol, which is the maximum size of all marker symbol layer...
QgsProperty dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
virtual QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const =0
Translates a string using the Qt QTranslator mechanism.
A store for object properties.
bool isActive() const
Returns whether the property is currently active.
A color ramp consisting of random colors, constrained within component ranges.
A container for the context for various read/write operations on objects.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
const QString currentLayerId() const
Returns the currently used layer id as string.
Contains information about the context of a rendering operation.
double rendererScale() const
Returns the renderer map scale.
QgsExpressionContext & expressionContext()
Gets the expression context.
Encapsulates the formatting for a QgsRendererRange label.
Represents a value range for a QgsGraduatedSymbolRenderer.
void setUpperValue(double upperValue)
Sets the upper bound of the range.
QString label() const
Returns the label used for the range.
void setSymbol(QgsSymbol *s)
Sets the symbol used for the range.
QgsSymbol * symbol() const
Returns the symbol used for the range.
void setLabel(const QString &label)
Sets the label used for the range.
bool renderState() const
Returns true if the range should be rendered.
double upperValue() const
Returns the upper bound of the range.
void setLowerValue(double lowerValue)
Sets the lower bound of the range.
QString uuid() const
Returns the unique identifier for this range.
double lowerValue() const
Returns the lower bound of the range.
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.
QVariantMap extraProperties() const
Returns the open ended set of properties that can drive/inform the SLD encoding.
A color ramp entity for QgsStyle databases.
Definition qgsstyle.h:1491
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.
A symbol entity for QgsStyle databases.
Definition qgsstyle.h:1462
static std::unique_ptr< QgsColorRamp > loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
static Qgis::ScaleMethod decodeScaleMethod(const QString &str)
Decodes a symbol scale method from a string.
static QDomElement saveColorRamp(const QString &name, const QgsColorRamp *ramp, QDomDocument &doc)
Encodes a color ramp's settings to an XML element.
static void clearSymbolMap(QgsSymbolMap &symbols)
static QgsSymbolMap loadSymbols(QDomElement &element, const QgsReadWriteContext &context)
Reads a collection of symbols from XML and returns them in a map. Caller is responsible for deleting ...
static QDomElement saveSymbols(QgsSymbolMap &symbols, const QString &tagName, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a collection of symbols to XML with specified tagName for the top-level element.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:227
void setColor(const QColor &color) const
Sets the color for the symbol.
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
static std::unique_ptr< QgsSymbol > defaultSymbol(Qgis::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based dataset.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:8361
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:8360
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7725
#define SIP_DEPRECATED
Definition qgis_sip.h:113
bool labelGreaterThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
bool valueLessThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
bool valueGreaterThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
bool labelLessThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
bool valueLessThan(const QgsRendererRange &r1, const QgsRendererRange &r2)
bool labelGreaterThan(const QgsRendererRange &r1, const QgsRendererRange &r2)
bool labelLessThan(const QgsRendererRange &r1, const QgsRendererRange &r2)
bool valueGreaterThan(const QgsRendererRange &r1, const QgsRendererRange &r2)
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:80
#define RENDERER_TAG_NAME
Definition qgsrenderer.h:57
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition qgsrenderer.h:52
QList< QgsSymbol * > QgsSymbolList
Definition qgsrenderer.h:51
QList< QgsRendererRange > QgsRangeList
Contains information relating to the style entity currently being visited.