1 /***************************************************************************
2 qgssinglesymbolrenderer.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
16 #include "qgssinglesymbolrenderer.h"
17
18 #include "qgssymbol.h"
19 #include "qgssymbollayerutils.h"
20
21 #include "qgsdatadefinedsizelegend.h"
22 #include "qgslogger.h"
23 #include "qgsfeature.h"
24 #include "qgsvectorlayer.h"
25 #include "qgssymbollayer.h"
26 #include "qgsogcutils.h"
27 #include "qgspointdisplacementrenderer.h"
28 #include "qgsinvertedpolygonrenderer.h"
29 #include "qgspainteffect.h"
30 #include "qgspainteffectregistry.h"
31 #include "qgsproperty.h"
32 #include "qgsstyleentityvisitor.h"
33
34 #include <QDomDocument>
35 #include <QDomElement>
36
QgsSingleSymbolRenderer(QgsSymbol * symbol)37 QgsSingleSymbolRenderer::QgsSingleSymbolRenderer( QgsSymbol *symbol )
38 : QgsFeatureRenderer( QStringLiteral( "singleSymbol" ) )
39 , mSymbol( symbol )
40 {
41 Q_ASSERT( symbol );
42 }
43
44 QgsSingleSymbolRenderer::~QgsSingleSymbolRenderer() = default;
45
symbolForFeature(const QgsFeature &,QgsRenderContext &) const46 QgsSymbol *QgsSingleSymbolRenderer::symbolForFeature( const QgsFeature &, QgsRenderContext & ) const
47 {
48 return mSymbol.get();
49 }
50
originalSymbolForFeature(const QgsFeature & feature,QgsRenderContext & context) const51 QgsSymbol *QgsSingleSymbolRenderer::originalSymbolForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
52 {
53 Q_UNUSED( context )
54 Q_UNUSED( feature )
55 return mSymbol.get();
56 }
57
startRender(QgsRenderContext & context,const QgsFields & fields)58 void QgsSingleSymbolRenderer::startRender( QgsRenderContext &context, const QgsFields &fields )
59 {
60 QgsFeatureRenderer::startRender( context, fields );
61
62 if ( !mSymbol )
63 return;
64
65 mSymbol->startRender( context, fields );
66 }
67
stopRender(QgsRenderContext & context)68 void QgsSingleSymbolRenderer::stopRender( QgsRenderContext &context )
69 {
70 QgsFeatureRenderer::stopRender( context );
71
72 if ( !mSymbol )
73 return;
74
75 mSymbol->stopRender( context );
76 }
77
usedAttributes(const QgsRenderContext & context) const78 QSet<QString> QgsSingleSymbolRenderer::usedAttributes( const QgsRenderContext &context ) const
79 {
80 QSet<QString> attributes;
81 if ( mSymbol )
82 attributes.unite( mSymbol->usedAttributes( context ) );
83 return attributes;
84 }
85
accept(QgsStyleEntityVisitorInterface * visitor) const86 bool QgsSingleSymbolRenderer::accept( QgsStyleEntityVisitorInterface *visitor ) const
87 {
88 if ( mSymbol )
89 {
90 QgsStyleSymbolEntity entity( mSymbol.get() );
91 return visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) );
92 }
93 return true;
94 }
95
symbol() const96 QgsSymbol *QgsSingleSymbolRenderer::symbol() const
97 {
98 return mSymbol.get();
99 }
100
setSymbol(QgsSymbol * s)101 void QgsSingleSymbolRenderer::setSymbol( QgsSymbol *s )
102 {
103 Q_ASSERT( s );
104 mSymbol.reset( s );
105 }
106
dump() const107 QString QgsSingleSymbolRenderer::dump() const
108 {
109 return mSymbol ? QStringLiteral( "SINGLE: %1" ).arg( mSymbol->dump() ) : QString();
110 }
111
clone() const112 QgsSingleSymbolRenderer *QgsSingleSymbolRenderer::clone() const
113 {
114 QgsSingleSymbolRenderer *r = new QgsSingleSymbolRenderer( mSymbol->clone() );
115 r->setDataDefinedSizeLegend( mDataDefinedSizeLegend ? new QgsDataDefinedSizeLegend( *mDataDefinedSizeLegend ) : nullptr );
116 copyRendererData( r );
117 return r;
118 }
119
toSld(QDomDocument & doc,QDomElement & element,const QgsStringMap & props) const120 void QgsSingleSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
121 {
122 QgsStringMap newProps = props;
123
124 QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
125 element.appendChild( ruleElem );
126
127 QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
128 nameElem.appendChild( doc.createTextNode( QStringLiteral( "Single symbol" ) ) );
129 ruleElem.appendChild( nameElem );
130
131 QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, newProps );
132
133 if ( mSymbol ) mSymbol->toSld( doc, ruleElem, newProps );
134 }
135
symbols(QgsRenderContext & context) const136 QgsSymbolList QgsSingleSymbolRenderer::symbols( QgsRenderContext &context ) const
137 {
138 Q_UNUSED( context )
139 QgsSymbolList lst;
140 lst.append( mSymbol.get() );
141 return lst;
142 }
143
144
create(QDomElement & element,const QgsReadWriteContext & context)145 QgsFeatureRenderer *QgsSingleSymbolRenderer::create( QDomElement &element, const QgsReadWriteContext &context )
146 {
147 QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
148 if ( symbolsElem.isNull() )
149 return nullptr;
150
151 QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
152
153 if ( !symbolMap.contains( QStringLiteral( "0" ) ) )
154 return nullptr;
155
156 QgsSingleSymbolRenderer *r = new QgsSingleSymbolRenderer( symbolMap.take( QStringLiteral( "0" ) ) );
157
158 // delete symbols if there are any more
159 QgsSymbolLayerUtils::clearSymbolMap( symbolMap );
160
161 QDomElement rotationElem = element.firstChildElement( QStringLiteral( "rotation" ) );
162 if ( !rotationElem.isNull() && !rotationElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
163 {
164 convertSymbolRotation( r->mSymbol.get(), rotationElem.attribute( QStringLiteral( "field" ) ) );
165 }
166
167 QDomElement sizeScaleElem = element.firstChildElement( QStringLiteral( "sizescale" ) );
168 if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
169 {
170 convertSymbolSizeScale( r->mSymbol.get(),
171 QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
172 sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
173 }
174
175 QDomElement ddsLegendSizeElem = element.firstChildElement( QStringLiteral( "data-defined-size-legend" ) );
176 if ( !ddsLegendSizeElem.isNull() )
177 {
178 r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
179 }
180
181 // TODO: symbol levels
182 return r;
183 }
184
createFromSld(QDomElement & element,QgsWkbTypes::GeometryType geomType)185 QgsFeatureRenderer *QgsSingleSymbolRenderer::createFromSld( QDomElement &element, QgsWkbTypes::GeometryType geomType )
186 {
187 // XXX this renderer can handle only one Rule!
188
189 // get the first Rule element
190 QDomElement ruleElem = element.firstChildElement( QStringLiteral( "Rule" ) );
191 if ( ruleElem.isNull() )
192 {
193 QgsDebugMsg( QStringLiteral( "no Rule elements found!" ) );
194 return nullptr;
195 }
196
197 QString label, description;
198 QgsSymbolLayerList layers;
199
200 // retrieve the Rule element child nodes
201 QDomElement childElem = ruleElem.firstChildElement();
202 while ( !childElem.isNull() )
203 {
204 if ( childElem.localName() == QLatin1String( "Name" ) )
205 {
206 // <se:Name> tag contains the rule identifier,
207 // so prefer title tag for the label property value
208 if ( label.isEmpty() )
209 label = childElem.firstChild().nodeValue();
210 }
211 else if ( childElem.localName() == QLatin1String( "Description" ) )
212 {
213 // <se:Description> can contains a title and an abstract
214 QDomElement titleElem = childElem.firstChildElement( QStringLiteral( "Title" ) );
215 if ( !titleElem.isNull() )
216 {
217 label = titleElem.firstChild().nodeValue();
218 }
219
220 QDomElement abstractElem = childElem.firstChildElement( QStringLiteral( "Abstract" ) );
221 if ( !abstractElem.isNull() )
222 {
223 description = abstractElem.firstChild().nodeValue();
224 }
225 }
226 else if ( childElem.localName() == QLatin1String( "Abstract" ) )
227 {
228 // <sld:Abstract> (v1.0)
229 description = childElem.firstChild().nodeValue();
230 }
231 else if ( childElem.localName() == QLatin1String( "Title" ) )
232 {
233 // <sld:Title> (v1.0)
234 label = childElem.firstChild().nodeValue();
235 }
236 else if ( childElem.localName().endsWith( QLatin1String( "Symbolizer" ) ) )
237 {
238 // create symbol layers for this symbolizer
239 QgsSymbolLayerUtils::createSymbolLayerListFromSld( childElem, geomType, layers );
240 }
241
242 childElem = childElem.nextSiblingElement();
243 }
244
245 if ( layers.isEmpty() )
246 return nullptr;
247
248 // now create the symbol
249 std::unique_ptr< QgsSymbol > symbol;
250 switch ( geomType )
251 {
252 case QgsWkbTypes::LineGeometry:
253 symbol = qgis::make_unique< QgsLineSymbol >( layers );
254 break;
255
256 case QgsWkbTypes::PolygonGeometry:
257 symbol = qgis::make_unique< QgsFillSymbol >( layers );
258 break;
259
260 case QgsWkbTypes::PointGeometry:
261 symbol = qgis::make_unique< QgsMarkerSymbol >( layers );
262 break;
263
264 default:
265 QgsDebugMsg( QStringLiteral( "invalid geometry type: found %1" ).arg( geomType ) );
266 return nullptr;
267 }
268
269 // and finally return the new renderer
270 return new QgsSingleSymbolRenderer( symbol.release() );
271 }
272
save(QDomDocument & doc,const QgsReadWriteContext & context)273 QDomElement QgsSingleSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
274 {
275 QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
276 rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "singleSymbol" ) );
277 rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
278 rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
279
280 QgsSymbolMap symbols;
281 symbols[QStringLiteral( "0" )] = mSymbol.get();
282 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
283 rendererElem.appendChild( symbolsElem );
284
285 QDomElement rotationElem = doc.createElement( QStringLiteral( "rotation" ) );
286 rendererElem.appendChild( rotationElem );
287
288 QDomElement sizeScaleElem = doc.createElement( QStringLiteral( "sizescale" ) );
289 rendererElem.appendChild( sizeScaleElem );
290
291 if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect ) )
292 mPaintEffect->saveProperties( doc, rendererElem );
293
294 if ( !mOrderBy.isEmpty() )
295 {
296 QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
297 mOrderBy.save( orderBy );
298 rendererElem.appendChild( orderBy );
299 }
300 rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
301
302 if ( mDataDefinedSizeLegend )
303 {
304 QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
305 mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
306 rendererElem.appendChild( ddsLegendElem );
307 }
308
309 return rendererElem;
310 }
311
legendSymbolItems() const312 QgsLegendSymbolList QgsSingleSymbolRenderer::legendSymbolItems() const
313 {
314 if ( mDataDefinedSizeLegend && mSymbol->type() == QgsSymbol::Marker )
315 {
316 const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( mSymbol.get() );
317 QgsProperty sizeDD( symbol->dataDefinedSize() );
318 if ( sizeDD && sizeDD.isActive() )
319 {
320 QgsDataDefinedSizeLegend ddSizeLegend( *mDataDefinedSizeLegend );
321 ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSymbol.get() ), sizeDD );
322 return ddSizeLegend.legendSymbolList();
323 }
324 }
325
326 QgsLegendSymbolList lst;
327 lst << QgsLegendSymbolItem( mSymbol.get(), QString(), QStringLiteral( "0" ) );
328 return lst;
329 }
330
legendKeysForFeature(const QgsFeature & feature,QgsRenderContext & context) const331 QSet< QString > QgsSingleSymbolRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
332 {
333 Q_UNUSED( feature )
334 Q_UNUSED( context )
335 return QSet< QString >() << QStringLiteral( "0" );
336 }
337
setLegendSymbolItem(const QString & key,QgsSymbol * symbol)338 void QgsSingleSymbolRenderer::setLegendSymbolItem( const QString &key, QgsSymbol *symbol )
339 {
340 Q_UNUSED( key )
341 setSymbol( symbol );
342 }
343
convertFromRenderer(const QgsFeatureRenderer * renderer)344 QgsSingleSymbolRenderer *QgsSingleSymbolRenderer::convertFromRenderer( const QgsFeatureRenderer *renderer )
345 {
346 QgsSingleSymbolRenderer *r = nullptr;
347 if ( renderer->type() == QLatin1String( "singleSymbol" ) )
348 {
349 r = dynamic_cast<QgsSingleSymbolRenderer *>( renderer->clone() );
350 }
351 else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
352 {
353 const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
354 if ( pointDistanceRenderer )
355 r = convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
356 }
357 else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
358 {
359 const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
360 if ( invertedPolygonRenderer )
361 r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
362 }
363
364 if ( !r )
365 {
366 QgsRenderContext context;
367 QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
368 if ( !symbols.isEmpty() )
369 {
370 r = new QgsSingleSymbolRenderer( symbols.at( 0 )->clone() );
371 }
372 }
373
374 if ( r )
375 {
376 r->setOrderBy( renderer->orderBy() );
377 r->setOrderByEnabled( renderer->orderByEnabled() );
378 }
379
380 return r;
381 }
382
setDataDefinedSizeLegend(QgsDataDefinedSizeLegend * settings)383 void QgsSingleSymbolRenderer::setDataDefinedSizeLegend( QgsDataDefinedSizeLegend *settings )
384 {
385 mDataDefinedSizeLegend.reset( settings );
386 }
387
dataDefinedSizeLegend() const388 QgsDataDefinedSizeLegend *QgsSingleSymbolRenderer::dataDefinedSizeLegend() const
389 {
390 return mDataDefinedSizeLegend.get();
391 }
392