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