Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgscategorizedsymbolrenderer.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 : : #include <algorithm>
16 : :
17 : : #include "qgscategorizedsymbolrenderer.h"
18 : :
19 : : #include "qgsdatadefinedsizelegend.h"
20 : : #include "qgssymbol.h"
21 : : #include "qgssymbollayerutils.h"
22 : : #include "qgscolorramp.h"
23 : : #include "qgsgraduatedsymbolrenderer.h"
24 : : #include "qgspointdisplacementrenderer.h"
25 : : #include "qgsinvertedpolygonrenderer.h"
26 : : #include "qgspainteffect.h"
27 : : #include "qgspainteffectregistry.h"
28 : : #include "qgssymbollayer.h"
29 : : #include "qgsfeature.h"
30 : : #include "qgsvectorlayer.h"
31 : : #include "qgslogger.h"
32 : : #include "qgsproperty.h"
33 : : #include "qgsstyle.h"
34 : : #include "qgsfieldformatter.h"
35 : : #include "qgsfieldformatterregistry.h"
36 : : #include "qgsapplication.h"
37 : : #include "qgsexpressioncontextutils.h"
38 : : #include "qgsstyleentityvisitor.h"
39 : : #include "qgsembeddedsymbolrenderer.h"
40 : :
41 : : #include <QDomDocument>
42 : : #include <QDomElement>
43 : : #include <QSettings> // for legend
44 : : #include <QRegularExpression>
45 : :
46 : 0 : QgsRendererCategory::QgsRendererCategory( const QVariant &value, QgsSymbol *symbol, const QString &label, bool render )
47 : 0 : : mValue( value )
48 : 0 : , mSymbol( symbol )
49 : 0 : , mLabel( label )
50 : 0 : , mRender( render )
51 : : {
52 : 0 : }
53 : :
54 : 0 : QgsRendererCategory::QgsRendererCategory( const QgsRendererCategory &cat )
55 : 0 : : mValue( cat.mValue )
56 : 0 : , mSymbol( cat.mSymbol ? cat.mSymbol->clone() : nullptr )
57 : 0 : , mLabel( cat.mLabel )
58 : 0 : , mRender( cat.mRender )
59 : : {
60 : 0 : }
61 : :
62 : : // copy+swap idion, the copy is done through the 'pass by value'
63 : 0 : QgsRendererCategory &QgsRendererCategory::operator=( QgsRendererCategory cat )
64 : : {
65 : 0 : swap( cat );
66 : 0 : return *this;
67 : : }
68 : :
69 : 0 : void QgsRendererCategory::swap( QgsRendererCategory &cat )
70 : : {
71 : 0 : std::swap( mValue, cat.mValue );
72 : 0 : std::swap( mSymbol, cat.mSymbol );
73 : 0 : std::swap( mLabel, cat.mLabel );
74 : 0 : }
75 : :
76 : 0 : QVariant QgsRendererCategory::value() const
77 : : {
78 : 0 : return mValue;
79 : : }
80 : :
81 : 0 : QgsSymbol *QgsRendererCategory::symbol() const
82 : : {
83 : 0 : return mSymbol.get();
84 : : }
85 : :
86 : 0 : QString QgsRendererCategory::label() const
87 : : {
88 : 0 : return mLabel;
89 : : }
90 : :
91 : 0 : bool QgsRendererCategory::renderState() const
92 : : {
93 : 0 : return mRender;
94 : : }
95 : :
96 : 0 : void QgsRendererCategory::setValue( const QVariant &value )
97 : : {
98 : 0 : mValue = value;
99 : 0 : }
100 : :
101 : 0 : void QgsRendererCategory::setSymbol( QgsSymbol *s )
102 : : {
103 : 0 : if ( mSymbol.get() != s ) mSymbol.reset( s );
104 : 0 : }
105 : :
106 : 0 : void QgsRendererCategory::setLabel( const QString &label )
107 : : {
108 : 0 : mLabel = label;
109 : 0 : }
110 : :
111 : 0 : void QgsRendererCategory::setRenderState( bool render )
112 : : {
113 : 0 : mRender = render;
114 : 0 : }
115 : :
116 : 0 : QString QgsRendererCategory::dump() const
117 : : {
118 : 0 : return QStringLiteral( "%1::%2::%3:%4\n" ).arg( mValue.toString(), mLabel, mSymbol->dump() ).arg( mRender );
119 : 0 : }
120 : :
121 : 0 : void QgsRendererCategory::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
122 : : {
123 : 0 : if ( !mSymbol.get() || props.value( QStringLiteral( "attribute" ), QString() ).toString().isEmpty() )
124 : 0 : return;
125 : :
126 : 0 : QString attrName = props[ QStringLiteral( "attribute" )].toString();
127 : :
128 : 0 : QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
129 : 0 : element.appendChild( ruleElem );
130 : :
131 : 0 : QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
132 : 0 : nameElem.appendChild( doc.createTextNode( mLabel ) );
133 : 0 : ruleElem.appendChild( nameElem );
134 : :
135 : 0 : QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
136 : 0 : QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
137 : 0 : QString descrStr = QStringLiteral( "%1 is '%2'" ).arg( attrName, mValue.toString() );
138 : 0 : titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
139 : 0 : descrElem.appendChild( titleElem );
140 : 0 : ruleElem.appendChild( descrElem );
141 : :
142 : : // create the ogc:Filter for the range
143 : 0 : QString filterFunc;
144 : 0 : if ( mValue.isNull() || mValue.toString().isEmpty() )
145 : : {
146 : 0 : filterFunc = QStringLiteral( "%1 = '%2' or %1 is null" )
147 : 0 : .arg( attrName.replace( '\"', QLatin1String( "\"\"" ) ),
148 : 0 : mValue.toString().replace( '\'', QLatin1String( "''" ) ) );
149 : 0 : }
150 : : else
151 : : {
152 : 0 : filterFunc = QStringLiteral( "%1 = '%2'" )
153 : 0 : .arg( attrName.replace( '\"', QLatin1String( "\"\"" ) ),
154 : 0 : mValue.toString().replace( '\'', QLatin1String( "''" ) ) );
155 : : }
156 : :
157 : 0 : QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, filterFunc );
158 : :
159 : : // add the mix/max scale denoms if we got any from the callers
160 : 0 : QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
161 : :
162 : 0 : mSymbol->toSld( doc, ruleElem, props );
163 : 0 : }
164 : :
165 : : ///////////////////
166 : :
167 : 0 : QgsCategorizedSymbolRenderer::QgsCategorizedSymbolRenderer( const QString &attrName, const QgsCategoryList &categories )
168 : 0 : : QgsFeatureRenderer( QStringLiteral( "categorizedSymbol" ) )
169 : 0 : , mAttrName( attrName )
170 : 0 : {
171 : : //important - we need a deep copy of the categories list, not a shared copy. This is required because
172 : : //QgsRendererCategory::symbol() is marked const, and so retrieving the symbol via this method does not
173 : : //trigger a detachment and copy of mCategories BUT that same method CAN be used to modify a symbol in place
174 : 0 : for ( const QgsRendererCategory &cat : categories )
175 : : {
176 : 0 : if ( !cat.symbol() )
177 : : {
178 : 0 : QgsDebugMsg( QStringLiteral( "invalid symbol in a category! ignoring..." ) );
179 : 0 : }
180 : 0 : mCategories << cat;
181 : : }
182 : 0 : }
183 : :
184 : 0 : QgsCategorizedSymbolRenderer::~QgsCategorizedSymbolRenderer() = default;
185 : :
186 : 0 : void QgsCategorizedSymbolRenderer::rebuildHash()
187 : : {
188 : 0 : mSymbolHash.clear();
189 : :
190 : 0 : for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
191 : : {
192 : 0 : const QVariant val = cat.value();
193 : 0 : if ( val.type() == QVariant::List )
194 : : {
195 : 0 : const QVariantList list = val.toList();
196 : 0 : for ( const QVariant &v : list )
197 : : {
198 : 0 : mSymbolHash.insert( v.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
199 : : }
200 : 0 : }
201 : : else
202 : : {
203 : 0 : mSymbolHash.insert( val.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
204 : : }
205 : 0 : }
206 : 0 : }
207 : :
208 : 0 : QgsSymbol *QgsCategorizedSymbolRenderer::skipRender()
209 : : {
210 : 0 : return nullptr;
211 : : }
212 : :
213 : 0 : QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value ) const
214 : : {
215 : 0 : bool found = false;
216 : 0 : return symbolForValue( value, found );
217 : : }
218 : :
219 : 0 : QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value, bool &foundMatchingSymbol ) const
220 : : {
221 : 0 : foundMatchingSymbol = false;
222 : :
223 : : // TODO: special case for int, double
224 : 0 : QHash<QString, QgsSymbol *>::const_iterator it = mSymbolHash.constFind( value.isNull() ? QString() : value.toString() );
225 : 0 : if ( it == mSymbolHash.constEnd() )
226 : : {
227 : 0 : if ( mSymbolHash.isEmpty() )
228 : : {
229 : 0 : QgsDebugMsg( QStringLiteral( "there are no hashed symbols!!!" ) );
230 : 0 : }
231 : : else
232 : : {
233 : 0 : QgsDebugMsgLevel( "attribute value not found: " + value.toString(), 3 );
234 : : }
235 : 0 : return nullptr;
236 : : }
237 : :
238 : 0 : foundMatchingSymbol = true;
239 : :
240 : 0 : return *it;
241 : 0 : }
242 : :
243 : 0 : QgsSymbol *QgsCategorizedSymbolRenderer::symbolForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
244 : : {
245 : 0 : return originalSymbolForFeature( feature, context );
246 : : }
247 : :
248 : 0 : QVariant QgsCategorizedSymbolRenderer::valueForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
249 : : {
250 : 0 : QgsAttributes attrs = feature.attributes();
251 : 0 : QVariant value;
252 : 0 : if ( mAttrNum == -1 )
253 : : {
254 : : Q_ASSERT( mExpression );
255 : :
256 : 0 : value = mExpression->evaluate( &context.expressionContext() );
257 : 0 : }
258 : : else
259 : : {
260 : 0 : value = attrs.value( mAttrNum );
261 : : }
262 : :
263 : 0 : return value;
264 : 0 : }
265 : :
266 : 0 : QgsSymbol *QgsCategorizedSymbolRenderer::originalSymbolForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
267 : : {
268 : 0 : QVariant value = valueForFeature( feature, context );
269 : :
270 : 0 : bool foundCategory = false;
271 : : // find the right symbol for the category
272 : 0 : QgsSymbol *symbol = symbolForValue( value, foundCategory );
273 : :
274 : 0 : if ( !foundCategory )
275 : : {
276 : : // if no symbol found, use default symbol
277 : 0 : return symbolForValue( QVariant( "" ), foundCategory );
278 : : }
279 : :
280 : 0 : return symbol;
281 : 0 : }
282 : :
283 : :
284 : 0 : int QgsCategorizedSymbolRenderer::categoryIndexForValue( const QVariant &val )
285 : : {
286 : 0 : for ( int i = 0; i < mCategories.count(); i++ )
287 : : {
288 : 0 : if ( mCategories[i].value() == val )
289 : 0 : return i;
290 : 0 : }
291 : 0 : return -1;
292 : 0 : }
293 : :
294 : 0 : int QgsCategorizedSymbolRenderer::categoryIndexForLabel( const QString &val )
295 : : {
296 : 0 : int idx = -1;
297 : 0 : for ( int i = 0; i < mCategories.count(); i++ )
298 : : {
299 : 0 : if ( mCategories[i].label() == val )
300 : : {
301 : 0 : if ( idx != -1 )
302 : 0 : return -1;
303 : : else
304 : 0 : idx = i;
305 : 0 : }
306 : 0 : }
307 : 0 : return idx;
308 : 0 : }
309 : :
310 : 0 : bool QgsCategorizedSymbolRenderer::updateCategoryValue( int catIndex, const QVariant &value )
311 : : {
312 : 0 : if ( catIndex < 0 || catIndex >= mCategories.size() )
313 : 0 : return false;
314 : 0 : mCategories[catIndex].setValue( value );
315 : 0 : return true;
316 : 0 : }
317 : :
318 : 0 : bool QgsCategorizedSymbolRenderer::updateCategorySymbol( int catIndex, QgsSymbol *symbol )
319 : : {
320 : 0 : if ( catIndex < 0 || catIndex >= mCategories.size() )
321 : 0 : return false;
322 : 0 : mCategories[catIndex].setSymbol( symbol );
323 : 0 : return true;
324 : 0 : }
325 : :
326 : 0 : bool QgsCategorizedSymbolRenderer::updateCategoryLabel( int catIndex, const QString &label )
327 : : {
328 : 0 : if ( catIndex < 0 || catIndex >= mCategories.size() )
329 : 0 : return false;
330 : 0 : mCategories[catIndex].setLabel( label );
331 : 0 : return true;
332 : 0 : }
333 : :
334 : 0 : bool QgsCategorizedSymbolRenderer::updateCategoryRenderState( int catIndex, bool render )
335 : : {
336 : 0 : if ( catIndex < 0 || catIndex >= mCategories.size() )
337 : 0 : return false;
338 : 0 : mCategories[catIndex].setRenderState( render );
339 : 0 : return true;
340 : 0 : }
341 : :
342 : 0 : void QgsCategorizedSymbolRenderer::addCategory( const QgsRendererCategory &cat )
343 : : {
344 : 0 : if ( !cat.symbol() )
345 : : {
346 : 0 : QgsDebugMsg( QStringLiteral( "invalid symbol in a category! ignoring..." ) );
347 : 0 : return;
348 : : }
349 : :
350 : 0 : mCategories.append( cat );
351 : 0 : }
352 : :
353 : 0 : bool QgsCategorizedSymbolRenderer::deleteCategory( int catIndex )
354 : : {
355 : 0 : if ( catIndex < 0 || catIndex >= mCategories.size() )
356 : 0 : return false;
357 : :
358 : 0 : mCategories.removeAt( catIndex );
359 : 0 : return true;
360 : 0 : }
361 : :
362 : 0 : void QgsCategorizedSymbolRenderer::deleteAllCategories()
363 : : {
364 : 0 : mCategories.clear();
365 : 0 : }
366 : :
367 : 0 : void QgsCategorizedSymbolRenderer::moveCategory( int from, int to )
368 : : {
369 : 0 : if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() ) return;
370 : 0 : mCategories.move( from, to );
371 : 0 : }
372 : :
373 : 0 : bool valueLessThan( const QgsRendererCategory &c1, const QgsRendererCategory &c2 )
374 : : {
375 : 0 : return qgsVariantLessThan( c1.value(), c2.value() );
376 : 0 : }
377 : 0 : bool valueGreaterThan( const QgsRendererCategory &c1, const QgsRendererCategory &c2 )
378 : : {
379 : 0 : return qgsVariantGreaterThan( c1.value(), c2.value() );
380 : 0 : }
381 : :
382 : 0 : void QgsCategorizedSymbolRenderer::sortByValue( Qt::SortOrder order )
383 : : {
384 : 0 : if ( order == Qt::AscendingOrder )
385 : : {
386 : 0 : std::sort( mCategories.begin(), mCategories.end(), valueLessThan );
387 : 0 : }
388 : : else
389 : : {
390 : 0 : std::sort( mCategories.begin(), mCategories.end(), valueGreaterThan );
391 : : }
392 : 0 : }
393 : :
394 : 0 : bool labelLessThan( const QgsRendererCategory &c1, const QgsRendererCategory &c2 )
395 : : {
396 : 0 : return QString::localeAwareCompare( c1.label(), c2.label() ) < 0;
397 : 0 : }
398 : :
399 : 0 : bool labelGreaterThan( const QgsRendererCategory &c1, const QgsRendererCategory &c2 )
400 : : {
401 : 0 : return !labelLessThan( c1, c2 );
402 : : }
403 : :
404 : 0 : void QgsCategorizedSymbolRenderer::sortByLabel( Qt::SortOrder order )
405 : : {
406 : 0 : if ( order == Qt::AscendingOrder )
407 : : {
408 : 0 : std::sort( mCategories.begin(), mCategories.end(), labelLessThan );
409 : 0 : }
410 : : else
411 : : {
412 : 0 : std::sort( mCategories.begin(), mCategories.end(), labelGreaterThan );
413 : : }
414 : 0 : }
415 : :
416 : 0 : void QgsCategorizedSymbolRenderer::startRender( QgsRenderContext &context, const QgsFields &fields )
417 : : {
418 : 0 : QgsFeatureRenderer::startRender( context, fields );
419 : :
420 : 0 : mCounting = context.rendererScale() == 0.0;
421 : :
422 : : // make sure that the hash table is up to date
423 : 0 : rebuildHash();
424 : :
425 : : // find out classification attribute index from name
426 : 0 : mAttrNum = fields.lookupField( mAttrName );
427 : 0 : if ( mAttrNum == -1 )
428 : : {
429 : 0 : mExpression.reset( new QgsExpression( mAttrName ) );
430 : 0 : mExpression->prepare( &context.expressionContext() );
431 : 0 : }
432 : :
433 : 0 : for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
434 : : {
435 : 0 : cat.symbol()->startRender( context, fields );
436 : : }
437 : 0 : }
438 : :
439 : 0 : void QgsCategorizedSymbolRenderer::stopRender( QgsRenderContext &context )
440 : : {
441 : 0 : QgsFeatureRenderer::stopRender( context );
442 : :
443 : 0 : for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
444 : : {
445 : 0 : cat.symbol()->stopRender( context );
446 : : }
447 : 0 : mExpression.reset();
448 : 0 : }
449 : :
450 : 0 : QSet<QString> QgsCategorizedSymbolRenderer::usedAttributes( const QgsRenderContext &context ) const
451 : : {
452 : 0 : QSet<QString> attributes;
453 : 0 :
454 : : // mAttrName can contain either attribute name or an expression.
455 : : // Sometimes it is not possible to distinguish between those two,
456 : : // e.g. "a - b" can be both a valid attribute name or expression.
457 : 0 : // Since we do not have access to fields here, try both options.
458 : 0 : attributes << mAttrName;
459 : :
460 : 0 : QgsExpression testExpr( mAttrName );
461 : 0 : if ( !testExpr.hasParserError() )
462 : 0 : attributes.unite( testExpr.referencedColumns() );
463 : :
464 : 0 : QgsCategoryList::const_iterator catIt = mCategories.constBegin();
465 : 0 : for ( ; catIt != mCategories.constEnd(); ++catIt )
466 : : {
467 : 0 : QgsSymbol *catSymbol = catIt->symbol();
468 : 0 : if ( catSymbol )
469 : : {
470 : 0 : attributes.unite( catSymbol->usedAttributes( context ) );
471 : 0 : }
472 : 0 : }
473 : 0 : return attributes;
474 : 0 : }
475 : :
476 : 0 : bool QgsCategorizedSymbolRenderer::filterNeedsGeometry() const
477 : : {
478 : 0 : QgsExpression testExpr( mAttrName );
479 : 0 : if ( !testExpr.hasParserError() )
480 : : {
481 : 0 : QgsExpressionContext context;
482 : 0 : context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) ); // unfortunately no layer access available!
483 : 0 : testExpr.prepare( &context );
484 : 0 : return testExpr.needsGeometry();
485 : 0 : }
486 : 0 : return false;
487 : 0 : }
488 : :
489 : 0 : QString QgsCategorizedSymbolRenderer::dump() const
490 : : {
491 : 0 : QString s = QStringLiteral( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
492 : 0 : for ( int i = 0; i < mCategories.count(); i++ )
493 : 0 : s += mCategories[i].dump();
494 : 0 : return s;
495 : 0 : }
496 : :
497 : 0 : QgsCategorizedSymbolRenderer *QgsCategorizedSymbolRenderer::clone() const
498 : : {
499 : 0 : QgsCategorizedSymbolRenderer *r = new QgsCategorizedSymbolRenderer( mAttrName, mCategories );
500 : 0 : if ( mSourceSymbol )
501 : 0 : r->setSourceSymbol( mSourceSymbol->clone() );
502 : 0 : if ( mSourceColorRamp )
503 : : {
504 : 0 : r->setSourceColorRamp( mSourceColorRamp->clone() );
505 : 0 : }
506 : 0 : r->setUsingSymbolLevels( usingSymbolLevels() );
507 : 0 : r->setDataDefinedSizeLegend( mDataDefinedSizeLegend ? new QgsDataDefinedSizeLegend( *mDataDefinedSizeLegend ) : nullptr );
508 : :
509 : 0 : copyRendererData( r );
510 : 0 : return r;
511 : 0 : }
512 : :
513 : 0 : void QgsCategorizedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
514 : : {
515 : 0 : QVariantMap newProps = props;
516 : 0 : newProps[ QStringLiteral( "attribute" )] = mAttrName;
517 : :
518 : : // create a Rule for each range
519 : 0 : for ( QgsCategoryList::const_iterator it = mCategories.constBegin(); it != mCategories.constEnd(); ++it )
520 : : {
521 : 0 : it->toSld( doc, element, newProps );
522 : 0 : }
523 : 0 : }
524 : :
525 : 0 : QString QgsCategorizedSymbolRenderer::filter( const QgsFields &fields )
526 : : {
527 : 0 : int attrNum = fields.lookupField( mAttrName );
528 : 0 : bool isExpression = ( attrNum == -1 );
529 : :
530 : 0 : bool hasDefault = false;
531 : 0 : bool defaultActive = false;
532 : 0 : bool allActive = true;
533 : 0 : bool noneActive = true;
534 : :
535 : : //we need to build lists of both inactive and active values, as either list may be required
536 : : //depending on whether the default category is active or not
537 : 0 : QString activeValues;
538 : 0 : QString inactiveValues;
539 : :
540 : 0 : for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
541 : : {
542 : 0 : if ( cat.value() == "" || cat.value().isNull() )
543 : : {
544 : 0 : hasDefault = true;
545 : 0 : defaultActive = cat.renderState();
546 : 0 : }
547 : :
548 : 0 : noneActive = noneActive && !cat.renderState();
549 : 0 : allActive = allActive && cat.renderState();
550 : :
551 : 0 : QVariant::Type valType = isExpression ? cat.value().type() : fields.at( attrNum ).type();
552 : 0 : const bool isList = cat.value().type() == QVariant::List;
553 : 0 : QString value = QgsExpression::quotedValue( cat.value(), valType );
554 : :
555 : 0 : if ( !cat.renderState() )
556 : : {
557 : 0 : if ( cat.value() != "" )
558 : : {
559 : 0 : if ( isList )
560 : : {
561 : 0 : const QVariantList list = cat.value().toList();
562 : 0 : for ( const QVariant &v : list )
563 : : {
564 : 0 : if ( !inactiveValues.isEmpty() )
565 : 0 : inactiveValues.append( ',' );
566 : :
567 : 0 : inactiveValues.append( QgsExpression::quotedValue( v, isExpression ? v.type() : fields.at( attrNum ).type() ) );
568 : : }
569 : 0 : }
570 : : else
571 : : {
572 : 0 : if ( !inactiveValues.isEmpty() )
573 : 0 : inactiveValues.append( ',' );
574 : :
575 : 0 : inactiveValues.append( value );
576 : : }
577 : 0 : }
578 : 0 : }
579 : : else
580 : : {
581 : 0 : if ( cat.value() != "" )
582 : : {
583 : 0 : if ( isList )
584 : : {
585 : 0 : const QVariantList list = cat.value().toList();
586 : 0 : for ( const QVariant &v : list )
587 : : {
588 : 0 : if ( !activeValues.isEmpty() )
589 : 0 : activeValues.append( ',' );
590 : :
591 : 0 : activeValues.append( QgsExpression::quotedValue( v, isExpression ? v.type() : fields.at( attrNum ).type() ) );
592 : : }
593 : 0 : }
594 : : else
595 : : {
596 : 0 : if ( !activeValues.isEmpty() )
597 : 0 : activeValues.append( ',' );
598 : :
599 : 0 : activeValues.append( value );
600 : : }
601 : 0 : }
602 : : }
603 : 0 : }
604 : :
605 : 0 : QString attr = isExpression ? mAttrName : QStringLiteral( "\"%1\"" ).arg( mAttrName );
606 : :
607 : 0 : if ( allActive && hasDefault )
608 : : {
609 : 0 : return QString();
610 : : }
611 : 0 : else if ( noneActive )
612 : : {
613 : 0 : return QStringLiteral( "FALSE" );
614 : : }
615 : 0 : else if ( defaultActive )
616 : : {
617 : 0 : return QStringLiteral( "(%1) NOT IN (%2) OR (%1) IS NULL" ).arg( attr, inactiveValues );
618 : : }
619 : : else
620 : : {
621 : 0 : return QStringLiteral( "(%1) IN (%2)" ).arg( attr, activeValues );
622 : : }
623 : 0 : }
624 : :
625 : 0 : QgsSymbolList QgsCategorizedSymbolRenderer::symbols( QgsRenderContext &context ) const
626 : : {
627 : 0 : Q_UNUSED( context )
628 : 0 : QgsSymbolList lst;
629 : 0 : lst.reserve( mCategories.count() );
630 : 0 : for ( const QgsRendererCategory &cat : mCategories )
631 : : {
632 : 0 : lst.append( cat.symbol() );
633 : : }
634 : 0 : return lst;
635 : 0 : }
636 : :
637 : 0 : bool QgsCategorizedSymbolRenderer::accept( QgsStyleEntityVisitorInterface *visitor ) const
638 : : {
639 : 0 : for ( const QgsRendererCategory &cat : mCategories )
640 : : {
641 : 0 : QgsStyleSymbolEntity entity( cat.symbol() );
642 : 0 : if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, cat.value().toString(), cat.label() ) ) )
643 : 0 : return false;
644 : 0 : }
645 : :
646 : 0 : if ( mSourceColorRamp )
647 : : {
648 : 0 : QgsStyleColorRampEntity entity( mSourceColorRamp.get() );
649 : 0 : if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
650 : 0 : return false;
651 : 0 : }
652 : :
653 : 0 : return true;
654 : 0 : }
655 : :
656 : 0 : QgsFeatureRenderer *QgsCategorizedSymbolRenderer::create( QDomElement &element, const QgsReadWriteContext &context )
657 : : {
658 : 0 : QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
659 : 0 : if ( symbolsElem.isNull() )
660 : 0 : return nullptr;
661 : :
662 : 0 : QDomElement catsElem = element.firstChildElement( QStringLiteral( "categories" ) );
663 : 0 : if ( catsElem.isNull() )
664 : 0 : return nullptr;
665 : :
666 : 0 : QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
667 : 0 : QgsCategoryList cats;
668 : :
669 : 0 : QDomElement catElem = catsElem.firstChildElement();
670 : 0 : while ( !catElem.isNull() )
671 : : {
672 : 0 : if ( catElem.tagName() == QLatin1String( "category" ) )
673 : : {
674 : 0 : QVariant value;
675 : 0 : if ( catElem.hasAttribute( QStringLiteral( "value" ) ) )
676 : : {
677 : 0 : value = QVariant( catElem.attribute( QStringLiteral( "value" ) ) );
678 : 0 : }
679 : : else
680 : : {
681 : 0 : QVariantList values;
682 : 0 : QDomElement valElem = catElem.firstChildElement();
683 : 0 : while ( !valElem.isNull() )
684 : : {
685 : 0 : if ( valElem.tagName() == QLatin1String( "val" ) )
686 : : {
687 : 0 : values << QVariant( valElem.attribute( QStringLiteral( "value" ) ) );
688 : 0 : }
689 : 0 : valElem = valElem.nextSiblingElement();
690 : : }
691 : 0 : if ( !values.isEmpty() )
692 : 0 : value = values;
693 : 0 : }
694 : 0 : QString symbolName = catElem.attribute( QStringLiteral( "symbol" ) );
695 : 0 : QString label = catElem.attribute( QStringLiteral( "label" ) );
696 : 0 : bool render = catElem.attribute( QStringLiteral( "render" ) ) != QLatin1String( "false" );
697 : 0 : if ( symbolMap.contains( symbolName ) )
698 : : {
699 : 0 : QgsSymbol *symbol = symbolMap.take( symbolName );
700 : 0 : cats.append( QgsRendererCategory( value, symbol, label, render ) );
701 : 0 : }
702 : 0 : }
703 : 0 : catElem = catElem.nextSiblingElement();
704 : : }
705 : :
706 : 0 : QString attrName = element.attribute( QStringLiteral( "attr" ) );
707 : :
708 : 0 : QgsCategorizedSymbolRenderer *r = new QgsCategorizedSymbolRenderer( attrName, cats );
709 : :
710 : : // delete symbols if there are any more
711 : 0 : QgsSymbolLayerUtils::clearSymbolMap( symbolMap );
712 : :
713 : : // try to load source symbol (optional)
714 : 0 : QDomElement sourceSymbolElem = element.firstChildElement( QStringLiteral( "source-symbol" ) );
715 : 0 : if ( !sourceSymbolElem.isNull() )
716 : : {
717 : 0 : QgsSymbolMap sourceSymbolMap = QgsSymbolLayerUtils::loadSymbols( sourceSymbolElem, context );
718 : 0 : if ( sourceSymbolMap.contains( QStringLiteral( "0" ) ) )
719 : : {
720 : 0 : r->setSourceSymbol( sourceSymbolMap.take( QStringLiteral( "0" ) ) );
721 : 0 : }
722 : 0 : QgsSymbolLayerUtils::clearSymbolMap( sourceSymbolMap );
723 : 0 : }
724 : :
725 : : // try to load color ramp (optional)
726 : 0 : QDomElement sourceColorRampElem = element.firstChildElement( QStringLiteral( "colorramp" ) );
727 : 0 : if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "[source]" ) )
728 : : {
729 : 0 : r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ) );
730 : 0 : }
731 : :
732 : 0 : QDomElement rotationElem = element.firstChildElement( QStringLiteral( "rotation" ) );
733 : 0 : if ( !rotationElem.isNull() && !rotationElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
734 : : {
735 : 0 : for ( const QgsRendererCategory &cat : r->mCategories )
736 : : {
737 : 0 : convertSymbolRotation( cat.symbol(), rotationElem.attribute( QStringLiteral( "field" ) ) );
738 : : }
739 : 0 : if ( r->mSourceSymbol )
740 : : {
741 : 0 : convertSymbolRotation( r->mSourceSymbol.get(), rotationElem.attribute( QStringLiteral( "field" ) ) );
742 : 0 : }
743 : 0 : }
744 : :
745 : 0 : QDomElement sizeScaleElem = element.firstChildElement( QStringLiteral( "sizescale" ) );
746 : 0 : if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
747 : : {
748 : 0 : for ( const QgsRendererCategory &cat : r->mCategories )
749 : : {
750 : 0 : convertSymbolSizeScale( cat.symbol(),
751 : 0 : QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
752 : 0 : sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
753 : : }
754 : 0 : if ( r->mSourceSymbol && r->mSourceSymbol->type() == QgsSymbol::Marker )
755 : : {
756 : 0 : convertSymbolSizeScale( r->mSourceSymbol.get(),
757 : 0 : QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
758 : 0 : sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
759 : 0 : }
760 : 0 : }
761 : :
762 : 0 : QDomElement ddsLegendSizeElem = element.firstChildElement( QStringLiteral( "data-defined-size-legend" ) );
763 : 0 : if ( !ddsLegendSizeElem.isNull() )
764 : : {
765 : 0 : r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
766 : 0 : }
767 : :
768 : : // TODO: symbol levels
769 : 0 : return r;
770 : 0 : }
771 : :
772 : 0 : QDomElement QgsCategorizedSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
773 : : {
774 : : // clazy:skip
775 : 0 : QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
776 : 0 : rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "categorizedSymbol" ) );
777 : 0 : rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
778 : 0 : rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
779 : 0 : rendererElem.setAttribute( QStringLiteral( "attr" ), mAttrName );
780 : :
781 : : // categories
782 : 0 : if ( !mCategories.isEmpty() )
783 : : {
784 : 0 : int i = 0;
785 : 0 : QgsSymbolMap symbols;
786 : 0 : QDomElement catsElem = doc.createElement( QStringLiteral( "categories" ) );
787 : 0 : QgsCategoryList::const_iterator it = mCategories.constBegin();
788 : 0 : for ( ; it != mCategories.constEnd(); ++it )
789 : : {
790 : 0 : const QgsRendererCategory &cat = *it;
791 : 0 : QString symbolName = QString::number( i );
792 : 0 : symbols.insert( symbolName, cat.symbol() );
793 : :
794 : 0 : QDomElement catElem = doc.createElement( QStringLiteral( "category" ) );
795 : 0 : if ( cat.value().type() == QVariant::List )
796 : : {
797 : 0 : const QVariantList list = cat.value().toList();
798 : 0 : for ( const QVariant &v : list )
799 : : {
800 : 0 : QDomElement valueElem = doc.createElement( QStringLiteral( "val" ) );
801 : 0 : valueElem.setAttribute( "value", v.toString() );
802 : 0 : catElem.appendChild( valueElem );
803 : 0 : }
804 : 0 : }
805 : : else
806 : : {
807 : 0 : catElem.setAttribute( QStringLiteral( "value" ), cat.value().toString() );
808 : : }
809 : 0 : catElem.setAttribute( QStringLiteral( "symbol" ), symbolName );
810 : 0 : catElem.setAttribute( QStringLiteral( "label" ), cat.label() );
811 : 0 : catElem.setAttribute( QStringLiteral( "render" ), cat.renderState() ? "true" : "false" );
812 : 0 : catsElem.appendChild( catElem );
813 : 0 : i++;
814 : 0 : }
815 : 0 : rendererElem.appendChild( catsElem );
816 : :
817 : : // save symbols
818 : 0 : QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
819 : 0 : rendererElem.appendChild( symbolsElem );
820 : :
821 : 0 : }
822 : :
823 : : // save source symbol
824 : 0 : if ( mSourceSymbol )
825 : : {
826 : 0 : QgsSymbolMap sourceSymbols;
827 : 0 : sourceSymbols.insert( QStringLiteral( "0" ), mSourceSymbol.get() );
828 : 0 : QDomElement sourceSymbolElem = QgsSymbolLayerUtils::saveSymbols( sourceSymbols, QStringLiteral( "source-symbol" ), doc, context );
829 : 0 : rendererElem.appendChild( sourceSymbolElem );
830 : 0 : }
831 : :
832 : : // save source color ramp
833 : 0 : if ( mSourceColorRamp )
834 : : {
835 : 0 : QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( QStringLiteral( "[source]" ), mSourceColorRamp.get(), doc );
836 : 0 : rendererElem.appendChild( colorRampElem );
837 : 0 : }
838 : :
839 : 0 : QDomElement rotationElem = doc.createElement( QStringLiteral( "rotation" ) );
840 : 0 : rendererElem.appendChild( rotationElem );
841 : :
842 : 0 : QDomElement sizeScaleElem = doc.createElement( QStringLiteral( "sizescale" ) );
843 : 0 : rendererElem.appendChild( sizeScaleElem );
844 : :
845 : 0 : if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect ) )
846 : 0 : mPaintEffect->saveProperties( doc, rendererElem );
847 : :
848 : 0 : if ( !mOrderBy.isEmpty() )
849 : : {
850 : 0 : QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
851 : 0 : mOrderBy.save( orderBy );
852 : 0 : rendererElem.appendChild( orderBy );
853 : 0 : }
854 : 0 : rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
855 : :
856 : 0 : if ( mDataDefinedSizeLegend )
857 : : {
858 : 0 : QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
859 : 0 : mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
860 : 0 : rendererElem.appendChild( ddsLegendElem );
861 : 0 : }
862 : :
863 : 0 : return rendererElem;
864 : 0 : }
865 : :
866 : :
867 : 0 : QgsLegendSymbolList QgsCategorizedSymbolRenderer::baseLegendSymbolItems() const
868 : : {
869 : 0 : QgsLegendSymbolList lst;
870 : 0 : int i = 0;
871 : 0 : for ( const QgsRendererCategory &cat : mCategories )
872 : : {
873 : 0 : lst << QgsLegendSymbolItem( cat.symbol(), cat.label(), QString::number( i++ ), true );
874 : : }
875 : 0 : return lst;
876 : 0 : }
877 : :
878 : 0 : QgsLegendSymbolList QgsCategorizedSymbolRenderer::legendSymbolItems() const
879 : : {
880 : 0 : if ( mDataDefinedSizeLegend && mSourceSymbol && mSourceSymbol->type() == QgsSymbol::Marker )
881 : : {
882 : : // check that all symbols that have the same size expression
883 : 0 : QgsProperty ddSize;
884 : 0 : for ( const QgsRendererCategory &category : mCategories )
885 : : {
886 : 0 : const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( category.symbol() );
887 : 0 : if ( ddSize )
888 : : {
889 : 0 : QgsProperty sSize( symbol->dataDefinedSize() );
890 : 0 : if ( sSize != ddSize )
891 : : {
892 : : // no common size expression
893 : 0 : return baseLegendSymbolItems();
894 : : }
895 : 0 : }
896 : : else
897 : : {
898 : 0 : ddSize = symbol->dataDefinedSize();
899 : : }
900 : : }
901 : :
902 : 0 : if ( ddSize && ddSize.isActive() )
903 : : {
904 : 0 : QgsLegendSymbolList lst;
905 : :
906 : 0 : QgsDataDefinedSizeLegend ddSizeLegend( *mDataDefinedSizeLegend );
907 : 0 : ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSourceSymbol.get() ), ddSize );
908 : 0 : lst += ddSizeLegend.legendSymbolList();
909 : :
910 : 0 : lst += baseLegendSymbolItems();
911 : 0 : return lst;
912 : 0 : }
913 : 0 : }
914 : :
915 : 0 : return baseLegendSymbolItems();
916 : 0 : }
917 : :
918 : 0 : QSet<QString> QgsCategorizedSymbolRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
919 : : {
920 : 0 : QString value = valueForFeature( feature, context ).toString();
921 : 0 : int i = 0;
922 : :
923 : 0 : for ( const QgsRendererCategory &cat : mCategories )
924 : : {
925 : 0 : bool match = false;
926 : 0 : if ( cat.value().type() == QVariant::List )
927 : : {
928 : 0 : const QVariantList list = cat.value().toList();
929 : 0 : for ( const QVariant &v : list )
930 : : {
931 : 0 : if ( value == v )
932 : : {
933 : 0 : match = true;
934 : 0 : break;
935 : : }
936 : : }
937 : 0 : }
938 : : else
939 : : {
940 : 0 : match = value == cat.value();
941 : : }
942 : :
943 : 0 : if ( match )
944 : : {
945 : 0 : if ( cat.renderState() || mCounting )
946 : 0 : return QSet< QString >() << QString::number( i );
947 : : else
948 : 0 : return QSet< QString >();
949 : : }
950 : 0 : i++;
951 : : }
952 : :
953 : 0 : return QSet< QString >();
954 : 0 : }
955 : :
956 : 0 : QgsSymbol *QgsCategorizedSymbolRenderer::sourceSymbol()
957 : : {
958 : 0 : return mSourceSymbol.get();
959 : : }
960 : :
961 : 0 : const QgsSymbol *QgsCategorizedSymbolRenderer::sourceSymbol() const
962 : : {
963 : 0 : return mSourceSymbol.get();
964 : : }
965 : :
966 : 0 : void QgsCategorizedSymbolRenderer::setSourceSymbol( QgsSymbol *sym )
967 : : {
968 : 0 : mSourceSymbol.reset( sym );
969 : 0 : }
970 : :
971 : 0 : QgsColorRamp *QgsCategorizedSymbolRenderer::sourceColorRamp()
972 : : {
973 : 0 : return mSourceColorRamp.get();
974 : : }
975 : :
976 : 0 : const QgsColorRamp *QgsCategorizedSymbolRenderer::sourceColorRamp() const
977 : : {
978 : 0 : return mSourceColorRamp.get();
979 : : }
980 : :
981 : 0 : void QgsCategorizedSymbolRenderer::setSourceColorRamp( QgsColorRamp *ramp )
982 : : {
983 : 0 : mSourceColorRamp.reset( ramp );
984 : 0 : }
985 : :
986 : 0 : void QgsCategorizedSymbolRenderer::updateColorRamp( QgsColorRamp *ramp )
987 : : {
988 : 0 : setSourceColorRamp( ramp );
989 : 0 : double num = mCategories.count() - 1;
990 : 0 : double count = 0;
991 : :
992 : 0 : QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp );
993 : 0 : if ( randomRamp )
994 : : {
995 : : //ramp is a random colors ramp, so inform it of the total number of required colors
996 : : //this allows the ramp to pregenerate a set of visually distinctive colors
997 : 0 : randomRamp->setTotalColorCount( mCategories.count() );
998 : 0 : }
999 : :
1000 : 0 : for ( const QgsRendererCategory &cat : mCategories )
1001 : : {
1002 : 0 : double value = count / num;
1003 : 0 : cat.symbol()->setColor( mSourceColorRamp->color( value ) );
1004 : 0 : count += 1;
1005 : : }
1006 : 0 : }
1007 : :
1008 : 0 : void QgsCategorizedSymbolRenderer::updateSymbols( QgsSymbol *sym )
1009 : : {
1010 : 0 : int i = 0;
1011 : 0 : for ( const QgsRendererCategory &cat : mCategories )
1012 : : {
1013 : 0 : QgsSymbol *symbol = sym->clone();
1014 : 0 : symbol->setColor( cat.symbol()->color() );
1015 : 0 : updateCategorySymbol( i, symbol );
1016 : 0 : ++i;
1017 : : }
1018 : 0 : setSourceSymbol( sym->clone() );
1019 : 0 : }
1020 : :
1021 : 0 : bool QgsCategorizedSymbolRenderer::legendSymbolItemsCheckable() const
1022 : : {
1023 : 0 : return true;
1024 : : }
1025 : :
1026 : 0 : bool QgsCategorizedSymbolRenderer::legendSymbolItemChecked( const QString &key )
1027 : : {
1028 : : bool ok;
1029 : 0 : int index = key.toInt( &ok );
1030 : 0 : if ( ok && index >= 0 && index < mCategories.size() )
1031 : 0 : return mCategories.at( index ).renderState();
1032 : : else
1033 : 0 : return true;
1034 : 0 : }
1035 : :
1036 : 0 : void QgsCategorizedSymbolRenderer::setLegendSymbolItem( const QString &key, QgsSymbol *symbol )
1037 : : {
1038 : : bool ok;
1039 : 0 : int index = key.toInt( &ok );
1040 : 0 : if ( ok )
1041 : 0 : updateCategorySymbol( index, symbol );
1042 : : else
1043 : 0 : delete symbol;
1044 : 0 : }
1045 : :
1046 : 0 : void QgsCategorizedSymbolRenderer::checkLegendSymbolItem( const QString &key, bool state )
1047 : : {
1048 : : bool ok;
1049 : 0 : int index = key.toInt( &ok );
1050 : 0 : if ( ok )
1051 : 0 : updateCategoryRenderState( index, state );
1052 : 0 : }
1053 : :
1054 : 0 : QgsCategorizedSymbolRenderer *QgsCategorizedSymbolRenderer::convertFromRenderer( const QgsFeatureRenderer *renderer, QgsVectorLayer *layer )
1055 : : {
1056 : 0 : std::unique_ptr< QgsCategorizedSymbolRenderer > r;
1057 : 0 : if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
1058 : : {
1059 : 0 : r.reset( static_cast<QgsCategorizedSymbolRenderer *>( renderer->clone() ) );
1060 : 0 : }
1061 : 0 : else if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
1062 : : {
1063 : 0 : const QgsGraduatedSymbolRenderer *graduatedSymbolRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
1064 : 0 : if ( graduatedSymbolRenderer )
1065 : : {
1066 : 0 : r.reset( new QgsCategorizedSymbolRenderer( QString(), QgsCategoryList() ) );
1067 : 0 : if ( graduatedSymbolRenderer->sourceSymbol() )
1068 : 0 : r->setSourceSymbol( graduatedSymbolRenderer->sourceSymbol()->clone() );
1069 : 0 : if ( graduatedSymbolRenderer->sourceColorRamp() )
1070 : : {
1071 : 0 : r->setSourceColorRamp( graduatedSymbolRenderer->sourceColorRamp()->clone() );
1072 : 0 : }
1073 : 0 : r->setClassAttribute( graduatedSymbolRenderer->classAttribute() );
1074 : 0 : }
1075 : 0 : }
1076 : 0 : else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
1077 : : {
1078 : 0 : const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
1079 : 0 : if ( pointDistanceRenderer )
1080 : 0 : r.reset( convertFromRenderer( pointDistanceRenderer->embeddedRenderer() ) );
1081 : 0 : }
1082 : 0 : else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
1083 : : {
1084 : 0 : const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
1085 : 0 : if ( invertedPolygonRenderer )
1086 : 0 : r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
1087 : 0 : }
1088 : 0 : else if ( renderer->type() == QLatin1String( "embeddedSymbol" ) && layer )
1089 : : {
1090 : 0 : const QgsEmbeddedSymbolRenderer *embeddedRenderer = dynamic_cast<const QgsEmbeddedSymbolRenderer *>( renderer );
1091 : 0 : QgsCategoryList categories;
1092 : 0 : QgsFeatureRequest req;
1093 : 0 : req.setFlags( QgsFeatureRequest::EmbeddedSymbols | QgsFeatureRequest::NoGeometry );
1094 : 0 : req.setNoAttributes();
1095 : 0 : QgsFeatureIterator it = layer->getFeatures( req );
1096 : 0 : QgsFeature feature;
1097 : 0 : while ( it.nextFeature( feature ) && categories.size() < 2000 )
1098 : : {
1099 : 0 : if ( feature.embeddedSymbol() )
1100 : 0 : categories.append( QgsRendererCategory( feature.id(), feature.embeddedSymbol()->clone(), QString::number( feature.id() ) ) );
1101 : : }
1102 : 0 : categories.append( QgsRendererCategory( QVariant(), embeddedRenderer->defaultSymbol()->clone(), QString() ) );
1103 : 0 : r.reset( new QgsCategorizedSymbolRenderer( QStringLiteral( "$id" ), categories ) );
1104 : 0 : }
1105 : :
1106 : : // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1107 : : // Could have applied this to specific renderer types (singleSymbol, graduatedSymbol)
1108 : :
1109 : 0 : if ( !r )
1110 : : {
1111 : 0 : r = std::make_unique< QgsCategorizedSymbolRenderer >( QString(), QgsCategoryList() );
1112 : 0 : QgsRenderContext context;
1113 : 0 : QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
1114 : 0 : if ( !symbols.isEmpty() )
1115 : : {
1116 : 0 : r->setSourceSymbol( symbols.at( 0 )->clone() );
1117 : 0 : }
1118 : 0 : }
1119 : :
1120 : 0 : r->setOrderBy( renderer->orderBy() );
1121 : 0 : r->setOrderByEnabled( renderer->orderByEnabled() );
1122 : :
1123 : 0 : return r.release();
1124 : 0 : }
1125 : :
1126 : 0 : void QgsCategorizedSymbolRenderer::setDataDefinedSizeLegend( QgsDataDefinedSizeLegend *settings )
1127 : : {
1128 : 0 : mDataDefinedSizeLegend.reset( settings );
1129 : 0 : }
1130 : :
1131 : 0 : QgsDataDefinedSizeLegend *QgsCategorizedSymbolRenderer::dataDefinedSizeLegend() const
1132 : : {
1133 : 0 : return mDataDefinedSizeLegend.get();
1134 : : }
1135 : :
1136 : 0 : int QgsCategorizedSymbolRenderer::matchToSymbols( QgsStyle *style, const QgsSymbol::SymbolType type, QVariantList &unmatchedCategories, QStringList &unmatchedSymbols, const bool caseSensitive, const bool useTolerantMatch )
1137 : : {
1138 : 0 : if ( !style )
1139 : 0 : return 0;
1140 : :
1141 : 0 : int matched = 0;
1142 : 0 : unmatchedSymbols = style->symbolNames();
1143 : 0 : const QSet< QString > allSymbolNames = qgis::listToSet( unmatchedSymbols );
1144 : :
1145 : 0 : const QRegularExpression tolerantMatchRe( QStringLiteral( "[^\\w\\d ]" ), QRegularExpression::UseUnicodePropertiesOption );
1146 : :
1147 : 0 : for ( int catIdx = 0; catIdx < mCategories.count(); ++catIdx )
1148 : : {
1149 : 0 : const QVariant value = mCategories.at( catIdx ).value();
1150 : 0 : const QString val = value.toString().trimmed();
1151 : 0 : std::unique_ptr< QgsSymbol > symbol( style->symbol( val ) );
1152 : : // case-sensitive match
1153 : 0 : if ( symbol && symbol->type() == type )
1154 : : {
1155 : 0 : matched++;
1156 : 0 : unmatchedSymbols.removeAll( val );
1157 : 0 : updateCategorySymbol( catIdx, symbol.release() );
1158 : 0 : continue;
1159 : : }
1160 : :
1161 : 0 : if ( !caseSensitive || useTolerantMatch )
1162 : : {
1163 : 0 : QString testVal = val;
1164 : 0 : if ( useTolerantMatch )
1165 : 0 : testVal.replace( tolerantMatchRe, QString() );
1166 : :
1167 : 0 : bool foundMatch = false;
1168 : 0 : for ( const QString &name : allSymbolNames )
1169 : : {
1170 : 0 : QString testName = name.trimmed();
1171 : 0 : if ( useTolerantMatch )
1172 : 0 : testName.replace( tolerantMatchRe, QString() );
1173 : :
1174 : 0 : if ( testName == testVal || ( !caseSensitive && testName.trimmed().compare( testVal, Qt::CaseInsensitive ) == 0 ) )
1175 : : {
1176 : : // found a case-insensitive match
1177 : 0 : std::unique_ptr< QgsSymbol > symbol( style->symbol( name ) );
1178 : 0 : if ( symbol && symbol->type() == type )
1179 : : {
1180 : 0 : matched++;
1181 : 0 : unmatchedSymbols.removeAll( name );
1182 : 0 : updateCategorySymbol( catIdx, symbol.release() );
1183 : 0 : foundMatch = true;
1184 : 0 : break;
1185 : : }
1186 : 0 : }
1187 : 0 : }
1188 : 0 : if ( foundMatch )
1189 : 0 : continue;
1190 : 0 : }
1191 : :
1192 : 0 : unmatchedCategories << value;
1193 : 0 : }
1194 : :
1195 : 0 : return matched;
1196 : 0 : }
1197 : :
1198 : 0 : QgsCategoryList QgsCategorizedSymbolRenderer::createCategories( const QList<QVariant> &values, const QgsSymbol *symbol, QgsVectorLayer *layer, const QString &attributeName )
1199 : : {
1200 : 0 : QgsCategoryList cats;
1201 : 0 : QVariantList vals = values;
1202 : : // sort the categories first
1203 : 0 : QgsSymbolLayerUtils::sortVariantList( vals, Qt::AscendingOrder );
1204 : :
1205 : 0 : if ( layer && !attributeName.isNull() )
1206 : : {
1207 : 0 : const QgsFields fields = layer->fields();
1208 : 0 : for ( const QVariant &value : vals )
1209 : : {
1210 : 0 : QgsSymbol *newSymbol = symbol->clone();
1211 : 0 : if ( !value.isNull() )
1212 : : {
1213 : 0 : int fieldIdx = fields.lookupField( attributeName );
1214 : 0 : QString categoryName = value.toString();
1215 : 0 : if ( fieldIdx != -1 )
1216 : : {
1217 : 0 : const QgsField field = fields.at( fieldIdx );
1218 : 0 : const QgsEditorWidgetSetup setup = field.editorWidgetSetup();
1219 : 0 : const QgsFieldFormatter *formatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
1220 : 0 : categoryName = formatter->representValue( layer, fieldIdx, setup.config(), QVariant(), value );
1221 : 0 : }
1222 : 0 : cats.append( QgsRendererCategory( value, newSymbol, categoryName, true ) );
1223 : 0 : }
1224 : : }
1225 : 0 : }
1226 : :
1227 : : // add null (default) value
1228 : 0 : QgsSymbol *newSymbol = symbol->clone();
1229 : 0 : cats.append( QgsRendererCategory( QVariant(), newSymbol, QString(), true ) );
1230 : :
1231 : 0 : return cats;
1232 : 0 : }
1233 : :
|