1 /***************************************************************************
2 qgsvectorlayerlabeling.cpp
3 ---------------------
4 begin : September 2015
5 copyright : (C) 2015 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 "qgsvectorlayerlabeling.h"
16
17 #include "qgspallabeling.h"
18 #include "qgsrulebasedlabeling.h"
19 #include "qgsvectorlayer.h"
20 #include "qgssymbollayerutils.h"
21 #include "qgssymbollayer.h"
22 #include "qgsmarkersymbollayer.h"
23 #include "qgis.h"
24 #include "qgsstyleentityvisitor.h"
25
26
create(const QDomElement & element,const QgsReadWriteContext & context)27 QgsAbstractVectorLayerLabeling *QgsAbstractVectorLayerLabeling::create( const QDomElement &element, const QgsReadWriteContext &context )
28 {
29 QString type = element.attribute( QStringLiteral( "type" ) );
30 if ( type == QLatin1String( "rule-based" ) )
31 {
32 return QgsRuleBasedLabeling::create( element, context );
33 }
34 else if ( type == QLatin1String( "simple" ) )
35 {
36 return QgsVectorLayerSimpleLabeling::create( element, context );
37 }
38 else
39 {
40 return nullptr;
41 }
42 }
43
accept(QgsStyleEntityVisitorInterface *) const44 bool QgsAbstractVectorLayerLabeling::accept( QgsStyleEntityVisitorInterface * ) const
45 {
46 return true;
47 }
48
provider(QgsVectorLayer * layer) const49 QgsVectorLayerLabelProvider *QgsVectorLayerSimpleLabeling::provider( QgsVectorLayer *layer ) const
50 {
51 return new QgsVectorLayerLabelProvider( layer, QString(), false, mSettings.get() );
52 }
53
QgsVectorLayerSimpleLabeling(const QgsPalLayerSettings & settings)54 QgsVectorLayerSimpleLabeling::QgsVectorLayerSimpleLabeling( const QgsPalLayerSettings &settings )
55 : mSettings( new QgsPalLayerSettings( settings ) )
56 {
57
58 }
59
type() const60 QString QgsVectorLayerSimpleLabeling::type() const
61 {
62 return QStringLiteral( "simple" );
63 }
64
clone() const65 QgsAbstractVectorLayerLabeling *QgsVectorLayerSimpleLabeling::clone() const
66 {
67 return new QgsVectorLayerSimpleLabeling( *mSettings );
68 }
69
save(QDomDocument & doc,const QgsReadWriteContext & context) const70 QDomElement QgsVectorLayerSimpleLabeling::save( QDomDocument &doc, const QgsReadWriteContext &context ) const
71 {
72 QDomElement elem = doc.createElement( QStringLiteral( "labeling" ) );
73 elem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "simple" ) );
74 elem.appendChild( mSettings->writeXml( doc, context ) );
75 return elem;
76 }
77
settings(const QString & providerId) const78 QgsPalLayerSettings QgsVectorLayerSimpleLabeling::settings( const QString &providerId ) const
79 {
80 Q_UNUSED( providerId )
81 return *mSettings;
82 }
83
accept(QgsStyleEntityVisitorInterface * visitor) const84 bool QgsVectorLayerSimpleLabeling::accept( QgsStyleEntityVisitorInterface *visitor ) const
85 {
86 if ( mSettings )
87 {
88 QgsStyleLabelSettingsEntity entity( *mSettings );
89 if ( !visitor->visit( &entity ) )
90 return false;
91 }
92 return true;
93 }
94
requiresAdvancedEffects() const95 bool QgsVectorLayerSimpleLabeling::requiresAdvancedEffects() const
96 {
97 return mSettings->format().containsAdvancedEffects();
98 }
99
create(const QDomElement & element,const QgsReadWriteContext & context)100 QgsVectorLayerSimpleLabeling *QgsVectorLayerSimpleLabeling::create( const QDomElement &element, const QgsReadWriteContext &context )
101 {
102 QDomElement settingsElem = element.firstChildElement( QStringLiteral( "settings" ) );
103 if ( !settingsElem.isNull() )
104 {
105 QgsPalLayerSettings settings;
106 settings.readXml( settingsElem, context );
107 return new QgsVectorLayerSimpleLabeling( settings );
108 }
109
110 return new QgsVectorLayerSimpleLabeling( QgsPalLayerSettings() );
111 }
112
quadOffsetToSldAnchor(QgsPalLayerSettings::QuadrantPosition quadrantPosition)113 QPointF quadOffsetToSldAnchor( QgsPalLayerSettings::QuadrantPosition quadrantPosition )
114 {
115 double quadOffsetX = 0.5, quadOffsetY = 0.5;
116
117 // adjust quadrant offset of labels
118 switch ( quadrantPosition )
119 {
120 case QgsPalLayerSettings::QuadrantAboveLeft:
121 quadOffsetX = 1;
122 quadOffsetY = 0;
123 break;
124 case QgsPalLayerSettings::QuadrantAbove:
125 quadOffsetX = 0.5;
126 quadOffsetY = 0;
127 break;
128 case QgsPalLayerSettings::QuadrantAboveRight:
129 quadOffsetX = 0;
130 quadOffsetY = 0;
131 break;
132 case QgsPalLayerSettings::QuadrantLeft:
133 quadOffsetX = 1;
134 quadOffsetY = 0.5;
135 break;
136 case QgsPalLayerSettings::QuadrantRight:
137 quadOffsetX = 0;
138 quadOffsetY = 0.5;
139 break;
140 case QgsPalLayerSettings::QuadrantBelowLeft:
141 quadOffsetX = 1;
142 quadOffsetY = 1;
143 break;
144 case QgsPalLayerSettings::QuadrantBelow:
145 quadOffsetX = 0.5;
146 quadOffsetY = 1;
147 break;
148 case QgsPalLayerSettings::QuadrantBelowRight:
149 quadOffsetX = 0;
150 quadOffsetY = 1.0;
151 break;
152 case QgsPalLayerSettings::QuadrantOver:
153 break;
154 }
155
156 return QPointF( quadOffsetX, quadOffsetY );
157 }
158
159 /*
160 * This is not a generic function encoder, just enough to encode the label case control functions
161 */
appendSimpleFunction(QDomDocument & doc,QDomElement & parent,const QString & name,const QString & attribute)162 void appendSimpleFunction( QDomDocument &doc, QDomElement &parent, const QString &name, const QString &attribute )
163 {
164 QDomElement function = doc.createElement( QStringLiteral( "ogc:Function" ) );
165 function.setAttribute( QStringLiteral( "name" ), name );
166 parent.appendChild( function );
167 QDomElement property = doc.createElement( QStringLiteral( "ogc:PropertyName" ) );
168 property.appendChild( doc.createTextNode( attribute ) );
169 function.appendChild( property );
170 }
171
backgroundToMarkerLayer(const QgsTextBackgroundSettings & settings)172 std::unique_ptr<QgsMarkerSymbolLayer> backgroundToMarkerLayer( const QgsTextBackgroundSettings &settings )
173 {
174 std::unique_ptr<QgsMarkerSymbolLayer> layer;
175 switch ( settings.type() )
176 {
177 case QgsTextBackgroundSettings::ShapeSVG:
178 {
179 QgsSvgMarkerSymbolLayer *svg = new QgsSvgMarkerSymbolLayer( settings.svgFile() );
180 svg->setStrokeWidth( settings.strokeWidth() );
181 svg->setStrokeWidthUnit( settings.strokeWidthUnit() );
182 layer.reset( svg );
183 break;
184 }
185 case QgsTextBackgroundSettings::ShapeMarkerSymbol:
186 {
187 // just grab the first layer and hope for the best
188 if ( settings.markerSymbol() && settings.markerSymbol()->symbolLayerCount() > 0 )
189 {
190 layer.reset( static_cast< QgsMarkerSymbolLayer * >( settings.markerSymbol()->symbolLayer( 0 )->clone() ) );
191 break;
192 }
193 FALLTHROUGH // not set, just go with the default
194 }
195 case QgsTextBackgroundSettings::ShapeCircle:
196 case QgsTextBackgroundSettings::ShapeEllipse:
197 case QgsTextBackgroundSettings::ShapeRectangle:
198 case QgsTextBackgroundSettings::ShapeSquare:
199 {
200 QgsSimpleMarkerSymbolLayer *marker = new QgsSimpleMarkerSymbolLayer();
201 // default value
202 QgsSimpleMarkerSymbolLayerBase::Shape shape = QgsSimpleMarkerSymbolLayerBase::Diamond;
203 switch ( settings.type() )
204 {
205 case QgsTextBackgroundSettings::ShapeCircle:
206 case QgsTextBackgroundSettings::ShapeEllipse:
207 shape = QgsSimpleMarkerSymbolLayerBase::Circle;
208 break;
209 case QgsTextBackgroundSettings::ShapeRectangle:
210 case QgsTextBackgroundSettings::ShapeSquare:
211 shape = QgsSimpleMarkerSymbolLayerBase::Square;
212 break;
213 case QgsTextBackgroundSettings::ShapeSVG:
214 case QgsTextBackgroundSettings::ShapeMarkerSymbol:
215 break;
216 }
217
218 marker->setShape( shape );
219 marker->setStrokeWidth( settings.strokeWidth() );
220 marker->setStrokeWidthUnit( settings.strokeWidthUnit() );
221 layer.reset( marker );
222 }
223 }
224 layer->setEnabled( true );
225 // a marker does not have a size x and y, just a size (and it should be at least one)
226 QSizeF size = settings.size();
227 layer->setSize( std::max( 1., std::max( size.width(), size.height() ) ) );
228 layer->setSizeUnit( settings.sizeUnit() );
229 // fill and stroke
230 QColor fillColor = settings.fillColor();
231 QColor strokeColor = settings.strokeColor();
232 if ( settings.opacity() < 1 )
233 {
234 int alpha = std::round( settings.opacity() * 255 );
235 fillColor.setAlpha( alpha );
236 strokeColor.setAlpha( alpha );
237 }
238 layer->setFillColor( fillColor );
239 layer->setStrokeColor( strokeColor );
240 // rotation
241 if ( settings.rotationType() == QgsTextBackgroundSettings::RotationFixed )
242 {
243 layer->setAngle( settings.rotation() );
244 }
245 // offset
246 layer->setOffset( settings.offset() );
247 layer->setOffsetUnit( settings.offsetUnit() );
248
249 return layer;
250 }
251
writeTextSymbolizer(QDomNode & parent,QgsPalLayerSettings & settings,const QgsStringMap & props) const252 void QgsAbstractVectorLayerLabeling::writeTextSymbolizer( QDomNode &parent, QgsPalLayerSettings &settings, const QgsStringMap &props ) const
253 {
254 QDomDocument doc = parent.ownerDocument();
255
256 // text symbolizer
257 QDomElement textSymbolizerElement = doc.createElement( QStringLiteral( "se:TextSymbolizer" ) );
258 parent.appendChild( textSymbolizerElement );
259
260 // label
261 QgsTextFormat format = settings.format();
262 QFont font = format.font();
263 QDomElement labelElement = doc.createElement( QStringLiteral( "se:Label" ) );
264 textSymbolizerElement.appendChild( labelElement );
265 if ( settings.isExpression )
266 {
267 labelElement.appendChild( doc.createComment( QStringLiteral( "SE Export for %1 not implemented yet" ).arg( settings.getLabelExpression()->dump() ) ) );
268 labelElement.appendChild( doc.createTextNode( "Placeholder" ) );
269 }
270 else
271 {
272 QgsStringUtils::Capitalization capitalization = format.capitalization();
273 if ( capitalization == QgsStringUtils::MixedCase && font.capitalization() != QFont::MixedCase )
274 capitalization = static_cast< QgsStringUtils::Capitalization >( font.capitalization() );
275 if ( capitalization == QgsStringUtils::AllUppercase )
276 {
277 appendSimpleFunction( doc, labelElement, QStringLiteral( "strToUpperCase" ), settings.fieldName );
278 }
279 else if ( capitalization == QgsStringUtils::AllLowercase )
280 {
281 appendSimpleFunction( doc, labelElement, QStringLiteral( "strToLowerCase" ), settings.fieldName );
282 }
283 else if ( capitalization == QgsStringUtils::ForceFirstLetterToCapital )
284 {
285 appendSimpleFunction( doc, labelElement, QStringLiteral( "strCapitalize" ), settings.fieldName );
286 }
287 else
288 {
289 QDomElement propertyNameElement = doc.createElement( QStringLiteral( "ogc:PropertyName" ) );
290 propertyNameElement.appendChild( doc.createTextNode( settings.fieldName ) );
291 labelElement.appendChild( propertyNameElement );
292 }
293 }
294
295 // font
296 QDomElement fontElement = doc.createElement( QStringLiteral( "se:Font" ) );
297 textSymbolizerElement.appendChild( fontElement );
298 fontElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "font-family" ), font.family() ) );
299 double fontSize = QgsSymbolLayerUtils::rescaleUom( format.size(), format.sizeUnit(), props );
300 fontElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "font-size" ), QString::number( fontSize ) ) );
301 if ( format.font().italic() )
302 {
303 fontElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "font-style" ), QStringLiteral( "italic" ) ) );
304 }
305 if ( format.font().bold() )
306 {
307 fontElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "font-weight" ), QStringLiteral( "bold" ) ) );
308 }
309
310 // label placement
311 QDomElement labelPlacement = doc.createElement( QStringLiteral( "se:LabelPlacement" ) );
312 textSymbolizerElement.appendChild( labelPlacement );
313 double maxDisplacement = 0;
314 double repeatDistance = 0;
315 switch ( settings.placement )
316 {
317 case QgsPalLayerSettings::OverPoint:
318 {
319 QDomElement pointPlacement = doc.createElement( "se:PointPlacement" );
320 labelPlacement.appendChild( pointPlacement );
321 // anchor point
322 QPointF anchor = quadOffsetToSldAnchor( settings.quadOffset );
323 QgsSymbolLayerUtils::createAnchorPointElement( doc, pointPlacement, anchor );
324 // displacement
325 if ( settings.xOffset > 0 || settings.yOffset > 0 )
326 {
327 QgsUnitTypes::RenderUnit offsetUnit = settings.offsetUnits;
328 double dx = QgsSymbolLayerUtils::rescaleUom( settings.xOffset, offsetUnit, props );
329 double dy = QgsSymbolLayerUtils::rescaleUom( settings.yOffset, offsetUnit, props );
330 QgsSymbolLayerUtils::createDisplacementElement( doc, pointPlacement, QPointF( dx, dy ) );
331 }
332 // rotation
333 if ( settings.angleOffset != 0 )
334 {
335 QDomElement rotation = doc.createElement( "se:Rotation" );
336 pointPlacement.appendChild( rotation );
337 rotation.appendChild( doc.createTextNode( QString::number( settings.angleOffset ) ) );
338 }
339 }
340 break;
341 case QgsPalLayerSettings::AroundPoint:
342 case QgsPalLayerSettings::OrderedPositionsAroundPoint:
343 {
344 QDomElement pointPlacement = doc.createElement( "se:PointPlacement" );
345 labelPlacement.appendChild( pointPlacement );
346
347 // SLD cannot do either, but let's do a best effort setting the distance using
348 // anchor point and displacement
349 QgsSymbolLayerUtils::createAnchorPointElement( doc, pointPlacement, QPointF( 0, 0.5 ) );
350 QgsUnitTypes::RenderUnit distUnit = settings.distUnits;
351 double radius = QgsSymbolLayerUtils::rescaleUom( settings.dist, distUnit, props );
352 double offset = std::sqrt( radius * radius / 2 ); // make it start top/right
353 maxDisplacement = radius + 1; // lock the distance
354 QgsSymbolLayerUtils::createDisplacementElement( doc, pointPlacement, QPointF( offset, offset ) );
355 }
356 break;
357 case QgsPalLayerSettings::Horizontal:
358 case QgsPalLayerSettings::Free:
359 case QgsPalLayerSettings::OutsidePolygons:
360 {
361 // still a point placement (for "free" it's a fallback, there is no SLD equivalent)
362 QDomElement pointPlacement = doc.createElement( "se:PointPlacement" );
363 labelPlacement.appendChild( pointPlacement );
364 QgsSymbolLayerUtils::createAnchorPointElement( doc, pointPlacement, QPointF( 0.5, 0.5 ) );
365 QgsUnitTypes::RenderUnit distUnit = settings.distUnits;
366 double dist = QgsSymbolLayerUtils::rescaleUom( settings.dist, distUnit, props );
367 QgsSymbolLayerUtils::createDisplacementElement( doc, pointPlacement, QPointF( 0, dist ) );
368 break;
369 }
370 case QgsPalLayerSettings::Line:
371 case QgsPalLayerSettings::Curved:
372 case QgsPalLayerSettings::PerimeterCurved:
373 {
374 QDomElement linePlacement = doc.createElement( "se:LinePlacement" );
375 labelPlacement.appendChild( linePlacement );
376
377 // perpendicular distance if required
378 if ( settings.dist > 0 )
379 {
380 QgsUnitTypes::RenderUnit distUnit = settings.distUnits;
381 double dist = QgsSymbolLayerUtils::rescaleUom( settings.dist, distUnit, props );
382 QDomElement perpendicular = doc.createElement( "se:PerpendicularOffset" );
383 linePlacement.appendChild( perpendicular );
384 perpendicular.appendChild( doc.createTextNode( qgsDoubleToString( dist, 2 ) ) );
385 }
386
387 // repeat distance if required
388 if ( settings.repeatDistance > 0 )
389 {
390 QDomElement repeat = doc.createElement( "se:Repeat" );
391 linePlacement.appendChild( repeat );
392 repeat.appendChild( doc.createTextNode( QStringLiteral( "true" ) ) );
393 QDomElement gap = doc.createElement( "se:Gap" );
394 linePlacement.appendChild( gap );
395 repeatDistance = QgsSymbolLayerUtils::rescaleUom( settings.repeatDistance, settings.repeatDistanceUnit, props );
396 gap.appendChild( doc.createTextNode( qgsDoubleToString( repeatDistance, 2 ) ) );
397 }
398
399 // always generalized
400 QDomElement generalize = doc.createElement( "se:GeneralizeLine" );
401 linePlacement.appendChild( generalize );
402 generalize.appendChild( doc.createTextNode( QStringLiteral( "true" ) ) );
403 }
404 break;
405 }
406
407 // halo
408 QgsTextBufferSettings buffer = format.buffer();
409 if ( buffer.enabled() )
410 {
411 QDomElement haloElement = doc.createElement( QStringLiteral( "se:Halo" ) );
412 textSymbolizerElement.appendChild( haloElement );
413
414 QDomElement radiusElement = doc.createElement( QStringLiteral( "se:Radius" ) );
415 haloElement.appendChild( radiusElement );
416 // the SLD uses a radius, which is actually half of the link thickness the buffer size specifies
417 double radius = QgsSymbolLayerUtils::rescaleUom( buffer.size(), buffer.sizeUnit(), props ) / 2;
418 radiusElement.appendChild( doc.createTextNode( qgsDoubleToString( radius ) ) );
419
420 QDomElement fillElement = doc.createElement( QStringLiteral( "se:Fill" ) );
421 haloElement.appendChild( fillElement );
422 fillElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "fill" ), buffer.color().name() ) );
423 if ( buffer.opacity() != 1 )
424 {
425 fillElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "fill-opacity" ), QString::number( buffer.opacity() ) ) );
426 }
427 }
428
429 // fill
430 QDomElement fillElement = doc.createElement( QStringLiteral( "se:Fill" ) );
431 textSymbolizerElement.appendChild( fillElement );
432 fillElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "fill" ), format.color().name() ) );
433 if ( format.opacity() != 1 )
434 {
435 fillElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "fill-opacity" ), QString::number( format.opacity() ) ) );
436 }
437
438 // background graphic (not supported by SE 1.1, but supported by the GeoTools ecosystem as an extension)
439 QgsTextBackgroundSettings background = format.background();
440 if ( background.enabled() )
441 {
442 std::unique_ptr<QgsMarkerSymbolLayer> layer = backgroundToMarkerLayer( background );
443 layer->writeSldMarker( doc, textSymbolizerElement, props );
444 }
445
446 // priority and zIndex, the default values are 0 and 5 in qgis (and between 0 and 10),
447 // in the GeoTools ecosystem there is a single priority value set at 1000 by default
448 if ( settings.priority != 5 || settings.zIndex > 0 )
449 {
450 QDomElement priorityElement = doc.createElement( QStringLiteral( "se:Priority" ) );
451 textSymbolizerElement.appendChild( priorityElement );
452 int priority = 500 + 1000 * settings.zIndex + ( settings.priority - 5 ) * 100;
453 if ( settings.priority == 0 && settings.zIndex > 0 )
454 {
455 // small adjustment to make sure labels in z index n+1 are all above level n despite the priority value
456 priority += 1;
457 }
458 priorityElement.appendChild( doc.createTextNode( QString::number( priority ) ) );
459 }
460
461 // vendor options for text appearance
462 if ( font.underline() )
463 {
464 QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "underlineText" ), QStringLiteral( "true" ) );
465 textSymbolizerElement.appendChild( vo );
466 }
467 if ( font.strikeOut() )
468 {
469 QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "strikethroughText" ), QStringLiteral( "true" ) );
470 textSymbolizerElement.appendChild( vo );
471 }
472 // vendor options for text positioning
473 if ( maxDisplacement > 0 )
474 {
475 QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "maxDisplacement" ), qgsDoubleToString( maxDisplacement, 2 ) );
476 textSymbolizerElement.appendChild( vo );
477 }
478 if ( settings.placement == QgsPalLayerSettings::Curved || settings.placement == QgsPalLayerSettings::PerimeterCurved )
479 {
480 QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "followLine" ), QStringLiteral( "true" ) );
481 textSymbolizerElement.appendChild( vo );
482 if ( settings.maxCurvedCharAngleIn > 0 || settings.maxCurvedCharAngleOut > 0 )
483 {
484 // SLD has no notion for this, the GeoTools ecosystem can only do a single angle
485 double angle = std::min( std::fabs( settings.maxCurvedCharAngleIn ), std::fabs( settings.maxCurvedCharAngleOut ) );
486 QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "maxAngleDelta" ), qgsDoubleToString( angle ) );
487 textSymbolizerElement.appendChild( vo );
488 }
489 }
490 if ( repeatDistance > 0 )
491 {
492 QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "repeat" ), qgsDoubleToString( repeatDistance, 2 ) );
493 textSymbolizerElement.appendChild( vo );
494 }
495 // miscellaneous options
496 if ( settings.displayAll )
497 {
498 QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "conflictResolution" ), QStringLiteral( "false" ) );
499 textSymbolizerElement.appendChild( vo );
500 }
501 if ( settings.upsidedownLabels == QgsPalLayerSettings::ShowAll )
502 {
503 QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "forceLeftToRight" ), QStringLiteral( "false" ) );
504 textSymbolizerElement.appendChild( vo );
505 }
506 if ( settings.lineSettings().mergeLines() )
507 {
508 QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "group" ), QStringLiteral( "yes" ) );
509 textSymbolizerElement.appendChild( vo );
510 if ( settings.labelPerPart )
511 {
512 QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "labelAllGroup" ), QStringLiteral( "true" ) );
513 textSymbolizerElement.appendChild( vo );
514 }
515 }
516 // background symbol resize handling
517 if ( background.enabled() )
518 {
519 // enable resizing if needed
520 switch ( background.sizeType() )
521 {
522 case QgsTextBackgroundSettings::SizeBuffer:
523 {
524 QString resizeType;
525 if ( background.type() == QgsTextBackgroundSettings::ShapeRectangle || background.type() == QgsTextBackgroundSettings::ShapeEllipse )
526 {
527 resizeType = QStringLiteral( "stretch" );
528 }
529 else
530 {
531 resizeType = QStringLiteral( "proportional" );
532 }
533 QDomElement voResize = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "graphic-resize" ), resizeType );
534 textSymbolizerElement.appendChild( voResize );
535
536 // now hadle margin
537 QSizeF size = background.size();
538 if ( size.width() > 0 || size.height() > 0 )
539 {
540 double x = QgsSymbolLayerUtils::rescaleUom( size.width(), background.sizeUnit(), props );
541 double y = QgsSymbolLayerUtils::rescaleUom( size.height(), background.sizeUnit(), props );
542 // in case of ellipse qgis pads the size generously to make sure the text is inside the ellipse
543 // the following seems to do the trick and keep visual output similar
544 if ( background.type() == QgsTextBackgroundSettings::ShapeEllipse )
545 {
546 x += fontSize / 2;
547 y += fontSize;
548 }
549 QString resizeSpec = QString( "%1 %2" ).arg( qgsDoubleToString( x, 2 ), qgsDoubleToString( y, 2 ) );
550 QDomElement voMargin = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "graphic-margin" ), resizeSpec );
551 textSymbolizerElement.appendChild( voMargin );
552 }
553 break;
554 }
555 case QgsTextBackgroundSettings::SizeFixed:
556 case QgsTextBackgroundSettings::SizePercent:
557 // nothing to do here
558 break;
559 }
560 }
561 }
562
563
toSld(QDomNode & parent,const QgsStringMap & props) const564 void QgsVectorLayerSimpleLabeling::toSld( QDomNode &parent, const QgsStringMap &props ) const
565 {
566
567 if ( mSettings->drawLabels )
568 {
569 QDomDocument doc = parent.ownerDocument();
570
571 QDomElement ruleElement = doc.createElement( QStringLiteral( "se:Rule" ) );
572 parent.appendChild( ruleElement );
573
574 // scale dependencies
575 if ( mSettings->scaleVisibility )
576 {
577 QgsStringMap scaleProps = QgsStringMap();
578 // tricky here, the max scale is expressed as its denominator, but it's still the max scale
579 // in other words, the smallest scale denominator....
580 scaleProps.insert( "scaleMinDenom", qgsDoubleToString( mSettings->maximumScale ) );
581 scaleProps.insert( "scaleMaxDenom", qgsDoubleToString( mSettings->minimumScale ) );
582 QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElement, scaleProps );
583 }
584
585 writeTextSymbolizer( ruleElement, *mSettings, props );
586 }
587
588
589 }
590
setSettings(QgsPalLayerSettings * settings,const QString & providerId)591 void QgsVectorLayerSimpleLabeling::setSettings( QgsPalLayerSettings *settings, const QString &providerId )
592 {
593 Q_UNUSED( providerId )
594
595 if ( mSettings.get() == settings )
596 return;
597
598 mSettings.reset( settings );
599 }
600