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