QGIS API Documentation  3.2.0-Bonn (bc43194)
qgspallabeling.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspallabeling.cpp
3  Smart labeling for vector layers
4  -------------------
5  begin : June 2009
6  copyright : (C) Martin Dobias
7  email : wonder dot sk at gmail dot com
8 
9  ***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgspallabeling.h"
19 #include "qgstextlabelfeature.h"
20 #include "qgsunittypes.h"
21 #include "qgsexception.h"
22 
23 #include <list>
24 
25 #include "pal/pal.h"
26 #include "pal/feature.h"
27 #include "pal/layer.h"
28 #include "pal/palexception.h"
29 #include "pal/problem.h"
30 #include "pal/labelposition.h"
31 
32 #include <cmath>
33 
34 #include <QApplication>
35 #include <QByteArray>
36 #include <QString>
37 #include <QFontMetrics>
38 #include <QTime>
39 #include <QPainter>
40 
41 #include "diagram/qgsdiagram.h"
42 #include "qgsdiagramrenderer.h"
43 #include "qgsfontutils.h"
44 #include "qgslabelsearchtree.h"
45 #include "qgsexpression.h"
46 #include "qgslabelingengine.h"
47 #include "qgsvectorlayerlabeling.h"
48 
49 #include "qgslogger.h"
50 #include "qgsvectorlayer.h"
51 #include "qgsvectordataprovider.h"
54 #include "qgsgeometry.h"
55 #include "qgsmarkersymbollayer.h"
56 #include "qgspainting.h"
57 #include "qgsproject.h"
58 #include "qgsproperty.h"
59 #include "qgssymbollayerutils.h"
61 #include "qgscurvepolygon.h"
62 #include <QMessageBox>
63 
64 
65 using namespace pal;
66 
67 // -------------
68 
69 /* ND: Default point label position priority. These are set to match variants of the ideal placement priority described
70  in "Making Maps", Krygier & Wood (2011) (p216),
71  "Elements of Cartography", Robinson et al (1995)
72  and "Designing Better Maps", Brewer (2005) (p76)
73  Note that while they agree on positions 1-4, 5-8 are more contentious so I've selected these placements
74  based on my preferences, and to follow Krygier and Wood's placements more closer. (I'm not going to disagree
75  with Denis Wood on anything cartography related...!)
76 */
77 const QVector< QgsPalLayerSettings::PredefinedPointPosition > QgsPalLayerSettings::DEFAULT_PLACEMENT_ORDER
78 {
87 };
88 //debugging only - don't use these placements by default
89 /* << QgsPalLayerSettings::TopSlightlyLeft
90 << QgsPalLayerSettings::BottomSlightlyLeft;
91 << QgsPalLayerSettings::TopMiddle
92 << QgsPalLayerSettings::BottomMiddle;*/
93 
94 QgsPropertiesDefinition QgsPalLayerSettings::sPropertyDefinitions;
95 
96 void QgsPalLayerSettings::initPropertyDefinitions()
97 {
98  if ( !sPropertyDefinitions.isEmpty() )
99  return;
100 
101  const QString origin = QStringLiteral( "labeling" );
102 
103  sPropertyDefinitions = QgsPropertiesDefinition
104  {
105  { QgsPalLayerSettings::Size, QgsPropertyDefinition( "Size", QObject::tr( "Font size" ), QgsPropertyDefinition::DoublePositive, origin ) },
106  { QgsPalLayerSettings::Bold, QgsPropertyDefinition( "Bold", QObject::tr( "Bold style" ), QgsPropertyDefinition::Boolean, origin ) },
107  { QgsPalLayerSettings::Italic, QgsPropertyDefinition( "Italic", QObject::tr( "Italic style" ), QgsPropertyDefinition::Boolean, origin ) },
108  { QgsPalLayerSettings::Underline, QgsPropertyDefinition( "Underline", QObject::tr( "Draw underline" ), QgsPropertyDefinition::Boolean, origin ) },
109  { QgsPalLayerSettings::Color, QgsPropertyDefinition( "Color", QObject::tr( "Text color" ), QgsPropertyDefinition::ColorNoAlpha, origin ) },
110  { QgsPalLayerSettings::Strikeout, QgsPropertyDefinition( "Strikeout", QObject::tr( "Draw strikeout" ), QgsPropertyDefinition::Boolean, origin ) },
111  {
112  QgsPalLayerSettings::Family, QgsPropertyDefinition( "Family", QgsPropertyDefinition::DataTypeString, QObject::tr( "Font family" ), QObject::tr( "string " ) + QObject::tr( "[<b>family</b>|<b>family[foundry]</b>],<br>"
113  "e.g. Helvetica or Helvetica [Cronyx]" ), origin )
114  },
115  {
116  QgsPalLayerSettings::FontStyle, QgsPropertyDefinition( "FontStyle", QgsPropertyDefinition::DataTypeString, QObject::tr( "Font style" ), QObject::tr( "string " ) + QObject::tr( "[<b>font style name</b>|<b>Ignore</b>],<br>"
117  "e.g. Bold Condensed or Light Italic" ), origin )
118  },
119  { QgsPalLayerSettings::FontSizeUnit, QgsPropertyDefinition( "FontSizeUnit", QObject::tr( "Font size units" ), QgsPropertyDefinition::RenderUnits, origin ) },
120  { QgsPalLayerSettings::FontTransp, QgsPropertyDefinition( "FontTransp", QObject::tr( "Text transparency" ), QgsPropertyDefinition::Opacity, origin ) },
121  { QgsPalLayerSettings::FontOpacity, QgsPropertyDefinition( "FontOpacity", QObject::tr( "Text opacity" ), QgsPropertyDefinition::Opacity, origin ) },
122  { QgsPalLayerSettings::FontCase, QgsPropertyDefinition( "FontCase", QgsPropertyDefinition::DataTypeString, QObject::tr( "Font case" ), QObject::tr( "string " ) + QStringLiteral( "[<b>NoChange</b>|<b>Upper</b>|<br><b>Lower</b>|<b>Capitalize</b>]" ), origin ) },
123  { QgsPalLayerSettings::FontLetterSpacing, QgsPropertyDefinition( "FontLetterSpacing", QObject::tr( "Letter spacing" ), QgsPropertyDefinition::Double, origin ) },
124  { QgsPalLayerSettings::FontWordSpacing, QgsPropertyDefinition( "FontWordSpacing", QObject::tr( "Word spacing" ), QgsPropertyDefinition::Double, origin ) },
125  { QgsPalLayerSettings::FontBlendMode, QgsPropertyDefinition( "FontBlendMode", QObject::tr( "Text blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
126  { QgsPalLayerSettings::MultiLineWrapChar, QgsPropertyDefinition( "MultiLineWrapChar", QObject::tr( "Wrap character" ), QgsPropertyDefinition::String, origin ) },
127  { QgsPalLayerSettings::MultiLineHeight, QgsPropertyDefinition( "MultiLineHeight", QObject::tr( "Line height" ), QgsPropertyDefinition::DoublePositive, origin ) },
128  { QgsPalLayerSettings::MultiLineAlignment, QgsPropertyDefinition( "MultiLineAlignment", QgsPropertyDefinition::DataTypeString, QObject::tr( "Line alignment" ), QObject::tr( "string " ) + "[<b>Left</b>|<b>Center</b>|<b>Right</b>|<b>Follow</b>]", origin ) },
129  { QgsPalLayerSettings::DirSymbDraw, QgsPropertyDefinition( "DirSymbDraw", QObject::tr( "Draw direction symbol" ), QgsPropertyDefinition::Boolean, origin ) },
130  { QgsPalLayerSettings::DirSymbLeft, QgsPropertyDefinition( "DirSymbLeft", QObject::tr( "Left direction symbol" ), QgsPropertyDefinition::String, origin ) },
131  { QgsPalLayerSettings::DirSymbRight, QgsPropertyDefinition( "DirSymbRight", QObject::tr( "Right direction symbol" ), QgsPropertyDefinition::String, origin ) },
132  { QgsPalLayerSettings::DirSymbPlacement, QgsPropertyDefinition( "DirSymbPlacement", QgsPropertyDefinition::DataTypeString, QObject::tr( "Direction symbol placement" ), QObject::tr( "string " ) + "[<b>LeftRight</b>|<b>Above</b>|<b>Below</b>]", origin ) },
133  { QgsPalLayerSettings::DirSymbReverse, QgsPropertyDefinition( "DirSymbReverse", QObject::tr( "Reverse direction symbol" ), QgsPropertyDefinition::Boolean, origin ) },
134  { QgsPalLayerSettings::NumFormat, QgsPropertyDefinition( "NumFormat", QObject::tr( "Format as number" ), QgsPropertyDefinition::Boolean, origin ) },
135  { QgsPalLayerSettings::NumDecimals, QgsPropertyDefinition( "NumDecimals", QObject::tr( "Number of decimal places" ), QgsPropertyDefinition::IntegerPositive, origin ) },
136  { QgsPalLayerSettings::NumPlusSign, QgsPropertyDefinition( "NumPlusSign", QObject::tr( "Draw + sign" ), QgsPropertyDefinition::Boolean, origin ) },
137  { QgsPalLayerSettings::BufferDraw, QgsPropertyDefinition( "BufferDraw", QObject::tr( "Draw buffer" ), QgsPropertyDefinition::Boolean, origin ) },
138  { QgsPalLayerSettings::BufferSize, QgsPropertyDefinition( "BufferSize", QObject::tr( "Symbol size" ), QgsPropertyDefinition::DoublePositive, origin ) },
139  { QgsPalLayerSettings::BufferUnit, QgsPropertyDefinition( "BufferUnit", QObject::tr( "Buffer units" ), QgsPropertyDefinition::RenderUnits, origin ) },
140  { QgsPalLayerSettings::BufferColor, QgsPropertyDefinition( "BufferColor", QObject::tr( "Buffer color" ), QgsPropertyDefinition::ColorNoAlpha, origin ) },
141  { QgsPalLayerSettings::BufferTransp, QgsPropertyDefinition( "BufferTransp", QObject::tr( "Buffer transparency" ), QgsPropertyDefinition::Opacity, origin ) },
142  { QgsPalLayerSettings::BufferOpacity, QgsPropertyDefinition( "BufferOpacity", QObject::tr( "Buffer opacity" ), QgsPropertyDefinition::Opacity, origin ) },
143  { QgsPalLayerSettings::BufferJoinStyle, QgsPropertyDefinition( "BufferJoinStyle", QObject::tr( "Buffer join style" ), QgsPropertyDefinition::PenJoinStyle, origin ) },
144  { QgsPalLayerSettings::BufferBlendMode, QgsPropertyDefinition( "BufferBlendMode", QObject::tr( "Buffer blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
145  { QgsPalLayerSettings::ShapeDraw, QgsPropertyDefinition( "ShapeDraw", QObject::tr( "Draw shape" ), QgsPropertyDefinition::Boolean, origin ) },
146  {
147  QgsPalLayerSettings::ShapeKind, QgsPropertyDefinition( "ShapeKind", QgsPropertyDefinition::DataTypeString, QObject::tr( "Shape type" ), QObject::tr( "string " ) + QStringLiteral( "[<b>Rectangle</b>|<b>Square</b>|<br>"
148  "<b>Ellipse</b>|<b>Circle</b>|<b>SVG</b>]" ), origin )
149  },
150  { QgsPalLayerSettings::ShapeSVGFile, QgsPropertyDefinition( "ShapeSVGFile", QObject::tr( "Shape SVG path" ), QgsPropertyDefinition::SvgPath, origin ) },
151  { QgsPalLayerSettings::ShapeSizeType, QgsPropertyDefinition( "ShapeSizeType", QgsPropertyDefinition::DataTypeString, QObject::tr( "Shape size type" ), QObject::tr( "string " ) + "[<b>Buffer</b>|<b>Fixed</b>]", origin ) },
152  { QgsPalLayerSettings::ShapeSizeX, QgsPropertyDefinition( "ShapeSizeX", QObject::tr( "Shape size (X)" ), QgsPropertyDefinition::Double, origin ) },
153  { QgsPalLayerSettings::ShapeSizeY, QgsPropertyDefinition( "ShapeSizeY", QObject::tr( "Shape size (Y)" ), QgsPropertyDefinition::Double, origin ) },
154  { QgsPalLayerSettings::ShapeSizeUnits, QgsPropertyDefinition( "ShapeSizeUnits", QObject::tr( "Shape size units" ), QgsPropertyDefinition::RenderUnits, origin ) },
155  { QgsPalLayerSettings::ShapeRotationType, QgsPropertyDefinition( "ShapeRotationType", QgsPropertyDefinition::DataTypeString, QObject::tr( "Shape rotation type" ), QObject::tr( "string " ) + "[<b>Sync</b>|<b>Offset</b>|<b>Fixed</b>]", origin ) },
156  { QgsPalLayerSettings::ShapeRotation, QgsPropertyDefinition( "ShapeRotation", QObject::tr( "Shape rotation" ), QgsPropertyDefinition::Rotation, origin ) },
157  { QgsPalLayerSettings::ShapeOffset, QgsPropertyDefinition( "ShapeOffset", QObject::tr( "Shape offset" ), QgsPropertyDefinition::Offset, origin ) },
158  { QgsPalLayerSettings::ShapeOffsetUnits, QgsPropertyDefinition( "ShapeOffsetUnits", QObject::tr( "Shape offset units" ), QgsPropertyDefinition::RenderUnits, origin ) },
159  { QgsPalLayerSettings::ShapeRadii, QgsPropertyDefinition( "ShapeRadii", QObject::tr( "Shape radii" ), QgsPropertyDefinition::Size2D, origin ) },
160  { QgsPalLayerSettings::ShapeRadiiUnits, QgsPropertyDefinition( "ShapeRadiiUnits", QObject::tr( "Symbol radii units" ), QgsPropertyDefinition::RenderUnits, origin ) },
161  { QgsPalLayerSettings::ShapeTransparency, QgsPropertyDefinition( "ShapeTransparency", QObject::tr( "Shape transparency" ), QgsPropertyDefinition::Opacity, origin ) },
162  { QgsPalLayerSettings::ShapeOpacity, QgsPropertyDefinition( "ShapeOpacity", QObject::tr( "Shape opacity" ), QgsPropertyDefinition::Opacity, origin ) },
163  { QgsPalLayerSettings::ShapeBlendMode, QgsPropertyDefinition( "ShapeBlendMode", QObject::tr( "Shape blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
164  { QgsPalLayerSettings::ShapeFillColor, QgsPropertyDefinition( "ShapeFillColor", QObject::tr( "Shape fill color" ), QgsPropertyDefinition::ColorWithAlpha, origin ) },
165  { QgsPalLayerSettings::ShapeStrokeColor, QgsPropertyDefinition( "ShapeBorderColor", QObject::tr( "Shape stroke color" ), QgsPropertyDefinition::ColorWithAlpha, origin ) },
166  { QgsPalLayerSettings::ShapeStrokeWidth, QgsPropertyDefinition( "ShapeBorderWidth", QObject::tr( "Shape stroke width" ), QgsPropertyDefinition::StrokeWidth, origin ) },
167  { QgsPalLayerSettings::ShapeStrokeWidthUnits, QgsPropertyDefinition( "ShapeBorderWidthUnits", QObject::tr( "Shape stroke width units" ), QgsPropertyDefinition::RenderUnits, origin ) },
168  { QgsPalLayerSettings::ShapeJoinStyle, QgsPropertyDefinition( "ShapeJoinStyle", QObject::tr( "Shape join style" ), QgsPropertyDefinition::PenJoinStyle, origin ) },
169  { QgsPalLayerSettings::ShadowDraw, QgsPropertyDefinition( "ShadowDraw", QObject::tr( "Draw shadow" ), QgsPropertyDefinition::Boolean, origin ) },
170  {
171  QgsPalLayerSettings::ShadowUnder, QgsPropertyDefinition( "ShadowUnder", QgsPropertyDefinition::DataTypeString, QObject::tr( "Symbol size" ), QObject::tr( "string " ) + QStringLiteral( "[<b>Lowest</b>|<b>Text</b>|<br>"
172  "<b>Buffer</b>|<b>Background</b>]" ), origin )
173  },
174  { QgsPalLayerSettings::ShadowOffsetAngle, QgsPropertyDefinition( "ShadowOffsetAngle", QObject::tr( "Shadow offset angle" ), QgsPropertyDefinition::Rotation, origin ) },
175  { QgsPalLayerSettings::ShadowOffsetDist, QgsPropertyDefinition( "ShadowOffsetDist", QObject::tr( "Shadow offset distance" ), QgsPropertyDefinition::DoublePositive, origin ) },
176  { QgsPalLayerSettings::ShadowOffsetUnits, QgsPropertyDefinition( "ShadowOffsetUnits", QObject::tr( "Shadow offset units" ), QgsPropertyDefinition::RenderUnits, origin ) },
177  { QgsPalLayerSettings::ShadowRadius, QgsPropertyDefinition( "ShadowRadius", QObject::tr( "Shadow blur radius" ), QgsPropertyDefinition::DoublePositive, origin ) },
178  { QgsPalLayerSettings::ShadowRadiusUnits, QgsPropertyDefinition( "ShadowRadiusUnits", QObject::tr( "Shadow blur units" ), QgsPropertyDefinition::RenderUnits, origin ) },
179  { QgsPalLayerSettings::ShadowTransparency, QgsPropertyDefinition( "ShadowTransparency", QObject::tr( "Shadow transparency" ), QgsPropertyDefinition::Opacity, origin ) },
180  { QgsPalLayerSettings::ShadowOpacity, QgsPropertyDefinition( "ShadowOpacity", QObject::tr( "Shadow opacity" ), QgsPropertyDefinition::Opacity, origin ) },
181  { QgsPalLayerSettings::ShadowScale, QgsPropertyDefinition( "ShadowScale", QObject::tr( "Shadow scale" ), QgsPropertyDefinition::IntegerPositive, origin ) },
182  { QgsPalLayerSettings::ShadowColor, QgsPropertyDefinition( "ShadowColor", QObject::tr( "Shadow color" ), QgsPropertyDefinition::ColorNoAlpha, origin ) },
183  { QgsPalLayerSettings::ShadowBlendMode, QgsPropertyDefinition( "ShadowBlendMode", QObject::tr( "Shadow blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
184 
185  { QgsPalLayerSettings::CentroidWhole, QgsPropertyDefinition( "CentroidWhole", QgsPropertyDefinition::DataTypeString, QObject::tr( "Centroid of whole shape" ), QObject::tr( "string " ) + "[<b>Visible</b>|<b>Whole</b>]", origin ) },
186  {
187  QgsPalLayerSettings::OffsetQuad, QgsPropertyDefinition( "OffsetQuad", QgsPropertyDefinition::DataTypeString, QObject::tr( "Offset quadrant" ), QObject::tr( "int<br>" ) + QStringLiteral( "[<b>0</b>=Above Left|<b>1</b>=Above|<b>2</b>=Above Right|<br>"
188  "<b>3</b>=Left|<b>4</b>=Over|<b>5</b>=Right|<br>"
189  "<b>6</b>=Below Left|<b>7</b>=Below|<b>8</b>=Below Right]" ), origin )
190  },
191  { QgsPalLayerSettings::OffsetXY, QgsPropertyDefinition( "OffsetXY", QObject::tr( "Offset" ), QgsPropertyDefinition::Offset, origin ) },
192  { QgsPalLayerSettings::OffsetUnits, QgsPropertyDefinition( "OffsetUnits", QObject::tr( "Offset units" ), QgsPropertyDefinition::RenderUnits, origin ) },
193  { QgsPalLayerSettings::LabelDistance, QgsPropertyDefinition( "LabelDistance", QObject::tr( "Label distance" ), QgsPropertyDefinition::DoublePositive, origin ) },
194  { QgsPalLayerSettings::DistanceUnits, QgsPropertyDefinition( "DistanceUnits", QObject::tr( "Label distance units" ), QgsPropertyDefinition::RenderUnits, origin ) },
195  { QgsPalLayerSettings::OffsetRotation, QgsPropertyDefinition( "OffsetRotation", QObject::tr( "Offset rotation" ), QgsPropertyDefinition::Rotation, origin ) },
196  { QgsPalLayerSettings::CurvedCharAngleInOut, QgsPropertyDefinition( "CurvedCharAngleInOut", QgsPropertyDefinition::DataTypeString, QObject::tr( "Curved character angles" ), QObject::tr( "double coord [<b>in,out</b> as 20.0-60.0,20.0-95.0]" ), origin ) },
197  { QgsPalLayerSettings::RepeatDistance, QgsPropertyDefinition( "RepeatDistance", QObject::tr( "Repeat distance" ), QgsPropertyDefinition::DoublePositive, origin ) },
198  { QgsPalLayerSettings::RepeatDistanceUnit, QgsPropertyDefinition( "RepeatDistanceUnit", QObject::tr( "Repeat distance unit" ), QgsPropertyDefinition::RenderUnits, origin ) },
199  { QgsPalLayerSettings::Priority, QgsPropertyDefinition( "Priority", QgsPropertyDefinition::DataTypeString, QObject::tr( "Label priority" ), QObject::tr( "double [0.0-10.0]" ), origin ) },
200  { QgsPalLayerSettings::IsObstacle, QgsPropertyDefinition( "IsObstacle", QObject::tr( "Feature is a label obstacle" ), QgsPropertyDefinition::Boolean, origin ) },
201  { QgsPalLayerSettings::ObstacleFactor, QgsPropertyDefinition( "ObstacleFactor", QgsPropertyDefinition::DataTypeNumeric, QObject::tr( "Obstacle factor" ), QObject::tr( "double [0.0-10.0]" ), origin ) },
202  {
203  QgsPalLayerSettings::PredefinedPositionOrder, QgsPropertyDefinition( "PredefinedPositionOrder", QgsPropertyDefinition::DataTypeString, QObject::tr( "Predefined position order" ), QObject::tr( "Comma separated list of placements in order of priority<br>" )
204  + QStringLiteral( "[<b>TL</b>=Top left|<b>TSL</b>=Top, slightly left|<b>T</b>=Top middle|<br>"
205  "<b>TSR</b>=Top, slightly right|<b>TR</b>=Top right|<br>"
206  "<b>L</b>=Left|<b>R</b>=Right|<br>"
207  "<b>BL</b>=Bottom left|<b>BSL</b>=Bottom, slightly left|<b>B</b>=Bottom middle|<br>"
208  "<b>BSR</b>=Bottom, slightly right|<b>BR</b>=Bottom right]" ), origin )
209  },
210  { QgsPalLayerSettings::PositionX, QgsPropertyDefinition( "PositionX", QObject::tr( "Position (X)" ), QgsPropertyDefinition::Double, origin ) },
211  { QgsPalLayerSettings::PositionY, QgsPropertyDefinition( "PositionY", QObject::tr( "Position (Y)" ), QgsPropertyDefinition::Double, origin ) },
212  { QgsPalLayerSettings::Hali, QgsPropertyDefinition( "Hali", QgsPropertyDefinition::DataTypeString, QObject::tr( "Horizontal alignment" ), QObject::tr( "string " ) + "[<b>Left</b>|<b>Center</b>|<b>Right</b>]", origin ) },
213  {
214  QgsPalLayerSettings::Vali, QgsPropertyDefinition( "Vali", QgsPropertyDefinition::DataTypeString, QObject::tr( "Vertical alignment" ), QObject::tr( "string " ) + QStringLiteral( "[<b>Bottom</b>|<b>Base</b>|<br>"
215  "<b>Half</b>|<b>Cap</b>|<b>Top</b>]" ), origin )
216  },
217  { QgsPalLayerSettings::Rotation, QgsPropertyDefinition( "Rotation", QObject::tr( "Label rotation (deprecated)" ), QgsPropertyDefinition::Rotation, origin ) },
218  { QgsPalLayerSettings::LabelRotation, QgsPropertyDefinition( "LabelRotation", QObject::tr( "Label rotation" ), QgsPropertyDefinition::Rotation, origin ) },
219  { QgsPalLayerSettings::ScaleVisibility, QgsPropertyDefinition( "ScaleVisibility", QObject::tr( "Scale based visibility" ), QgsPropertyDefinition::Boolean, origin ) },
220  { QgsPalLayerSettings::MinScale, QgsPropertyDefinition( "MinScale", QObject::tr( "Minimum scale (denominator)" ), QgsPropertyDefinition::Double, origin ) },
221  { QgsPalLayerSettings::MaxScale, QgsPropertyDefinition( "MaxScale", QObject::tr( "Maximum scale (denominator)" ), QgsPropertyDefinition::Double, origin ) },
222  { QgsPalLayerSettings::MinimumScale, QgsPropertyDefinition( "MinimumScale", QObject::tr( "Minimum scale (denominator)" ), QgsPropertyDefinition::Double, origin ) },
223  { QgsPalLayerSettings::MaximumScale, QgsPropertyDefinition( "MaximumScale", QObject::tr( "Maximum scale (denominator)" ), QgsPropertyDefinition::Double, origin ) },
224 
225  { QgsPalLayerSettings::FontLimitPixel, QgsPropertyDefinition( "FontLimitPixel", QObject::tr( "Limit font pixel size" ), QgsPropertyDefinition::Boolean, origin ) },
226  { QgsPalLayerSettings::FontMinPixel, QgsPropertyDefinition( "FontMinPixel", QObject::tr( "Minimum pixel size" ), QgsPropertyDefinition::IntegerPositive, origin ) },
227  { QgsPalLayerSettings::FontMaxPixel, QgsPropertyDefinition( "FontMaxPixel", QObject::tr( "Maximum pixel size" ), QgsPropertyDefinition::IntegerPositive, origin ) },
228  { QgsPalLayerSettings::ZIndex, QgsPropertyDefinition( "ZIndex", QObject::tr( "Label z-index" ), QgsPropertyDefinition::Double, origin ) },
229  { QgsPalLayerSettings::Show, QgsPropertyDefinition( "Show", QObject::tr( "Show label" ), QgsPropertyDefinition::Boolean, origin ) },
230  { QgsPalLayerSettings::AlwaysShow, QgsPropertyDefinition( "AlwaysShow", QObject::tr( "Always show label" ), QgsPropertyDefinition::Boolean, origin ) },
231  };
232 }
233 
235 {
236  initPropertyDefinitions();
237 
238  drawLabels = true;
239  isExpression = false;
240  fieldIndex = 0;
241 
242  previewBkgrdColor = Qt::white;
243  useSubstitutions = false;
244 
245  // text formatting
246  multilineAlign = MultiFollowPlacement;
247  addDirectionSymbol = false;
248  leftDirectionSymbol = QStringLiteral( "<" );
249  rightDirectionSymbol = QStringLiteral( ">" );
250  reverseDirectionSymbol = false;
251  placeDirectionSymbol = SymbolLeftRight;
252  formatNumbers = false;
253  decimals = 3;
254  plusSign = false;
255 
256  // placement
257  placement = AroundPoint;
258  placementFlags = AboveLine | MapOrientation;
259  centroidWhole = false;
260  centroidInside = false;
261  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
262  fitInPolygonOnly = false;
263  quadOffset = QuadrantOver;
264  xOffset = 0;
265  yOffset = 0;
266  offsetUnits = QgsUnitTypes::RenderMillimeters;
267  dist = 0;
269  offsetType = FromPoint;
270  angleOffset = 0;
271  preserveRotation = true;
272  maxCurvedCharAngleIn = 25.0;
273  maxCurvedCharAngleOut = -25.0;
274  priority = 5;
275  repeatDistance = 0;
276  repeatDistanceUnit = QgsUnitTypes::RenderMillimeters;
277 
278  // rendering
279  scaleVisibility = false;
280  maximumScale = 0.0;
281  minimumScale = 0.0;
282  fontLimitPixelSize = false;
283  fontMinPixelSize = 0; //trigger to turn it on by default for map unit labels
284  fontMaxPixelSize = 10000;
285  displayAll = false;
286  upsidedownLabels = Upright;
287 
288  labelPerPart = false;
289  mergeLines = false;
290  minFeatureSize = 0.0;
291  limitNumLabels = false;
292  maxNumLabels = 2000;
293  obstacle = true;
294  obstacleFactor = 1.0;
295  obstacleType = PolygonInterior;
296  zIndex = 0.0;
297 }
298 
300  : fieldIndex( 0 )
301  , mDataDefinedProperties( s.mDataDefinedProperties )
302 {
303  *this = s;
304 }
305 
307 {
308  if ( this == &s )
309  return *this;
310 
311  // copy only permanent stuff
312 
314 
315  // text style
316  fieldName = s.fieldName;
321 
322  // text formatting
323  wrapChar = s.wrapChar;
331  decimals = s.decimals;
332  plusSign = s.plusSign;
333 
334  // placement
335  placement = s.placement;
342  xOffset = s.xOffset;
343  yOffset = s.yOffset;
346  dist = s.dist;
348  distUnits = s.distUnits;
354  priority = s.priority;
358 
359  // rendering
368 
374  obstacle = s.obstacle;
377  zIndex = s.zIndex;
378 
379  mFormat = s.mFormat;
380  mDataDefinedProperties = s.mDataDefinedProperties;
381 
382  return *this;
383 }
384 
385 
387 {
388  // pal layer is deleted internally in PAL
389 
390  delete expression;
391 }
392 
393 
395 {
396  initPropertyDefinitions();
397  return sPropertyDefinitions;
398 }
399 
401 {
402  if ( !expression )
403  {
404  expression = new QgsExpression( fieldName );
405  }
406  return expression;
407 }
408 
409 static Qt::PenJoinStyle _decodePenJoinStyle( const QString &str )
410 {
411  if ( str.compare( QLatin1String( "Miter" ), Qt::CaseInsensitive ) == 0 ) return Qt::MiterJoin;
412  if ( str.compare( QLatin1String( "Round" ), Qt::CaseInsensitive ) == 0 ) return Qt::RoundJoin;
413  return Qt::BevelJoin; // "Bevel"
414 }
415 
416 QString updateDataDefinedString( const QString &value )
417 {
418  // TODO: update or remove this when project settings for labeling are migrated to better XML layout
419  QString newValue = value;
420  if ( !value.isEmpty() && !value.contains( QLatin1String( "~~" ) ) )
421  {
422  QStringList values;
423  values << QStringLiteral( "1" ); // all old-style values are active if not empty
424  values << QStringLiteral( "0" );
425  values << QLatin1String( "" );
426  values << value; // all old-style values are only field names
427  newValue = values.join( QStringLiteral( "~~" ) );
428  }
429 
430  return newValue;
431 }
432 
433 void QgsPalLayerSettings::readOldDataDefinedProperty( QgsVectorLayer *layer, QgsPalLayerSettings::Property p )
434 {
435  QString newPropertyName = "labeling/dataDefined/" + sPropertyDefinitions.value( p ).name();
436  QVariant newPropertyField = layer->customProperty( newPropertyName, QVariant() );
437 
438  if ( !newPropertyField.isValid() )
439  return;
440 
441  QString ddString = newPropertyField.toString();
442 
443  if ( !ddString.isEmpty() && ddString != QLatin1String( "0~~0~~~~" ) )
444  {
445  // TODO: update this when project settings for labeling are migrated to better XML layout
446  QString newStyleString = updateDataDefinedString( ddString );
447  QStringList ddv = newStyleString.split( QStringLiteral( "~~" ) );
448 
449  bool active = ddv.at( 0 ).toInt();
450  if ( ddv.at( 1 ).toInt() )
451  {
452  mDataDefinedProperties.setProperty( p, QgsProperty::fromExpression( ddv.at( 2 ), active ) );
453  }
454  else
455  {
456  mDataDefinedProperties.setProperty( p, QgsProperty::fromField( ddv.at( 3 ), active ) );
457  }
458  }
459  else
460  {
461  // remove unused properties
462  layer->removeCustomProperty( newPropertyName );
463  }
464 }
465 
466 void QgsPalLayerSettings::readOldDataDefinedPropertyMap( QgsVectorLayer *layer, QDomElement *parentElem )
467 {
468  if ( !layer && !parentElem )
469  {
470  return;
471  }
472 
473  QgsPropertiesDefinition::const_iterator i = sPropertyDefinitions.constBegin();
474  for ( ; i != sPropertyDefinitions.constEnd(); ++i )
475  {
476  if ( layer )
477  {
478  // reading from layer's custom properties
479  readOldDataDefinedProperty( layer, static_cast< Property >( i.key() ) );
480  }
481  else if ( parentElem )
482  {
483  // reading from XML
484  QDomElement e = parentElem->firstChildElement( i.value().name() );
485  if ( !e.isNull() )
486  {
487  bool active = e.attribute( QStringLiteral( "active" ) ).compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
488  bool isExpression = e.attribute( QStringLiteral( "useExpr" ) ).compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
489  if ( isExpression )
490  {
491  mDataDefinedProperties.setProperty( i.key(), QgsProperty::fromExpression( e.attribute( QStringLiteral( "expr" ) ), active ) );
492  }
493  else
494  {
495  mDataDefinedProperties.setProperty( i.key(), QgsProperty::fromField( e.attribute( QStringLiteral( "field" ) ), active ) );
496  }
497  }
498  }
499  }
500 }
501 
502 void QgsPalLayerSettings::readFromLayerCustomProperties( QgsVectorLayer *layer )
503 {
504  if ( layer->customProperty( QStringLiteral( "labeling" ) ).toString() != QLatin1String( "pal" ) )
505  {
506  if ( layer->geometryType() == QgsWkbTypes::PointGeometry )
508 
509  // for polygons the "over point" (over centroid) placement is better than the default
510  // "around point" (around centroid) which is more suitable for points
511  if ( layer->geometryType() == QgsWkbTypes::PolygonGeometry )
513 
514  return; // there's no information available
515  }
516 
517  // NOTE: set defaults for newly added properties, for backwards compatibility
518 
519  drawLabels = layer->customProperty( QStringLiteral( "labeling/drawLabels" ), true ).toBool();
520 
521  mFormat.readFromLayer( layer );
522 
523  // text style
524  fieldName = layer->customProperty( QStringLiteral( "labeling/fieldName" ) ).toString();
525  isExpression = layer->customProperty( QStringLiteral( "labeling/isExpression" ) ).toBool();
526  previewBkgrdColor = QColor( layer->customProperty( QStringLiteral( "labeling/previewBkgrdColor" ), QVariant( "#ffffff" ) ).toString() );
527  QDomDocument doc( QStringLiteral( "substitutions" ) );
528  doc.setContent( layer->customProperty( QStringLiteral( "labeling/substitutions" ) ).toString() );
529  QDomElement replacementElem = doc.firstChildElement( QStringLiteral( "substitutions" ) );
530  substitutions.readXml( replacementElem );
531  useSubstitutions = layer->customProperty( QStringLiteral( "labeling/useSubstitutions" ) ).toBool();
532 
533  // text formatting
534  wrapChar = layer->customProperty( QStringLiteral( "labeling/wrapChar" ) ).toString();
535  multilineAlign = static_cast< MultiLineAlign >( layer->customProperty( QStringLiteral( "labeling/multilineAlign" ), QVariant( MultiFollowPlacement ) ).toUInt() );
536  addDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/addDirectionSymbol" ) ).toBool();
537  leftDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/leftDirectionSymbol" ), QVariant( "<" ) ).toString();
538  rightDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/rightDirectionSymbol" ), QVariant( ">" ) ).toString();
539  reverseDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/reverseDirectionSymbol" ) ).toBool();
540  placeDirectionSymbol = static_cast< DirectionSymbols >( layer->customProperty( QStringLiteral( "labeling/placeDirectionSymbol" ), QVariant( SymbolLeftRight ) ).toUInt() );
541  formatNumbers = layer->customProperty( QStringLiteral( "labeling/formatNumbers" ) ).toBool();
542  decimals = layer->customProperty( QStringLiteral( "labeling/decimals" ) ).toInt();
543  plusSign = layer->customProperty( QStringLiteral( "labeling/plussign" ) ).toBool();
544 
545  // placement
546  placement = static_cast< Placement >( layer->customProperty( QStringLiteral( "labeling/placement" ) ).toInt() );
547  placementFlags = layer->customProperty( QStringLiteral( "labeling/placementFlags" ) ).toUInt();
548  centroidWhole = layer->customProperty( QStringLiteral( "labeling/centroidWhole" ), QVariant( false ) ).toBool();
549  centroidInside = layer->customProperty( QStringLiteral( "labeling/centroidInside" ), QVariant( false ) ).toBool();
550  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( layer->customProperty( QStringLiteral( "labeling/predefinedPositionOrder" ) ).toString() );
551  if ( predefinedPositionOrder.isEmpty() )
552  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
553  fitInPolygonOnly = layer->customProperty( QStringLiteral( "labeling/fitInPolygonOnly" ), QVariant( false ) ).toBool();
554  dist = layer->customProperty( QStringLiteral( "labeling/dist" ) ).toDouble();
555  distUnits = layer->customProperty( QStringLiteral( "labeling/distInMapUnits" ) ).toBool() ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters;
556  if ( layer->customProperty( QStringLiteral( "labeling/distMapUnitScale" ) ).toString().isEmpty() )
557  {
558  //fallback to older property
559  double oldMin = layer->customProperty( QStringLiteral( "labeling/distMapUnitMinScale" ), 0.0 ).toDouble();
560  distMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
561  double oldMax = layer->customProperty( QStringLiteral( "labeling/distMapUnitMaxScale" ), 0.0 ).toDouble();
562  distMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
563  }
564  else
565  {
566  distMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/distMapUnitScale" ) ).toString() );
567  }
568  offsetType = static_cast< OffsetType >( layer->customProperty( QStringLiteral( "labeling/offsetType" ), QVariant( FromPoint ) ).toUInt() );
569  quadOffset = static_cast< QuadrantPosition >( layer->customProperty( QStringLiteral( "labeling/quadOffset" ), QVariant( QuadrantOver ) ).toUInt() );
570  xOffset = layer->customProperty( QStringLiteral( "labeling/xOffset" ), QVariant( 0.0 ) ).toDouble();
571  yOffset = layer->customProperty( QStringLiteral( "labeling/yOffset" ), QVariant( 0.0 ) ).toDouble();
572  if ( layer->customProperty( QStringLiteral( "labeling/labelOffsetInMapUnits" ), QVariant( true ) ).toBool() )
574  else
576 
577  if ( layer->customProperty( QStringLiteral( "labeling/labelOffsetMapUnitScale" ) ).toString().isEmpty() )
578  {
579  //fallback to older property
580  double oldMin = layer->customProperty( QStringLiteral( "labeling/labelOffsetMapUnitMinScale" ), 0.0 ).toDouble();
581  labelOffsetMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
582  double oldMax = layer->customProperty( QStringLiteral( "labeling/labelOffsetMapUnitMaxScale" ), 0.0 ).toDouble();
583  labelOffsetMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
584  }
585  else
586  {
587  labelOffsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/labelOffsetMapUnitScale" ) ).toString() );
588  }
589 
590  QVariant tempAngle = layer->customProperty( QStringLiteral( "labeling/angleOffset" ), QVariant() );
591  if ( tempAngle.isValid() )
592  {
593  double oldAngle = layer->customProperty( QStringLiteral( "labeling/angleOffset" ), QVariant( 0.0 ) ).toDouble();
594  angleOffset = std::fmod( 360 - oldAngle, 360.0 );
595  }
596  else
597  {
598  angleOffset = layer->customProperty( QStringLiteral( "labeling/rotationAngle" ), QVariant( 0.0 ) ).toDouble();
599  }
600 
601  preserveRotation = layer->customProperty( QStringLiteral( "labeling/preserveRotation" ), QVariant( true ) ).toBool();
602  maxCurvedCharAngleIn = layer->customProperty( QStringLiteral( "labeling/maxCurvedCharAngleIn" ), QVariant( 25.0 ) ).toDouble();
603  maxCurvedCharAngleOut = layer->customProperty( QStringLiteral( "labeling/maxCurvedCharAngleOut" ), QVariant( -25.0 ) ).toDouble();
604  priority = layer->customProperty( QStringLiteral( "labeling/priority" ) ).toInt();
605  repeatDistance = layer->customProperty( QStringLiteral( "labeling/repeatDistance" ), 0.0 ).toDouble();
606  switch ( layer->customProperty( QStringLiteral( "labeling/repeatDistanceUnit" ), QVariant( 1 ) ).toUInt() )
607  {
608  case 0:
610  break;
611  case 1:
613  break;
614  case 2:
616  break;
617  case 3:
619  break;
620  }
621  if ( layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitScale" ) ).toString().isEmpty() )
622  {
623  //fallback to older property
624  double oldMin = layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitMinScale" ), 0.0 ).toDouble();
625  repeatDistanceMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
626  double oldMax = layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitMaxScale" ), 0.0 ).toDouble();
627  repeatDistanceMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
628  }
629  else
630  {
631  repeatDistanceMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitScale" ) ).toString() );
632  }
633 
634  // rendering
635  double scalemn = layer->customProperty( QStringLiteral( "labeling/scaleMin" ), QVariant( 0 ) ).toDouble();
636  double scalemx = layer->customProperty( QStringLiteral( "labeling/scaleMax" ), QVariant( 0 ) ).toDouble();
637 
638  // fix for scale visibility limits being keyed off of just its values in the past (<2.0)
639  QVariant scalevis = layer->customProperty( QStringLiteral( "labeling/scaleVisibility" ), QVariant() );
640  if ( scalevis.isValid() )
641  {
642  scaleVisibility = scalevis.toBool();
643  maximumScale = scalemn;
644  minimumScale = scalemx;
645  }
646  else if ( scalemn > 0 || scalemx > 0 )
647  {
648  scaleVisibility = true;
649  maximumScale = scalemn;
650  minimumScale = scalemx;
651  }
652  else
653  {
654  // keep scaleMin and scaleMax at new 1.0 defaults (1 and 10000000, were 0 and 0)
655  scaleVisibility = false;
656  }
657 
658 
659  fontLimitPixelSize = layer->customProperty( QStringLiteral( "labeling/fontLimitPixelSize" ), QVariant( false ) ).toBool();
660  fontMinPixelSize = layer->customProperty( QStringLiteral( "labeling/fontMinPixelSize" ), QVariant( 0 ) ).toInt();
661  fontMaxPixelSize = layer->customProperty( QStringLiteral( "labeling/fontMaxPixelSize" ), QVariant( 10000 ) ).toInt();
662  displayAll = layer->customProperty( QStringLiteral( "labeling/displayAll" ), QVariant( false ) ).toBool();
663  upsidedownLabels = static_cast< UpsideDownLabels >( layer->customProperty( QStringLiteral( "labeling/upsidedownLabels" ), QVariant( Upright ) ).toUInt() );
664 
665  labelPerPart = layer->customProperty( QStringLiteral( "labeling/labelPerPart" ) ).toBool();
666  mergeLines = layer->customProperty( QStringLiteral( "labeling/mergeLines" ) ).toBool();
667  minFeatureSize = layer->customProperty( QStringLiteral( "labeling/minFeatureSize" ) ).toDouble();
668  limitNumLabels = layer->customProperty( QStringLiteral( "labeling/limitNumLabels" ), QVariant( false ) ).toBool();
669  maxNumLabels = layer->customProperty( QStringLiteral( "labeling/maxNumLabels" ), QVariant( 2000 ) ).toInt();
670  obstacle = layer->customProperty( QStringLiteral( "labeling/obstacle" ), QVariant( true ) ).toBool();
671  obstacleFactor = layer->customProperty( QStringLiteral( "labeling/obstacleFactor" ), QVariant( 1.0 ) ).toDouble();
672  obstacleType = static_cast< ObstacleType >( layer->customProperty( QStringLiteral( "labeling/obstacleType" ), QVariant( PolygonInterior ) ).toUInt() );
673  zIndex = layer->customProperty( QStringLiteral( "labeling/zIndex" ), QVariant( 0.0 ) ).toDouble();
674 
675  mDataDefinedProperties.clear();
676  if ( layer->customProperty( QStringLiteral( "labeling/ddProperties" ) ).isValid() )
677  {
678  QDomDocument doc( QStringLiteral( "dd" ) );
679  doc.setContent( layer->customProperty( QStringLiteral( "labeling/ddProperties" ) ).toString() );
680  QDomElement elem = doc.firstChildElement( QStringLiteral( "properties" ) );
681  mDataDefinedProperties.readXml( elem, sPropertyDefinitions );
682  }
683  else
684  {
685  // read QGIS 2.x style data defined properties
686  readOldDataDefinedPropertyMap( layer, nullptr );
687  }
688  // upgrade older data defined settings
689  if ( mDataDefinedProperties.isActive( FontTransp ) )
690  {
691  mDataDefinedProperties.setProperty( FontOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( FontTransp ).asExpression() ) ) );
692  mDataDefinedProperties.setProperty( FontTransp, QgsProperty() );
693  }
694  if ( mDataDefinedProperties.isActive( BufferTransp ) )
695  {
696  mDataDefinedProperties.setProperty( BufferOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( BufferTransp ).asExpression() ) ) );
697  mDataDefinedProperties.setProperty( BufferTransp, QgsProperty() );
698  }
699  if ( mDataDefinedProperties.isActive( ShapeTransparency ) )
700  {
701  mDataDefinedProperties.setProperty( ShapeOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( ShapeTransparency ).asExpression() ) ) );
702  mDataDefinedProperties.setProperty( ShapeTransparency, QgsProperty() );
703  }
704  if ( mDataDefinedProperties.isActive( ShadowTransparency ) )
705  {
706  mDataDefinedProperties.setProperty( ShadowOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( ShadowTransparency ).asExpression() ) ) );
707  mDataDefinedProperties.setProperty( ShadowTransparency, QgsProperty() );
708  }
709  if ( mDataDefinedProperties.isActive( Rotation ) )
710  {
711  mDataDefinedProperties.setProperty( LabelRotation, QgsProperty::fromExpression( QStringLiteral( "360 - (%1)" ).arg( mDataDefinedProperties.property( Rotation ).asExpression() ) ) );
712  mDataDefinedProperties.setProperty( Rotation, QgsProperty() );
713  }
714  // older 2.x projects had min/max scale flipped - so change them here.
715  if ( mDataDefinedProperties.isActive( MinScale ) )
716  {
717  mDataDefinedProperties.setProperty( MaximumScale, mDataDefinedProperties.property( MinScale ) );
718  mDataDefinedProperties.setProperty( MinScale, QgsProperty() );
719  }
720  if ( mDataDefinedProperties.isActive( MaxScale ) )
721  {
722  mDataDefinedProperties.setProperty( MinimumScale, mDataDefinedProperties.property( MaxScale ) );
723  mDataDefinedProperties.setProperty( MaxScale, QgsProperty() );
724  }
725 }
726 
727 void QgsPalLayerSettings::readXml( QDomElement &elem, const QgsReadWriteContext &context )
728 {
729  // text style
730  QDomElement textStyleElem = elem.firstChildElement( QStringLiteral( "text-style" ) );
731  fieldName = textStyleElem.attribute( QStringLiteral( "fieldName" ) );
732  isExpression = textStyleElem.attribute( QStringLiteral( "isExpression" ) ).toInt();
733 
734  mFormat.readXml( elem, context );
735  previewBkgrdColor = QColor( textStyleElem.attribute( QStringLiteral( "previewBkgrdColor" ), QStringLiteral( "#ffffff" ) ) );
736  substitutions.readXml( textStyleElem.firstChildElement( QStringLiteral( "substitutions" ) ) );
737  useSubstitutions = textStyleElem.attribute( QStringLiteral( "useSubstitutions" ) ).toInt();
738 
739  // text formatting
740  QDomElement textFormatElem = elem.firstChildElement( QStringLiteral( "text-format" ) );
741  wrapChar = textFormatElem.attribute( QStringLiteral( "wrapChar" ) );
742  multilineAlign = static_cast< MultiLineAlign >( textFormatElem.attribute( QStringLiteral( "multilineAlign" ), QString::number( MultiFollowPlacement ) ).toUInt() );
743  addDirectionSymbol = textFormatElem.attribute( QStringLiteral( "addDirectionSymbol" ) ).toInt();
744  leftDirectionSymbol = textFormatElem.attribute( QStringLiteral( "leftDirectionSymbol" ), QStringLiteral( "<" ) );
745  rightDirectionSymbol = textFormatElem.attribute( QStringLiteral( "rightDirectionSymbol" ), QStringLiteral( ">" ) );
746  reverseDirectionSymbol = textFormatElem.attribute( QStringLiteral( "reverseDirectionSymbol" ) ).toInt();
747  placeDirectionSymbol = static_cast< DirectionSymbols >( textFormatElem.attribute( QStringLiteral( "placeDirectionSymbol" ), QString::number( SymbolLeftRight ) ).toUInt() );
748  formatNumbers = textFormatElem.attribute( QStringLiteral( "formatNumbers" ) ).toInt();
749  decimals = textFormatElem.attribute( QStringLiteral( "decimals" ) ).toInt();
750  plusSign = textFormatElem.attribute( QStringLiteral( "plussign" ) ).toInt();
751 
752  // placement
753  QDomElement placementElem = elem.firstChildElement( QStringLiteral( "placement" ) );
754  placement = static_cast< Placement >( placementElem.attribute( QStringLiteral( "placement" ) ).toInt() );
755  placementFlags = placementElem.attribute( QStringLiteral( "placementFlags" ) ).toUInt();
756  centroidWhole = placementElem.attribute( QStringLiteral( "centroidWhole" ), QStringLiteral( "0" ) ).toInt();
757  centroidInside = placementElem.attribute( QStringLiteral( "centroidInside" ), QStringLiteral( "0" ) ).toInt();
758  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( placementElem.attribute( QStringLiteral( "predefinedPositionOrder" ) ) );
759  if ( predefinedPositionOrder.isEmpty() )
760  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
761  fitInPolygonOnly = placementElem.attribute( QStringLiteral( "fitInPolygonOnly" ), QStringLiteral( "0" ) ).toInt();
762  dist = placementElem.attribute( QStringLiteral( "dist" ) ).toDouble();
763  if ( !placementElem.hasAttribute( QStringLiteral( "distUnits" ) ) )
764  {
765  if ( placementElem.attribute( QStringLiteral( "distInMapUnits" ) ).toInt() )
767  else
769  }
770  else
771  {
772  distUnits = QgsUnitTypes::decodeRenderUnit( placementElem.attribute( QStringLiteral( "distUnits" ) ) );
773  }
774  if ( !placementElem.hasAttribute( QStringLiteral( "distMapUnitScale" ) ) )
775  {
776  //fallback to older property
777  double oldMin = placementElem.attribute( QStringLiteral( "distMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
778  distMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
779  double oldMax = placementElem.attribute( QStringLiteral( "distMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
780  distMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
781  }
782  else
783  {
784  distMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( placementElem.attribute( QStringLiteral( "distMapUnitScale" ) ) );
785  }
786  offsetType = static_cast< OffsetType >( placementElem.attribute( QStringLiteral( "offsetType" ), QString::number( FromPoint ) ).toUInt() );
787  quadOffset = static_cast< QuadrantPosition >( placementElem.attribute( QStringLiteral( "quadOffset" ), QString::number( QuadrantOver ) ).toUInt() );
788  xOffset = placementElem.attribute( QStringLiteral( "xOffset" ), QStringLiteral( "0" ) ).toDouble();
789  yOffset = placementElem.attribute( QStringLiteral( "yOffset" ), QStringLiteral( "0" ) ).toDouble();
790  if ( !placementElem.hasAttribute( QStringLiteral( "offsetUnits" ) ) )
791  {
792  offsetUnits = placementElem.attribute( QStringLiteral( "labelOffsetInMapUnits" ), QStringLiteral( "1" ) ).toInt() ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters;
793  }
794  else
795  {
796  offsetUnits = QgsUnitTypes::decodeRenderUnit( placementElem.attribute( QStringLiteral( "offsetUnits" ) ) );
797  }
798  if ( !placementElem.hasAttribute( QStringLiteral( "labelOffsetMapUnitScale" ) ) )
799  {
800  //fallback to older property
801  double oldMin = placementElem.attribute( QStringLiteral( "labelOffsetMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
802  labelOffsetMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
803  double oldMax = placementElem.attribute( QStringLiteral( "labelOffsetMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
804  labelOffsetMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
805  }
806  else
807  {
808  labelOffsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( placementElem.attribute( QStringLiteral( "labelOffsetMapUnitScale" ) ) );
809  }
810 
811  if ( placementElem.hasAttribute( QStringLiteral( "angleOffset" ) ) )
812  {
813  double oldAngle = placementElem.attribute( QStringLiteral( "angleOffset" ), QStringLiteral( "0" ) ).toDouble();
814  angleOffset = std::fmod( 360 - oldAngle, 360.0 );
815  }
816  else
817  {
818  angleOffset = placementElem.attribute( QStringLiteral( "rotationAngle" ), QStringLiteral( "0" ) ).toDouble();
819  }
820 
821  preserveRotation = placementElem.attribute( QStringLiteral( "preserveRotation" ), QStringLiteral( "1" ) ).toInt();
822  maxCurvedCharAngleIn = placementElem.attribute( QStringLiteral( "maxCurvedCharAngleIn" ), QStringLiteral( "25" ) ).toDouble();
823  maxCurvedCharAngleOut = placementElem.attribute( QStringLiteral( "maxCurvedCharAngleOut" ), QStringLiteral( "-25" ) ).toDouble();
824  priority = placementElem.attribute( QStringLiteral( "priority" ) ).toInt();
825  repeatDistance = placementElem.attribute( QStringLiteral( "repeatDistance" ), QStringLiteral( "0" ) ).toDouble();
826  if ( !placementElem.hasAttribute( QStringLiteral( "repeatDistanceUnits" ) ) )
827  {
828  // upgrade old setting
829  switch ( placementElem.attribute( QStringLiteral( "repeatDistanceUnit" ), QString::number( 1 ) ).toUInt() )
830  {
831  case 0:
833  break;
834  case 1:
836  break;
837  case 2:
839  break;
840  case 3:
842  break;
843  }
844  }
845  else
846  {
847  repeatDistanceUnit = QgsUnitTypes::decodeRenderUnit( placementElem.attribute( QStringLiteral( "repeatDistanceUnits" ) ) );
848  }
849  if ( !placementElem.hasAttribute( QStringLiteral( "repeatDistanceMapUnitScale" ) ) )
850  {
851  //fallback to older property
852  double oldMin = placementElem.attribute( QStringLiteral( "repeatDistanceMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
853  repeatDistanceMapUnitScale.minScale = oldMin != 0 ? 1.0 / oldMin : 0;
854  double oldMax = placementElem.attribute( QStringLiteral( "repeatDistanceMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
855  repeatDistanceMapUnitScale.maxScale = oldMax != 0 ? 1.0 / oldMax : 0;
856  }
857  else
858  {
859  repeatDistanceMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( placementElem.attribute( QStringLiteral( "repeatDistanceMapUnitScale" ) ) );
860  }
861 
862  // rendering
863  QDomElement renderingElem = elem.firstChildElement( QStringLiteral( "rendering" ) );
864 
865  drawLabels = renderingElem.attribute( QStringLiteral( "drawLabels" ), QStringLiteral( "1" ) ).toInt();
866 
867  maximumScale = renderingElem.attribute( QStringLiteral( "scaleMin" ), QStringLiteral( "0" ) ).toDouble();
868  minimumScale = renderingElem.attribute( QStringLiteral( "scaleMax" ), QStringLiteral( "0" ) ).toDouble();
869  scaleVisibility = renderingElem.attribute( QStringLiteral( "scaleVisibility" ) ).toInt();
870 
871  fontLimitPixelSize = renderingElem.attribute( QStringLiteral( "fontLimitPixelSize" ), QStringLiteral( "0" ) ).toInt();
872  fontMinPixelSize = renderingElem.attribute( QStringLiteral( "fontMinPixelSize" ), QStringLiteral( "0" ) ).toInt();
873  fontMaxPixelSize = renderingElem.attribute( QStringLiteral( "fontMaxPixelSize" ), QStringLiteral( "10000" ) ).toInt();
874  displayAll = renderingElem.attribute( QStringLiteral( "displayAll" ), QStringLiteral( "0" ) ).toInt();
875  upsidedownLabels = static_cast< UpsideDownLabels >( renderingElem.attribute( QStringLiteral( "upsidedownLabels" ), QString::number( Upright ) ).toUInt() );
876 
877  labelPerPart = renderingElem.attribute( QStringLiteral( "labelPerPart" ) ).toInt();
878  mergeLines = renderingElem.attribute( QStringLiteral( "mergeLines" ) ).toInt();
879  minFeatureSize = renderingElem.attribute( QStringLiteral( "minFeatureSize" ) ).toDouble();
880  limitNumLabels = renderingElem.attribute( QStringLiteral( "limitNumLabels" ), QStringLiteral( "0" ) ).toInt();
881  maxNumLabels = renderingElem.attribute( QStringLiteral( "maxNumLabels" ), QStringLiteral( "2000" ) ).toInt();
882  obstacle = renderingElem.attribute( QStringLiteral( "obstacle" ), QStringLiteral( "1" ) ).toInt();
883  obstacleFactor = renderingElem.attribute( QStringLiteral( "obstacleFactor" ), QStringLiteral( "1" ) ).toDouble();
884  obstacleType = static_cast< ObstacleType >( renderingElem.attribute( QStringLiteral( "obstacleType" ), QString::number( PolygonInterior ) ).toUInt() );
885  zIndex = renderingElem.attribute( QStringLiteral( "zIndex" ), QStringLiteral( "0.0" ) ).toDouble();
886 
887  QDomElement ddElem = elem.firstChildElement( QStringLiteral( "dd_properties" ) );
888  if ( !ddElem.isNull() )
889  {
890  mDataDefinedProperties.readXml( ddElem, sPropertyDefinitions );
891  }
892  else
893  {
894  // upgrade 2.x style dd project
895  mDataDefinedProperties.clear();
896  QDomElement ddElem = elem.firstChildElement( QStringLiteral( "data-defined" ) );
897  readOldDataDefinedPropertyMap( nullptr, &ddElem );
898  }
899  // upgrade older data defined settings
900  if ( mDataDefinedProperties.isActive( FontTransp ) )
901  {
902  mDataDefinedProperties.setProperty( FontOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( FontTransp ).asExpression() ) ) );
903  mDataDefinedProperties.setProperty( FontTransp, QgsProperty() );
904  }
905  if ( mDataDefinedProperties.isActive( BufferTransp ) )
906  {
907  mDataDefinedProperties.setProperty( BufferOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( BufferTransp ).asExpression() ) ) );
908  mDataDefinedProperties.setProperty( BufferTransp, QgsProperty() );
909  }
910  if ( mDataDefinedProperties.isActive( ShapeTransparency ) )
911  {
912  mDataDefinedProperties.setProperty( ShapeOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( ShapeTransparency ).asExpression() ) ) );
913  mDataDefinedProperties.setProperty( ShapeTransparency, QgsProperty() );
914  }
915  if ( mDataDefinedProperties.isActive( ShadowTransparency ) )
916  {
917  mDataDefinedProperties.setProperty( ShadowOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( ShadowTransparency ).asExpression() ) ) );
918  mDataDefinedProperties.setProperty( ShadowTransparency, QgsProperty() );
919  }
920  if ( mDataDefinedProperties.isActive( Rotation ) )
921  {
922  mDataDefinedProperties.setProperty( LabelRotation, QgsProperty::fromExpression( QStringLiteral( "360 - (%1)" ).arg( mDataDefinedProperties.property( Rotation ).asExpression() ) ) );
923  mDataDefinedProperties.setProperty( Rotation, QgsProperty() );
924  }
925  // older 2.x projects had min/max scale flipped - so change them here.
926  if ( mDataDefinedProperties.isActive( MinScale ) )
927  {
928  mDataDefinedProperties.setProperty( MaximumScale, mDataDefinedProperties.property( MinScale ) );
929  mDataDefinedProperties.setProperty( MinScale, QgsProperty() );
930  }
931  if ( mDataDefinedProperties.isActive( MaxScale ) )
932  {
933  mDataDefinedProperties.setProperty( MinimumScale, mDataDefinedProperties.property( MaxScale ) );
934  mDataDefinedProperties.setProperty( MaxScale, QgsProperty() );
935  }
936 }
937 
938 
939 
940 QDomElement QgsPalLayerSettings::writeXml( QDomDocument &doc, const QgsReadWriteContext &context )
941 {
942  QDomElement textStyleElem = mFormat.writeXml( doc, context );
943 
944  // text style
945  textStyleElem.setAttribute( QStringLiteral( "fieldName" ), fieldName );
946  textStyleElem.setAttribute( QStringLiteral( "isExpression" ), isExpression );
947  textStyleElem.setAttribute( QStringLiteral( "previewBkgrdColor" ), previewBkgrdColor.name() );
948  QDomElement replacementElem = doc.createElement( QStringLiteral( "substitutions" ) );
949  substitutions.writeXml( replacementElem, doc );
950  textStyleElem.appendChild( replacementElem );
951  textStyleElem.setAttribute( QStringLiteral( "useSubstitutions" ), useSubstitutions );
952 
953  // text formatting
954  QDomElement textFormatElem = doc.createElement( QStringLiteral( "text-format" ) );
955  textFormatElem.setAttribute( QStringLiteral( "wrapChar" ), wrapChar );
956  textFormatElem.setAttribute( QStringLiteral( "multilineAlign" ), static_cast< unsigned int >( multilineAlign ) );
957  textFormatElem.setAttribute( QStringLiteral( "addDirectionSymbol" ), addDirectionSymbol );
958  textFormatElem.setAttribute( QStringLiteral( "leftDirectionSymbol" ), leftDirectionSymbol );
959  textFormatElem.setAttribute( QStringLiteral( "rightDirectionSymbol" ), rightDirectionSymbol );
960  textFormatElem.setAttribute( QStringLiteral( "reverseDirectionSymbol" ), reverseDirectionSymbol );
961  textFormatElem.setAttribute( QStringLiteral( "placeDirectionSymbol" ), static_cast< unsigned int >( placeDirectionSymbol ) );
962  textFormatElem.setAttribute( QStringLiteral( "formatNumbers" ), formatNumbers );
963  textFormatElem.setAttribute( QStringLiteral( "decimals" ), decimals );
964  textFormatElem.setAttribute( QStringLiteral( "plussign" ), plusSign );
965 
966  // placement
967  QDomElement placementElem = doc.createElement( QStringLiteral( "placement" ) );
968  placementElem.setAttribute( QStringLiteral( "placement" ), placement );
969  placementElem.setAttribute( QStringLiteral( "placementFlags" ), static_cast< unsigned int >( placementFlags ) );
970  placementElem.setAttribute( QStringLiteral( "centroidWhole" ), centroidWhole );
971  placementElem.setAttribute( QStringLiteral( "centroidInside" ), centroidInside );
972  placementElem.setAttribute( QStringLiteral( "predefinedPositionOrder" ), QgsLabelingUtils::encodePredefinedPositionOrder( predefinedPositionOrder ) );
973  placementElem.setAttribute( QStringLiteral( "fitInPolygonOnly" ), fitInPolygonOnly );
974  placementElem.setAttribute( QStringLiteral( "dist" ), dist );
975  placementElem.setAttribute( QStringLiteral( "distUnits" ), QgsUnitTypes::encodeUnit( distUnits ) );
976  placementElem.setAttribute( QStringLiteral( "distMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( distMapUnitScale ) );
977  placementElem.setAttribute( QStringLiteral( "offsetType" ), static_cast< unsigned int >( offsetType ) );
978  placementElem.setAttribute( QStringLiteral( "quadOffset" ), static_cast< unsigned int >( quadOffset ) );
979  placementElem.setAttribute( QStringLiteral( "xOffset" ), xOffset );
980  placementElem.setAttribute( QStringLiteral( "yOffset" ), yOffset );
981  placementElem.setAttribute( QStringLiteral( "offsetUnits" ), QgsUnitTypes::encodeUnit( offsetUnits ) );
982  placementElem.setAttribute( QStringLiteral( "labelOffsetMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( labelOffsetMapUnitScale ) );
983  placementElem.setAttribute( QStringLiteral( "rotationAngle" ), angleOffset );
984  placementElem.setAttribute( QStringLiteral( "preserveRotation" ), preserveRotation );
985  placementElem.setAttribute( QStringLiteral( "maxCurvedCharAngleIn" ), maxCurvedCharAngleIn );
986  placementElem.setAttribute( QStringLiteral( "maxCurvedCharAngleOut" ), maxCurvedCharAngleOut );
987  placementElem.setAttribute( QStringLiteral( "priority" ), priority );
988  placementElem.setAttribute( QStringLiteral( "repeatDistance" ), repeatDistance );
989  placementElem.setAttribute( QStringLiteral( "repeatDistanceUnits" ), QgsUnitTypes::encodeUnit( repeatDistanceUnit ) );
990  placementElem.setAttribute( QStringLiteral( "repeatDistanceMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( repeatDistanceMapUnitScale ) );
991 
992  // rendering
993  QDomElement renderingElem = doc.createElement( QStringLiteral( "rendering" ) );
994  renderingElem.setAttribute( QStringLiteral( "drawLabels" ), drawLabels );
995  renderingElem.setAttribute( QStringLiteral( "scaleVisibility" ), scaleVisibility );
996  renderingElem.setAttribute( QStringLiteral( "scaleMin" ), maximumScale );
997  renderingElem.setAttribute( QStringLiteral( "scaleMax" ), minimumScale );
998  renderingElem.setAttribute( QStringLiteral( "fontLimitPixelSize" ), fontLimitPixelSize );
999  renderingElem.setAttribute( QStringLiteral( "fontMinPixelSize" ), fontMinPixelSize );
1000  renderingElem.setAttribute( QStringLiteral( "fontMaxPixelSize" ), fontMaxPixelSize );
1001  renderingElem.setAttribute( QStringLiteral( "displayAll" ), displayAll );
1002  renderingElem.setAttribute( QStringLiteral( "upsidedownLabels" ), static_cast< unsigned int >( upsidedownLabels ) );
1003 
1004  renderingElem.setAttribute( QStringLiteral( "labelPerPart" ), labelPerPart );
1005  renderingElem.setAttribute( QStringLiteral( "mergeLines" ), mergeLines );
1006  renderingElem.setAttribute( QStringLiteral( "minFeatureSize" ), minFeatureSize );
1007  renderingElem.setAttribute( QStringLiteral( "limitNumLabels" ), limitNumLabels );
1008  renderingElem.setAttribute( QStringLiteral( "maxNumLabels" ), maxNumLabels );
1009  renderingElem.setAttribute( QStringLiteral( "obstacle" ), obstacle );
1010  renderingElem.setAttribute( QStringLiteral( "obstacleFactor" ), obstacleFactor );
1011  renderingElem.setAttribute( QStringLiteral( "obstacleType" ), static_cast< unsigned int >( obstacleType ) );
1012  renderingElem.setAttribute( QStringLiteral( "zIndex" ), zIndex );
1013 
1014  QDomElement ddElem = doc.createElement( QStringLiteral( "dd_properties" ) );
1015  mDataDefinedProperties.writeXml( ddElem, sPropertyDefinitions );
1016 
1017  QDomElement elem = doc.createElement( QStringLiteral( "settings" ) );
1018  elem.appendChild( textStyleElem );
1019  elem.appendChild( textFormatElem );
1020  elem.appendChild( placementElem );
1021  elem.appendChild( renderingElem );
1022  elem.appendChild( ddElem );
1023  return elem;
1024 }
1025 
1026 bool QgsPalLayerSettings::checkMinimumSizeMM( const QgsRenderContext &ct, const QgsGeometry &geom, double minSize ) const
1027 {
1028  return QgsPalLabeling::checkMinimumSizeMM( ct, geom, minSize );
1029 }
1030 
1031 void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, QString text, double &labelX, double &labelY, QgsFeature *f, QgsRenderContext *context )
1032 {
1033  if ( !fm || !f )
1034  {
1035  return;
1036  }
1037 
1038  //try to keep < 2.12 API - handle no passed render context
1039  std::unique_ptr< QgsRenderContext > scopedRc;
1040  if ( !context )
1041  {
1042  scopedRc.reset( new QgsRenderContext() );
1043  if ( f )
1044  scopedRc->expressionContext().setFeature( *f );
1045  }
1046  QgsRenderContext *rc = context ? context : scopedRc.get();
1047 
1048  QString wrapchr = wrapChar;
1049  double multilineH = mFormat.lineHeight();
1050 
1051  bool addDirSymb = addDirectionSymbol;
1052  QString leftDirSymb = leftDirectionSymbol;
1053  QString rightDirSymb = rightDirectionSymbol;
1055 
1056  if ( f == mCurFeat ) // called internally, use any stored data defined values
1057  {
1058  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
1059  {
1060  wrapchr = dataDefinedValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
1061  }
1062 
1063  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
1064  {
1065  multilineH = dataDefinedValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
1066  }
1067 
1068  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
1069  {
1070  addDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
1071  }
1072 
1073  if ( addDirSymb )
1074  {
1075 
1076  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
1077  {
1078  leftDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
1079  }
1080  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbRight ) )
1081  {
1082  rightDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
1083  }
1084 
1085  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
1086  {
1087  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( dataDefinedValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt() );
1088  }
1089 
1090  }
1091 
1092  }
1093  else // called externally with passed-in feature, evaluate data defined
1094  {
1096  wrapchr = mDataDefinedProperties.value( QgsPalLayerSettings::MultiLineWrapChar, rc->expressionContext(), wrapchr ).toString();
1097 
1098  rc->expressionContext().setOriginalValueVariable( multilineH );
1099  multilineH = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::MultiLineHeight, rc->expressionContext(), multilineH );
1100 
1101  rc->expressionContext().setOriginalValueVariable( addDirSymb );
1102  addDirSymb = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::DirSymbDraw, rc->expressionContext(), addDirSymb );
1103 
1104  if ( addDirSymb ) // don't do extra evaluations if not adding a direction symbol
1105  {
1106  rc->expressionContext().setOriginalValueVariable( leftDirSymb );
1107  leftDirSymb = mDataDefinedProperties.value( QgsPalLayerSettings::DirSymbLeft, rc->expressionContext(), leftDirSymb ).toString();
1108 
1109  rc->expressionContext().setOriginalValueVariable( rightDirSymb );
1110  rightDirSymb = mDataDefinedProperties.value( QgsPalLayerSettings::DirSymbLeft, rc->expressionContext(), rightDirSymb ).toString();
1111 
1112  rc->expressionContext().setOriginalValueVariable( static_cast< int >( placeDirSymb ) );
1113  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( mDataDefinedProperties.valueAsInt( QgsPalLayerSettings::DirSymbPlacement, rc->expressionContext(), placeDirSymb ) );
1114  }
1115 
1116  }
1117 
1118  if ( wrapchr.isEmpty() )
1119  {
1120  wrapchr = QStringLiteral( "\n" ); // default to new line delimiter
1121  }
1122 
1123  //consider the space needed for the direction symbol
1124  if ( addDirSymb && placement == QgsPalLayerSettings::Line
1125  && ( !leftDirSymb.isEmpty() || !rightDirSymb.isEmpty() ) )
1126  {
1127  QString dirSym = leftDirSymb;
1128 
1129  if ( fm->width( rightDirSymb ) > fm->width( dirSym ) )
1130  dirSym = rightDirSymb;
1131 
1132  if ( placeDirSymb == QgsPalLayerSettings::SymbolLeftRight )
1133  {
1134  text.append( dirSym );
1135  }
1136  else
1137  {
1138  text.prepend( dirSym + QStringLiteral( "\n" ) ); // SymbolAbove or SymbolBelow
1139  }
1140  }
1141 
1142  double w = 0.0, h = 0.0;
1143  QStringList multiLineSplit = QgsPalLabeling::splitToLines( text, wrapchr );
1144  int lines = multiLineSplit.size();
1145 
1146  double labelHeight = fm->ascent() + fm->descent(); // ignore +1 for baseline
1147 
1148  h += fm->height() + static_cast< double >( ( lines - 1 ) * labelHeight * multilineH );
1149 
1150  for ( int i = 0; i < lines; ++i )
1151  {
1152  double width = fm->width( multiLineSplit.at( i ) );
1153  if ( width > w )
1154  {
1155  w = width;
1156  }
1157  }
1158 
1159 #if 0 // XXX strk
1160  QgsPointXY ptSize = xform->toMapCoordinatesF( w, h );
1161  labelX = std::fabs( ptSize.x() - ptZero.x() );
1162  labelY = std::fabs( ptSize.y() - ptZero.y() );
1163 #else
1164  double uPP = xform->mapUnitsPerPixel();
1165  labelX = w * uPP;
1166  labelY = h * uPP;
1167 #endif
1168 }
1169 
1171 {
1172  // either used in QgsPalLabeling (palLayer is set) or in QgsLabelingEngine (labelFeature is set)
1173  Q_ASSERT( labelFeature );
1174 
1175  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
1176  mCurFeat = &f;
1177 
1178  // data defined is obstacle? calculate this first, to avoid wasting time working with obstacles we don't require
1179  bool isObstacle = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::IsObstacle, context.expressionContext(), obstacle ); // default to layer default
1180 
1181  if ( !drawLabels )
1182  {
1183  if ( isObstacle )
1184  {
1185  registerObstacleFeature( f, context, labelFeature, obstacleGeometry );
1186  }
1187  return;
1188  }
1189 
1190 // mCurFields = &layer->fields();
1191 
1192  // store data defined-derived values for later adding to label feature for use during rendering
1193  dataDefinedValues.clear();
1194 
1195  // data defined show label? defaults to show label if not set
1196  context.expressionContext().setOriginalValueVariable( true );
1197  if ( !mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Show, context.expressionContext(), true ) )
1198  {
1199  return;
1200  }
1201 
1202  // data defined scale visibility?
1203  bool useScaleVisibility = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::ScaleVisibility, context.expressionContext(), scaleVisibility );
1204 
1205  if ( useScaleVisibility )
1206  {
1207  // data defined min scale?
1209  double maxScale = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::MaximumScale, context.expressionContext(), maximumScale );
1210 
1211  // scales closer than 1:1
1212  if ( maxScale < 0 )
1213  {
1214  maxScale = 1 / std::fabs( maxScale );
1215  }
1216 
1217  if ( !qgsDoubleNear( maxScale, 0.0 ) && context.rendererScale() < maxScale )
1218  {
1219  return;
1220  }
1221 
1222  // data defined max scale?
1224  double minScale = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::MinimumScale, context.expressionContext(), minimumScale );
1225 
1226  // scales closer than 1:1
1227  if ( minScale < 0 )
1228  {
1229  minScale = 1 / std::fabs( minScale );
1230  }
1231 
1232  if ( !qgsDoubleNear( minScale, 0.0 ) && context.rendererScale() > minScale )
1233  {
1234  return;
1235  }
1236  }
1237 
1238  QFont labelFont = mFormat.font();
1239  // labelFont will be added to label feature for use during label painting
1240 
1241  // data defined font units?
1242  QgsUnitTypes::RenderUnit fontunits = mFormat.sizeUnit();
1243  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::FontSizeUnit, context.expressionContext() );
1244  if ( exprVal.isValid() )
1245  {
1246  QString units = exprVal.toString();
1247  if ( !units.isEmpty() )
1248  {
1249  bool ok;
1251  if ( ok )
1252  fontunits = res;
1253  }
1254  }
1255 
1256  //data defined label size?
1257  context.expressionContext().setOriginalValueVariable( mFormat.size() );
1258  double fontSize = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::Size, context.expressionContext(), mFormat.size() );
1259  if ( fontSize <= 0.0 )
1260  {
1261  return;
1262  }
1263 
1264  int fontPixelSize = QgsTextRenderer::sizeToPixel( fontSize, context, fontunits, mFormat.sizeMapUnitScale() );
1265  // don't try to show font sizes less than 1 pixel (Qt complains)
1266  if ( fontPixelSize < 1 )
1267  {
1268  return;
1269  }
1270  labelFont.setPixelSize( fontPixelSize );
1271 
1272  // NOTE: labelFont now always has pixelSize set, so pointSize or pointSizeF might return -1
1273 
1274  // defined 'minimum/maximum pixel font size'?
1275  if ( fontunits == QgsUnitTypes::RenderMapUnits )
1276  {
1277  if ( mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::FontLimitPixel, context.expressionContext(), fontLimitPixelSize ) )
1278  {
1279  int fontMinPixel = mDataDefinedProperties.valueAsInt( QgsPalLayerSettings::FontMinPixel, context.expressionContext(), fontMinPixelSize );
1280  int fontMaxPixel = mDataDefinedProperties.valueAsInt( QgsPalLayerSettings::FontMaxPixel, context.expressionContext(), fontMaxPixelSize );
1281 
1282  if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
1283  {
1284  return;
1285  }
1286  }
1287  }
1288 
1289  // NOTE: the following parsing functions calculate and store any data defined values for later use in QgsPalLabeling::drawLabeling
1290  // this is done to provide clarity, and because such parsing is not directly related to PAL feature registration calculations
1291 
1292  // calculate rest of font attributes and store any data defined values
1293  // this is done here for later use in making label backgrounds part of collision management (when implemented)
1294  labelFont.setCapitalization( QFont::MixedCase ); // reset this - we don't use QFont's handling as it breaks with curved labels
1295  parseTextStyle( labelFont, fontunits, context );
1296  parseTextFormatting( context );
1297  parseTextBuffer( context );
1298  parseShapeBackground( context );
1299  parseDropShadow( context );
1300 
1301  QString labelText;
1302 
1303  // Check to see if we are a expression string.
1304  if ( isExpression )
1305  {
1307  if ( exp->hasParserError() )
1308  {
1309  QgsDebugMsgLevel( QString( "Expression parser error:%1" ).arg( exp->parserErrorString() ), 4 );
1310  return;
1311  }
1312 
1313  QVariant result = exp->evaluate( &context.expressionContext() ); // expression prepared in QgsPalLabeling::prepareLayer()
1314  if ( exp->hasEvalError() )
1315  {
1316  QgsDebugMsgLevel( QString( "Expression parser eval error:%1" ).arg( exp->evalErrorString() ), 4 );
1317  return;
1318  }
1319  labelText = result.isNull() ? QLatin1String( "" ) : result.toString();
1320  }
1321  else
1322  {
1323  const QVariant &v = f.attribute( fieldIndex );
1324  labelText = v.isNull() ? QLatin1String( "" ) : v.toString();
1325  }
1326 
1327  // apply text replacements
1328  if ( useSubstitutions )
1329  {
1330  labelText = substitutions.process( labelText );
1331  }
1332 
1333  // apply capitalization
1335  // maintain API - capitalization may have been set in textFont
1336  if ( mFormat.font().capitalization() != QFont::MixedCase )
1337  {
1338  capitalization = static_cast< QgsStringUtils::Capitalization >( mFormat.font().capitalization() );
1339  }
1340  // data defined font capitalization?
1341  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::FontCase, context.expressionContext() );
1342  if ( exprVal.isValid() )
1343  {
1344  QString fcase = exprVal.toString().trimmed();
1345  QgsDebugMsgLevel( QString( "exprVal FontCase:%1" ).arg( fcase ), 4 );
1346 
1347  if ( !fcase.isEmpty() )
1348  {
1349  if ( fcase.compare( QLatin1String( "NoChange" ), Qt::CaseInsensitive ) == 0 )
1350  {
1351  capitalization = QgsStringUtils::MixedCase;
1352  }
1353  else if ( fcase.compare( QLatin1String( "Upper" ), Qt::CaseInsensitive ) == 0 )
1354  {
1355  capitalization = QgsStringUtils::AllUppercase;
1356  }
1357  else if ( fcase.compare( QLatin1String( "Lower" ), Qt::CaseInsensitive ) == 0 )
1358  {
1359  capitalization = QgsStringUtils::AllLowercase;
1360  }
1361  else if ( fcase.compare( QLatin1String( "Capitalize" ), Qt::CaseInsensitive ) == 0 )
1362  {
1364  }
1365  }
1366  }
1367  labelText = QgsStringUtils::capitalize( labelText, capitalization );
1368 
1369  // format number if label text is coercible to a number
1370  if ( mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::NumFormat, context.expressionContext(), formatNumbers ) )
1371  {
1372  // data defined decimal places?
1373  int decimalPlaces = mDataDefinedProperties.valueAsInt( QgsPalLayerSettings::NumDecimals, context.expressionContext(), decimals );
1374  if ( decimalPlaces <= 0 ) // needs to be positive
1375  decimalPlaces = decimals;
1376 
1377  // data defined plus sign?
1378  bool signPlus = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::NumPlusSign, context.expressionContext(), plusSign );
1379 
1380  QVariant textV( labelText );
1381  bool ok;
1382  double d = textV.toDouble( &ok );
1383  if ( ok )
1384  {
1385  QString numberFormat;
1386  if ( d > 0 && signPlus )
1387  {
1388  numberFormat.append( '+' );
1389  }
1390  numberFormat.append( "%1" );
1391  labelText = numberFormat.arg( d, 0, 'f', decimalPlaces );
1392  }
1393  }
1394 
1395  // NOTE: this should come AFTER any option that affects font metrics
1396  std::unique_ptr<QFontMetricsF> labelFontMetrics( new QFontMetricsF( labelFont ) );
1397  double labelX, labelY; // will receive label size
1398  calculateLabelSize( labelFontMetrics.get(), labelText, labelX, labelY, mCurFeat, &context );
1399 
1400 
1401  // maximum angle between curved label characters (hardcoded defaults used in QGIS <2.0)
1402  //
1403  double maxcharanglein = 20.0; // range 20.0-60.0
1404  double maxcharangleout = -20.0; // range 20.0-95.0
1405 
1407  {
1408  maxcharanglein = maxCurvedCharAngleIn;
1409  maxcharangleout = maxCurvedCharAngleOut;
1410 
1411  //data defined maximum angle between curved label characters?
1412  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::CurvedCharAngleInOut, context.expressionContext() );
1413  if ( exprVal.isValid() )
1414  {
1415  QString ptstr = exprVal.toString().trimmed();
1416  QgsDebugMsgLevel( QString( "exprVal CurvedCharAngleInOut:%1" ).arg( ptstr ), 4 );
1417 
1418  if ( !ptstr.isEmpty() )
1419  {
1420  QPointF maxcharanglePt = QgsSymbolLayerUtils::decodePoint( ptstr );
1421  maxcharanglein = qBound( 20.0, static_cast< double >( maxcharanglePt.x() ), 60.0 );
1422  maxcharangleout = qBound( 20.0, static_cast< double >( maxcharanglePt.y() ), 95.0 );
1423  }
1424  }
1425  // make sure maxcharangleout is always negative
1426  maxcharangleout = -( std::fabs( maxcharangleout ) );
1427  }
1428 
1429  // data defined centroid whole or clipped?
1430  bool wholeCentroid = centroidWhole;
1431  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::CentroidWhole, context.expressionContext() );
1432  if ( exprVal.isValid() )
1433  {
1434  QString str = exprVal.toString().trimmed();
1435  QgsDebugMsgLevel( QString( "exprVal CentroidWhole:%1" ).arg( str ), 4 );
1436 
1437  if ( !str.isEmpty() )
1438  {
1439  if ( str.compare( QLatin1String( "Visible" ), Qt::CaseInsensitive ) == 0 )
1440  {
1441  wholeCentroid = false;
1442  }
1443  else if ( str.compare( QLatin1String( "Whole" ), Qt::CaseInsensitive ) == 0 )
1444  {
1445  wholeCentroid = true;
1446  }
1447  }
1448  }
1449 
1450  QgsGeometry geom = f.geometry();
1451  if ( geom.isNull() )
1452  {
1453  return;
1454  }
1455 
1456  // simplify?
1457  const QgsVectorSimplifyMethod &simplifyMethod = context.vectorSimplifyMethod();
1458  std::unique_ptr<QgsGeometry> scopedClonedGeom;
1459  if ( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && simplifyMethod.forceLocalOptimization() )
1460  {
1461  int simplifyHints = simplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
1463  QgsGeometry g = geom;
1464  QgsMapToPixelSimplifier simplifier( simplifyHints, simplifyMethod.tolerance(), simplifyAlgorithm );
1465  geom = simplifier.simplify( geom );
1466  }
1467 
1468  // whether we're going to create a centroid for polygon
1469  bool centroidPoly = ( ( placement == QgsPalLayerSettings::AroundPoint
1471  && geom.type() == QgsWkbTypes::PolygonGeometry );
1472 
1473  // CLIP the geometry if it is bigger than the extent
1474  // don't clip if centroid is requested for whole feature
1475  bool doClip = false;
1476  if ( !centroidPoly || !wholeCentroid )
1477  {
1478  doClip = true;
1479  }
1480 
1481  // if using fitInPolygonOnly option, generate the permissible zone (must happen before geometry is modified - e.g.,
1482  // as a result of using perimeter based labeling and the geometry is converted to a boundary)
1483  QgsGeometry permissibleZone;
1485  {
1486  permissibleZone = geom;
1487  if ( QgsPalLabeling::geometryRequiresPreparation( permissibleZone, context, ct, doClip ? extentGeom : QgsGeometry() ) )
1488  {
1489  permissibleZone = QgsPalLabeling::prepareGeometry( permissibleZone, context, ct, doClip ? extentGeom : QgsGeometry() );
1490  }
1491  }
1492 
1493  // if using perimeter based labeling for polygons, get the polygon's
1494  // linear boundary and use that for the label geometry
1495  if ( ( geom.type() == QgsWkbTypes::PolygonGeometry )
1496  && ( placement == Line || placement == PerimeterCurved ) )
1497  {
1498  geom = QgsGeometry( geom.constGet()->boundary() );
1499  }
1500 
1501  geos::unique_ptr geos_geom_clone;
1502  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, doClip ? extentGeom : QgsGeometry() ) )
1503  {
1504  geom = QgsPalLabeling::prepareGeometry( geom, context, ct, doClip ? extentGeom : QgsGeometry() );
1505 
1506  if ( geom.isNull() )
1507  return;
1508  }
1509  geos_geom_clone = QgsGeos::asGeos( geom );
1510 
1511  if ( isObstacle )
1512  {
1513  if ( !obstacleGeometry.isNull() && QgsPalLabeling::geometryRequiresPreparation( obstacleGeometry, context, ct, doClip ? extentGeom : QgsGeometry() ) )
1514  {
1515  obstacleGeometry = QgsGeometry( QgsPalLabeling::prepareGeometry( obstacleGeometry, context, ct, doClip ? extentGeom : QgsGeometry() ) );
1516  }
1517  }
1518 
1519  if ( minFeatureSize > 0 && !checkMinimumSizeMM( context, geom, minFeatureSize ) )
1520  return;
1521 
1522  if ( !geos_geom_clone )
1523  return; // invalid geometry
1524 
1525  // likelihood exists label will be registered with PAL and may be drawn
1526  // check if max number of features to label (already registered with PAL) has been reached
1527  // Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
1528  if ( limitNumLabels )
1529  {
1530  if ( !maxNumLabels )
1531  {
1532  return;
1533  }
1534  if ( mFeatsRegPal >= maxNumLabels )
1535  {
1536  return;
1537  }
1538 
1539  int divNum = static_cast< int >( ( static_cast< double >( mFeaturesToLabel ) / maxNumLabels ) + 0.5 ); // NOLINT
1540  if ( divNum && ( mFeatsRegPal == static_cast< int >( mFeatsSendingToPal / divNum ) ) )
1541  {
1542  mFeatsSendingToPal += 1;
1543  if ( divNum && mFeatsSendingToPal % divNum )
1544  {
1545  return;
1546  }
1547  }
1548  }
1549 
1550  geos::unique_ptr geosObstacleGeomClone;
1551  if ( obstacleGeometry )
1552  {
1553  geosObstacleGeomClone = QgsGeos::asGeos( obstacleGeometry );
1554  }
1555 
1556 
1557  //data defined position / alignment / rotation?
1558  bool dataDefinedPosition = false;
1559  bool layerDefinedRotation = false;
1560  bool dataDefinedRotation = false;
1561  double xPos = 0.0, yPos = 0.0, angle = 0.0;
1562  bool ddXPos = false, ddYPos = false;
1563  double quadOffsetX = 0.0, quadOffsetY = 0.0;
1564  double offsetX = 0.0, offsetY = 0.0;
1565 
1566  //data defined quadrant offset?
1567  bool ddFixedQuad = false;
1568  QuadrantPosition quadOff = quadOffset;
1569  context.expressionContext().setOriginalValueVariable( static_cast< int >( quadOff ) );
1570  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::OffsetQuad, context.expressionContext() );
1571  if ( exprVal.isValid() )
1572  {
1573  bool ok;
1574  int quadInt = exprVal.toInt( &ok );
1575  QgsDebugMsgLevel( QString( "exprVal OffsetQuad:%1" ).arg( quadInt ), 4 );
1576  if ( ok && 0 <= quadInt && quadInt <= 8 )
1577  {
1578  quadOff = static_cast< QuadrantPosition >( quadInt );
1579  ddFixedQuad = true;
1580  }
1581  }
1582 
1583  // adjust quadrant offset of labels
1584  switch ( quadOff )
1585  {
1586  case QuadrantAboveLeft:
1587  quadOffsetX = -1.0;
1588  quadOffsetY = 1.0;
1589  break;
1590  case QuadrantAbove:
1591  quadOffsetX = 0.0;
1592  quadOffsetY = 1.0;
1593  break;
1594  case QuadrantAboveRight:
1595  quadOffsetX = 1.0;
1596  quadOffsetY = 1.0;
1597  break;
1598  case QuadrantLeft:
1599  quadOffsetX = -1.0;
1600  quadOffsetY = 0.0;
1601  break;
1602  case QuadrantRight:
1603  quadOffsetX = 1.0;
1604  quadOffsetY = 0.0;
1605  break;
1606  case QuadrantBelowLeft:
1607  quadOffsetX = -1.0;
1608  quadOffsetY = -1.0;
1609  break;
1610  case QuadrantBelow:
1611  quadOffsetX = 0.0;
1612  quadOffsetY = -1.0;
1613  break;
1614  case QuadrantBelowRight:
1615  quadOffsetX = 1.0;
1616  quadOffsetY = -1.0;
1617  break;
1618  case QuadrantOver:
1619  default:
1620  break;
1621  }
1622 
1623  //data defined label offset?
1624  double xOff = xOffset;
1625  double yOff = yOffset;
1627  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::OffsetXY, context.expressionContext() );
1628  if ( exprVal.isValid() )
1629  {
1630  QString ptstr = exprVal.toString().trimmed();
1631  QgsDebugMsgLevel( QString( "exprVal OffsetXY:%1" ).arg( ptstr ), 4 );
1632 
1633  if ( !ptstr.isEmpty() )
1634  {
1635  QPointF ddOffPt = QgsSymbolLayerUtils::decodePoint( ptstr );
1636  xOff = ddOffPt.x();
1637  yOff = ddOffPt.y();
1638  }
1639  }
1640 
1641  // data defined label offset units?
1643  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::OffsetUnits, context.expressionContext() );
1644  if ( exprVal.isValid() )
1645  {
1646  QString units = exprVal.toString().trimmed();
1647  QgsDebugMsgLevel( QString( "exprVal OffsetUnits:%1" ).arg( units ), 4 );
1648  if ( !units.isEmpty() )
1649  {
1650  bool ok = false;
1651  QgsUnitTypes::RenderUnit decodedUnits = QgsUnitTypes::decodeRenderUnit( units, &ok );
1652  if ( ok )
1653  {
1654  offUnit = decodedUnits;
1655  }
1656  }
1657  }
1658 
1659  // adjust offset of labels to match chosen unit and map scale
1660  // offsets match those of symbology: -x = left, -y = up
1661  offsetX = context.convertToMapUnits( xOff, offUnit, labelOffsetMapUnitScale );
1662  // must be negative to match symbology offset direction
1663  offsetY = context.convertToMapUnits( -yOff, offUnit, labelOffsetMapUnitScale );
1664 
1665  // layer defined rotation?
1666  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
1668  {
1669  layerDefinedRotation = true;
1670  angle = ( 360 - angleOffset ) * M_PI / 180; // convert to radians counterclockwise
1671  }
1672 
1673  const QgsMapToPixel &m2p = context.mapToPixel();
1674  //data defined rotation?
1676  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::LabelRotation, context.expressionContext() );
1677  if ( exprVal.isValid() )
1678  {
1679  bool ok;
1680  double rotD = exprVal.toDouble( &ok );
1681  QgsDebugMsgLevel( QString( "exprVal Rotation:%1" ).arg( rotD ), 4 );
1682  if ( ok )
1683  {
1684  dataDefinedRotation = true;
1685  // TODO: add setting to disable having data defined rotation follow
1686  // map rotation ?
1687  rotD += m2p.mapRotation();
1688  angle = ( 360 - rotD ) * M_PI / 180.0;
1689  }
1690  }
1691 
1692  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::PositionX, context.expressionContext() );
1693  if ( exprVal.isValid() )
1694  {
1695  if ( !exprVal.isNull() )
1696  xPos = exprVal.toDouble( &ddXPos );
1697  QgsDebugMsgLevel( QString( "exprVal PositionX:%1" ).arg( xPos ), 4 );
1698 
1699  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::PositionY, context.expressionContext() );
1700  if ( exprVal.isValid() )
1701  {
1702  //data defined position. But field values could be NULL -> positions will be generated by PAL
1703  if ( !exprVal.isNull() )
1704  yPos = exprVal.toDouble( &ddYPos );
1705  QgsDebugMsgLevel( QString( "exprVal PositionY:%1" ).arg( yPos ), 4 );
1706 
1707  if ( ddXPos && ddYPos )
1708  {
1709  dataDefinedPosition = true;
1710  // layer rotation set, but don't rotate pinned labels unless data defined
1711  if ( layerDefinedRotation && !dataDefinedRotation )
1712  {
1713  angle = 0.0;
1714  }
1715 
1716  //x/y shift in case of alignment
1717  double xdiff = 0.0;
1718  double ydiff = 0.0;
1719 
1720  //horizontal alignment
1721  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Hali, context.expressionContext() );
1722  if ( exprVal.isValid() )
1723  {
1724  QString haliString = exprVal.toString();
1725  QgsDebugMsgLevel( QString( "exprVal Hali:%1" ).arg( haliString ), 4 );
1726  if ( haliString.compare( QLatin1String( "Center" ), Qt::CaseInsensitive ) == 0 )
1727  {
1728  xdiff -= labelX / 2.0;
1729  }
1730  else if ( haliString.compare( QLatin1String( "Right" ), Qt::CaseInsensitive ) == 0 )
1731  {
1732  xdiff -= labelX;
1733  }
1734  }
1735 
1736  //vertical alignment
1737  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Vali, context.expressionContext() );
1738  if ( exprVal.isValid() )
1739  {
1740  QString valiString = exprVal.toString();
1741  QgsDebugMsgLevel( QString( "exprVal Vali:%1" ).arg( valiString ), 4 );
1742 
1743  if ( valiString.compare( QLatin1String( "Bottom" ), Qt::CaseInsensitive ) != 0 )
1744  {
1745  if ( valiString.compare( QLatin1String( "Top" ), Qt::CaseInsensitive ) == 0 )
1746  {
1747  ydiff -= labelY;
1748  }
1749  else
1750  {
1751  double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
1752  if ( valiString.compare( QLatin1String( "Base" ), Qt::CaseInsensitive ) == 0 )
1753  {
1754  ydiff -= labelY * descentRatio;
1755  }
1756  else //'Cap' or 'Half'
1757  {
1758  double capHeightRatio = ( labelFontMetrics->boundingRect( 'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
1759  ydiff -= labelY * capHeightRatio;
1760  if ( valiString.compare( QLatin1String( "Half" ), Qt::CaseInsensitive ) == 0 )
1761  {
1762  ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
1763  }
1764  }
1765  }
1766  }
1767  }
1768 
1769  if ( dataDefinedRotation )
1770  {
1771  //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
1772  double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
1773  double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
1774  xdiff = xd;
1775  ydiff = yd;
1776  }
1777 
1778  //project xPos and yPos from layer to map CRS, handle rotation
1779  QgsGeometry ddPoint( new QgsPoint( xPos, yPos ) );
1780  if ( QgsPalLabeling::geometryRequiresPreparation( ddPoint, context, ct ) )
1781  {
1782  ddPoint = QgsPalLabeling::prepareGeometry( ddPoint, context, ct );
1783  xPos = static_cast< const QgsPoint * >( ddPoint.constGet() )->x();
1784  yPos = static_cast< const QgsPoint * >( ddPoint.constGet() )->y();
1785  }
1786 
1787  xPos += xdiff;
1788  yPos += ydiff;
1789  }
1790  else
1791  {
1792  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
1793  if ( dataDefinedRotation && placement != QgsPalLayerSettings::OverPoint )
1794  {
1795  angle = 0.0;
1796  }
1797  }
1798  }
1799  }
1800 
1801  // data defined always show?
1802  bool alwaysShow = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::AlwaysShow, context.expressionContext(), false );
1803 
1804  // set repeat distance
1805  // data defined repeat distance?
1807  double repeatDist = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::RepeatDistance, context.expressionContext(), repeatDistance );
1808 
1809  // data defined label-repeat distance units?
1811  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::RepeatDistanceUnit, context.expressionContext() );
1812  if ( exprVal.isValid() )
1813  {
1814  QString units = exprVal.toString().trimmed();
1815  QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 );
1816  if ( !units.isEmpty() )
1817  {
1818  bool ok = false;
1819  QgsUnitTypes::RenderUnit decodedUnits = QgsUnitTypes::decodeRenderUnit( units, &ok );
1820  if ( ok )
1821  {
1822  repeatUnits = decodedUnits;
1823  }
1824  }
1825  }
1826 
1827  if ( !qgsDoubleNear( repeatDist, 0.0 ) )
1828  {
1829  if ( repeatUnits != QgsUnitTypes::RenderMapUnits )
1830  {
1831  repeatDist = context.convertToMapUnits( repeatDist, repeatUnits, repeatDistanceMapUnitScale );
1832  }
1833  }
1834 
1835  // feature to the layer
1836  QgsTextLabelFeature *lf = new QgsTextLabelFeature( f.id(), std::move( geos_geom_clone ), QSizeF( labelX, labelY ) );
1837  mFeatsRegPal++;
1838 
1839  *labelFeature = lf;
1840  ( *labelFeature )->setHasFixedPosition( dataDefinedPosition );
1841  ( *labelFeature )->setFixedPosition( QgsPointXY( xPos, yPos ) );
1842  // use layer-level defined rotation, but not if position fixed
1843  ( *labelFeature )->setHasFixedAngle( dataDefinedRotation || ( !dataDefinedPosition && !qgsDoubleNear( angle, 0.0 ) ) );
1844  ( *labelFeature )->setFixedAngle( angle );
1845  ( *labelFeature )->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
1846  ( *labelFeature )->setPositionOffset( QgsPointXY( offsetX, offsetY ) );
1847  ( *labelFeature )->setOffsetType( offsetType );
1848  ( *labelFeature )->setAlwaysShow( alwaysShow );
1849  ( *labelFeature )->setRepeatDistance( repeatDist );
1850  ( *labelFeature )->setLabelText( labelText );
1851  ( *labelFeature )->setPermissibleZone( permissibleZone );
1852  if ( geosObstacleGeomClone )
1853  {
1854  ( *labelFeature )->setObstacleGeometry( std::move( geosObstacleGeomClone ) );
1855 
1856  if ( geom.type() == QgsWkbTypes::PointGeometry )
1857  {
1858  //register symbol size
1859  ( *labelFeature )->setSymbolSize( QSizeF( obstacleGeometry.boundingBox().width(),
1860  obstacleGeometry.boundingBox().height() ) );
1861  }
1862  }
1863 
1864  //set label's visual margin so that top visual margin is the leading, and bottom margin is the font's descent
1865  //this makes labels align to the font's baseline or highest character
1866  double topMargin = std::max( 0.25 * labelFontMetrics->ascent(), 0.0 );
1867  double bottomMargin = 1.0 + labelFontMetrics->descent();
1868  QgsMargins vm( 0.0, topMargin, 0.0, bottomMargin );
1869  vm *= xform->mapUnitsPerPixel();
1870  ( *labelFeature )->setVisualMargin( vm );
1871 
1872  // store the label's calculated font for later use during painting
1873  QgsDebugMsgLevel( QString( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString(), labelFont.styleName() ), 4 );
1874  lf->setDefinedFont( labelFont );
1875 
1876  // TODO: only for placement which needs character info
1877  // account for any data defined font metrics adjustments
1879  labelFontMetrics.get(), xform, maxcharanglein, maxcharangleout );
1880  // for labelFeature the LabelInfo is passed to feat when it is registered
1881 
1882  // TODO: allow layer-wide feature dist in PAL...?
1883 
1884  // data defined label-feature distance?
1886  double distance = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::LabelDistance, context.expressionContext(), dist );
1887 
1888  // data defined label-feature distance units?
1890  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::DistanceUnits, context.expressionContext() );
1891  if ( exprVal.isValid() )
1892  {
1893  QString units = exprVal.toString().trimmed();
1894  QgsDebugMsgLevel( QString( "exprVal DistanceUnits:%1" ).arg( units ), 4 );
1895  if ( !units.isEmpty() )
1896  {
1897  bool ok = false;
1898  QgsUnitTypes::RenderUnit decodedUnits = QgsUnitTypes::decodeRenderUnit( units, &ok );
1899  if ( ok )
1900  {
1901  distUnit = decodedUnits;
1902  }
1903  }
1904  }
1905  distance = context.convertToPainterUnits( distance, distUnit, distMapUnitScale );
1906 
1907  // when using certain placement modes, we force a tiny minimum distance. This ensures that
1908  // candidates are created just offset from a border and avoids candidates being incorrectly flagged as colliding with neighbours
1910  {
1911  distance = std::max( distance, 1.0 );
1912  }
1913 
1914  if ( !qgsDoubleNear( distance, 0.0 ) )
1915  {
1916  double d = ptOne.distance( ptZero ) * distance;
1917  ( *labelFeature )->setDistLabel( d );
1918  }
1919 
1920  if ( ddFixedQuad )
1921  {
1922  ( *labelFeature )->setHasFixedQuadrant( true );
1923  }
1924 
1925  // data defined z-index?
1927  double z = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::ZIndex, context.expressionContext(), zIndex );
1928  ( *labelFeature )->setZIndex( z );
1929 
1930  // data defined priority?
1932  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Priority, context.expressionContext() );
1933  if ( exprVal.isValid() )
1934  {
1935  bool ok;
1936  double priorityD = exprVal.toDouble( &ok );
1937  if ( ok )
1938  {
1939  priorityD = qBound( 0.0, priorityD, 10.0 );
1940  priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
1941  ( *labelFeature )->setPriority( priorityD );
1942  }
1943  }
1944 
1945  ( *labelFeature )->setIsObstacle( isObstacle );
1946 
1947  double featObstacleFactor = obstacleFactor;
1949  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ObstacleFactor, context.expressionContext() );
1950  if ( exprVal.isValid() )
1951  {
1952  bool ok;
1953  double factorD = exprVal.toDouble( &ok );
1954  if ( ok )
1955  {
1956  factorD = qBound( 0.0, factorD, 10.0 );
1957  factorD = factorD / 5.0 + 0.0001; // convert 0 -> 10 to 0.0001 -> 2.0
1958  featObstacleFactor = factorD;
1959  }
1960  }
1961  ( *labelFeature )->setObstacleFactor( featObstacleFactor );
1962 
1963  QVector< QgsPalLayerSettings::PredefinedPointPosition > positionOrder = predefinedPositionOrder;
1964  if ( positionOrder.isEmpty() )
1965  positionOrder = QgsPalLayerSettings::DEFAULT_PLACEMENT_ORDER;
1966 
1968  QString dataDefinedOrder = mDataDefinedProperties.valueAsString( QgsPalLayerSettings::PredefinedPositionOrder, context.expressionContext() );
1969  if ( !dataDefinedOrder.isEmpty() )
1970  {
1971  positionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( dataDefinedOrder );
1972  }
1973  ( *labelFeature )->setPredefinedPositionOrder( positionOrder );
1974 
1975  // add parameters for data defined labeling to label feature
1976  lf->setDataDefinedValues( dataDefinedValues );
1977 }
1978 
1979 void QgsPalLayerSettings::registerObstacleFeature( QgsFeature &f, QgsRenderContext &context, QgsLabelFeature **obstacleFeature, const QgsGeometry &obstacleGeometry )
1980 {
1981  mCurFeat = &f;
1982 
1983  QgsGeometry geom;
1984  if ( obstacleGeometry )
1985  {
1986  geom = obstacleGeometry;
1987  }
1988  else
1989  {
1990  geom = f.geometry();
1991  }
1992 
1993  if ( geom.isNull() )
1994  {
1995  return;
1996  }
1997 
1998  // simplify?
1999  const QgsVectorSimplifyMethod &simplifyMethod = context.vectorSimplifyMethod();
2000  std::unique_ptr<QgsGeometry> scopedClonedGeom;
2001  if ( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && simplifyMethod.forceLocalOptimization() )
2002  {
2003  int simplifyHints = simplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
2005  QgsMapToPixelSimplifier simplifier( simplifyHints, simplifyMethod.tolerance(), simplifyAlgorithm );
2006  geom = simplifier.simplify( geom );
2007  }
2008 
2009  geos::unique_ptr geos_geom_clone;
2010  std::unique_ptr<QgsGeometry> scopedPreparedGeom;
2011 
2012  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, extentGeom ) )
2013  {
2014  geom = QgsPalLabeling::prepareGeometry( geom, context, ct, extentGeom );
2015  }
2016  geos_geom_clone = QgsGeos::asGeos( geom );
2017 
2018  if ( !geos_geom_clone )
2019  return; // invalid geometry
2020 
2021  // feature to the layer
2022  *obstacleFeature = new QgsLabelFeature( f.id(), std::move( geos_geom_clone ), QSizeF( 0, 0 ) );
2023  ( *obstacleFeature )->setIsObstacle( true );
2024  mFeatsRegPal++;
2025 }
2026 
2027 bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
2029  QVariant &exprVal, QgsExpressionContext &context, const QVariant &originalValue )
2030 {
2031  if ( !mDataDefinedProperties.isActive( p ) )
2032  return false;
2033 
2034  context.setOriginalValueVariable( originalValue );
2035  exprVal = mDataDefinedProperties.value( p, context );
2036  if ( exprVal.isValid() )
2037  {
2038  switch ( valType )
2039  {
2040  case DDBool:
2041  {
2042  bool bol = exprVal.toBool();
2043  dataDefinedValues.insert( p, QVariant( bol ) );
2044  return true;
2045  }
2046  case DDInt:
2047  {
2048  bool ok;
2049  int size = exprVal.toInt( &ok );
2050 
2051  if ( ok )
2052  {
2053  dataDefinedValues.insert( p, QVariant( size ) );
2054  return true;
2055  }
2056  return false;
2057  }
2058  case DDIntPos:
2059  {
2060  bool ok;
2061  int size = exprVal.toInt( &ok );
2062 
2063  if ( ok && size > 0 )
2064  {
2065  dataDefinedValues.insert( p, QVariant( size ) );
2066  return true;
2067  }
2068  return false;
2069  }
2070  case DDDouble:
2071  {
2072  bool ok;
2073  double size = exprVal.toDouble( &ok );
2074 
2075  if ( ok )
2076  {
2077  dataDefinedValues.insert( p, QVariant( size ) );
2078  return true;
2079  }
2080  return false;
2081  }
2082  case DDDoublePos:
2083  {
2084  bool ok;
2085  double size = exprVal.toDouble( &ok );
2086 
2087  if ( ok && size > 0.0 )
2088  {
2089  dataDefinedValues.insert( p, QVariant( size ) );
2090  return true;
2091  }
2092  return false;
2093  }
2094  case DDRotation180:
2095  {
2096  bool ok;
2097  double rot = exprVal.toDouble( &ok );
2098  if ( ok )
2099  {
2100  if ( rot < -180.0 && rot >= -360 )
2101  {
2102  rot += 360;
2103  }
2104  if ( rot > 180.0 && rot <= 360 )
2105  {
2106  rot -= 360;
2107  }
2108  if ( rot >= -180 && rot <= 180 )
2109  {
2110  dataDefinedValues.insert( p, QVariant( rot ) );
2111  return true;
2112  }
2113  }
2114  return false;
2115  }
2116  case DDOpacity:
2117  {
2118  bool ok;
2119  int size = exprVal.toDouble( &ok );
2120  if ( ok && size >= 0 && size <= 100 )
2121  {
2122  dataDefinedValues.insert( p, QVariant( size ) );
2123  return true;
2124  }
2125  return false;
2126  }
2127  case DDString:
2128  {
2129  QString str = exprVal.toString(); // don't trim whitespace
2130 
2131  dataDefinedValues.insert( p, QVariant( str ) ); // let it stay empty if it is
2132  return true;
2133  }
2134  case DDUnits:
2135  {
2136  QString unitstr = exprVal.toString().trimmed();
2137 
2138  if ( !unitstr.isEmpty() )
2139  {
2140  dataDefinedValues.insert( p, QVariant( static_cast< int >( QgsUnitTypes::decodeRenderUnit( unitstr ) ) ) );
2141  return true;
2142  }
2143  return false;
2144  }
2145  case DDColor:
2146  {
2147  QString colorstr = exprVal.toString().trimmed();
2148  QColor color = QgsSymbolLayerUtils::decodeColor( colorstr );
2149 
2150  if ( color.isValid() )
2151  {
2152  dataDefinedValues.insert( p, QVariant( color ) );
2153  return true;
2154  }
2155  return false;
2156  }
2157  case DDJoinStyle:
2158  {
2159  QString joinstr = exprVal.toString().trimmed();
2160 
2161  if ( !joinstr.isEmpty() )
2162  {
2163  dataDefinedValues.insert( p, QVariant( static_cast< int >( _decodePenJoinStyle( joinstr ) ) ) );
2164  return true;
2165  }
2166  return false;
2167  }
2168  case DDBlendMode:
2169  {
2170  QString blendstr = exprVal.toString().trimmed();
2171 
2172  if ( !blendstr.isEmpty() )
2173  {
2174  dataDefinedValues.insert( p, QVariant( static_cast< int >( QgsSymbolLayerUtils::decodeBlendMode( blendstr ) ) ) );
2175  return true;
2176  }
2177  return false;
2178  }
2179  case DDPointF:
2180  {
2181  QString ptstr = exprVal.toString().trimmed();
2182 
2183  if ( !ptstr.isEmpty() )
2184  {
2185  dataDefinedValues.insert( p, QVariant( QgsSymbolLayerUtils::decodePoint( ptstr ) ) );
2186  return true;
2187  }
2188  return false;
2189  }
2190  case DDSizeF:
2191  {
2192  QString ptstr = exprVal.toString().trimmed();
2193 
2194  if ( !ptstr.isEmpty() )
2195  {
2196  dataDefinedValues.insert( p, QVariant( QgsSymbolLayerUtils::decodeSize( ptstr ) ) );
2197  return true;
2198  }
2199  return false;
2200  }
2201  }
2202  }
2203  return false;
2204 }
2205 
2206 void QgsPalLayerSettings::parseTextStyle( QFont &labelFont,
2207  QgsUnitTypes::RenderUnit fontunits,
2208  QgsRenderContext &context )
2209 {
2210  // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
2211 
2212  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2213 
2214  // Two ways to generate new data defined font:
2215  // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
2216  // 2) Family + named style (bold or italic is ignored)
2217 
2218  // data defined font family?
2219  QString ddFontFamily;
2220  context.expressionContext().setOriginalValueVariable( labelFont.family() );
2221  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Family, context.expressionContext() );
2222  if ( exprVal.isValid() )
2223  {
2224  QString family = exprVal.toString().trimmed();
2225  QgsDebugMsgLevel( QString( "exprVal Font family:%1" ).arg( family ), 4 );
2226 
2227  if ( labelFont.family() != family )
2228  {
2229  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
2230  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
2231  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
2232  {
2233  ddFontFamily = family;
2234  }
2235  }
2236  }
2237 
2238  // data defined named font style?
2239  QString ddFontStyle;
2240  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::FontStyle, context.expressionContext() );
2241  if ( exprVal.isValid() )
2242  {
2243  QString fontstyle = exprVal.toString().trimmed();
2244  QgsDebugMsgLevel( QString( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
2245  ddFontStyle = fontstyle;
2246  }
2247 
2248  // data defined bold font style?
2249  context.expressionContext().setOriginalValueVariable( labelFont.bold() );
2250  bool ddBold = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Bold, context.expressionContext(), false );
2251 
2252  // data defined italic font style?
2253  context.expressionContext().setOriginalValueVariable( labelFont.italic() );
2254  bool ddItalic = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Italic, context.expressionContext(), false );
2255 
2256  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
2257  // (currently defaults to what has been read in from layer settings)
2258  QFont newFont;
2259  QFont appFont = QApplication::font();
2260  bool newFontBuilt = false;
2261  if ( ddBold || ddItalic )
2262  {
2263  // new font needs built, since existing style needs removed
2264  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
2265  newFontBuilt = true;
2266  newFont.setBold( ddBold );
2267  newFont.setItalic( ddItalic );
2268  }
2269  else if ( !ddFontStyle.isEmpty()
2270  && ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
2271  {
2272  if ( !ddFontFamily.isEmpty() )
2273  {
2274  // both family and style are different, build font from database
2275  QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
2276  if ( appFont != styledfont )
2277  {
2278  newFont = styledfont;
2279  newFontBuilt = true;
2280  }
2281  }
2282 
2283  // update the font face style
2284  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
2285  }
2286  else if ( !ddFontFamily.isEmpty() )
2287  {
2288  if ( ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
2289  {
2290  // just family is different, build font from database
2291  QFont styledfont = mFontDB.font( ddFontFamily, mFormat.namedStyle(), appFont.pointSize() );
2292  if ( appFont != styledfont )
2293  {
2294  newFont = styledfont;
2295  newFontBuilt = true;
2296  }
2297  }
2298  else
2299  {
2300  newFont = QFont( ddFontFamily );
2301  newFontBuilt = true;
2302  }
2303  }
2304 
2305  if ( newFontBuilt )
2306  {
2307  // copy over existing font settings
2308  //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
2309  newFont.setPixelSize( labelFont.pixelSize() );
2310  newFont.setUnderline( labelFont.underline() );
2311  newFont.setStrikeOut( labelFont.strikeOut() );
2312  newFont.setWordSpacing( labelFont.wordSpacing() );
2313  newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
2314 
2315  labelFont = newFont;
2316  }
2317 
2318  // data defined word spacing?
2319  double wordspace = labelFont.wordSpacing();
2320  context.expressionContext().setOriginalValueVariable( wordspace );
2321  wordspace = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::FontWordSpacing, context.expressionContext(), wordspace );
2322  labelFont.setWordSpacing( context.convertToPainterUnits( wordspace, fontunits, mFormat.sizeMapUnitScale() ) );
2323 
2324  // data defined letter spacing?
2325  double letterspace = labelFont.letterSpacing();
2326  context.expressionContext().setOriginalValueVariable( letterspace );
2327  letterspace = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::FontLetterSpacing, context.expressionContext(), letterspace );
2328  labelFont.setLetterSpacing( QFont::AbsoluteSpacing, context.convertToPainterUnits( letterspace, fontunits, mFormat.sizeMapUnitScale() ) );
2329 
2330  // data defined strikeout font style?
2331  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Strikeout ) )
2332  {
2333  context.expressionContext().setOriginalValueVariable( labelFont.strikeOut() );
2334  bool strikeout = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Strikeout, context.expressionContext(), false );
2335  labelFont.setStrikeOut( strikeout );
2336  }
2337 
2338  // data defined underline font style?
2339  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Underline ) )
2340  {
2341  context.expressionContext().setOriginalValueVariable( labelFont.underline() );
2342  bool underline = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Underline, context.expressionContext(), false );
2343  labelFont.setUnderline( underline );
2344  }
2345 
2346  // pass the rest on to QgsPalLabeling::drawLabeling
2347 
2348  // data defined font color?
2349  dataDefinedValEval( DDColor, QgsPalLayerSettings::Color, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( mFormat.color() ) );
2350 
2351  // data defined font opacity?
2352  dataDefinedValEval( DDOpacity, QgsPalLayerSettings::FontOpacity, exprVal, context.expressionContext(), mFormat.opacity() * 100 );
2353 
2354  // data defined font blend mode?
2355  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::FontBlendMode, exprVal, context.expressionContext() );
2356 
2357 }
2358 
2359 void QgsPalLayerSettings::parseTextBuffer( QgsRenderContext &context )
2360 {
2361  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2362 
2363  QgsTextBufferSettings buffer = mFormat.buffer();
2364 
2365  // data defined draw buffer?
2366  bool drawBuffer = mFormat.buffer().enabled();
2367  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::BufferDraw, exprVal, context.expressionContext(), buffer.enabled() ) )
2368  {
2369  drawBuffer = exprVal.toBool();
2370  }
2371 
2372  if ( !drawBuffer )
2373  {
2374  return;
2375  }
2376 
2377  // data defined buffer size?
2378  double bufrSize = buffer.size();
2379  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::BufferSize, exprVal, context.expressionContext(), buffer.size() ) )
2380  {
2381  bufrSize = exprVal.toDouble();
2382  }
2383 
2384  // data defined buffer transparency?
2385  double bufferOpacity = buffer.opacity() * 100;
2386  if ( dataDefinedValEval( DDOpacity, QgsPalLayerSettings::BufferOpacity, exprVal, context.expressionContext(), bufferOpacity ) )
2387  {
2388  bufferOpacity = exprVal.toDouble();
2389  }
2390 
2391  drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufferOpacity > 0 );
2392 
2393  if ( !drawBuffer )
2394  {
2395  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
2396  dataDefinedValues.remove( QgsPalLayerSettings::BufferSize );
2397  dataDefinedValues.remove( QgsPalLayerSettings::BufferOpacity );
2398  return; // don't bother evaluating values that won't be used
2399  }
2400 
2401  // data defined buffer units?
2402  dataDefinedValEval( DDUnits, QgsPalLayerSettings::BufferUnit, exprVal, context.expressionContext() );
2403 
2404  // data defined buffer color?
2405  dataDefinedValEval( DDColor, QgsPalLayerSettings::BufferColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( buffer.color() ) );
2406 
2407  // data defined buffer pen join style?
2408  dataDefinedValEval( DDJoinStyle, QgsPalLayerSettings::BufferJoinStyle, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodePenJoinStyle( buffer.joinStyle() ) );
2409 
2410  // data defined buffer blend mode?
2411  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::BufferBlendMode, exprVal, context.expressionContext() );
2412 }
2413 
2414 void QgsPalLayerSettings::parseTextFormatting( QgsRenderContext &context )
2415 {
2416  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2417 
2418  // data defined multiline wrap character?
2419  QString wrapchr = wrapChar;
2420  if ( dataDefinedValEval( DDString, QgsPalLayerSettings::MultiLineWrapChar, exprVal, context.expressionContext(), wrapChar ) )
2421  {
2422  wrapchr = exprVal.toString();
2423  }
2424 
2425  // data defined multiline height?
2426  dataDefinedValEval( DDDouble, QgsPalLayerSettings::MultiLineHeight, exprVal, context.expressionContext() );
2427 
2428  // data defined multiline text align?
2429  context.expressionContext().setOriginalValueVariable( mFormat.lineHeight() );
2430  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::MultiLineAlignment, context.expressionContext() );
2431  if ( exprVal.isValid() )
2432  {
2433  QString str = exprVal.toString().trimmed();
2434  QgsDebugMsgLevel( QString( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
2435 
2436  if ( !str.isEmpty() )
2437  {
2438  // "Left"
2440 
2441  if ( str.compare( QLatin1String( "Center" ), Qt::CaseInsensitive ) == 0 )
2442  {
2444  }
2445  else if ( str.compare( QLatin1String( "Right" ), Qt::CaseInsensitive ) == 0 )
2446  {
2447  aligntype = QgsPalLayerSettings::MultiRight;
2448  }
2449  else if ( str.compare( QLatin1String( "Follow" ), Qt::CaseInsensitive ) == 0 )
2450  {
2452  }
2453  dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant( static_cast< int >( aligntype ) ) );
2454  }
2455  }
2456 
2457  // data defined direction symbol?
2458  bool drawDirSymb = addDirectionSymbol;
2459  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbDraw, exprVal, context.expressionContext(), addDirectionSymbol ) )
2460  {
2461  drawDirSymb = exprVal.toBool();
2462  }
2463 
2464  if ( drawDirSymb )
2465  {
2466  // data defined direction left symbol?
2467  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbLeft, exprVal, context.expressionContext(), leftDirectionSymbol );
2468 
2469  // data defined direction right symbol?
2470  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbRight, exprVal, context.expressionContext(), rightDirectionSymbol );
2471 
2472  // data defined direction symbol placement?
2473  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::DirSymbPlacement, context.expressionContext() );
2474  if ( exprVal.isValid() )
2475  {
2476  QString str = exprVal.toString().trimmed();
2477  QgsDebugMsgLevel( QString( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
2478 
2479  if ( !str.isEmpty() )
2480  {
2481  // "LeftRight"
2483 
2484  if ( str.compare( QLatin1String( "Above" ), Qt::CaseInsensitive ) == 0 )
2485  {
2487  }
2488  else if ( str.compare( QLatin1String( "Below" ), Qt::CaseInsensitive ) == 0 )
2489  {
2491  }
2492  dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant( static_cast< int >( placetype ) ) );
2493  }
2494  }
2495 
2496  // data defined direction symbol reversed?
2497  dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbReverse, exprVal, context.expressionContext(), reverseDirectionSymbol );
2498  }
2499 
2500  // formatting for numbers is inline with generation of base label text and not passed to label painting
2501 }
2502 
2503 void QgsPalLayerSettings::parseShapeBackground( QgsRenderContext &context )
2504 {
2505  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2506 
2507  QgsTextBackgroundSettings background = mFormat.background();
2508 
2509  // data defined draw shape?
2510  bool drawShape = background.enabled();
2511  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShapeDraw, exprVal, context.expressionContext(), drawShape ) )
2512  {
2513  drawShape = exprVal.toBool();
2514  }
2515 
2516  if ( !drawShape )
2517  {
2518  return;
2519  }
2520 
2521  // data defined shape transparency?
2522  double shapeOpacity = background.opacity() * 100;
2523  if ( dataDefinedValEval( DDOpacity, QgsPalLayerSettings::ShapeOpacity, exprVal, context.expressionContext(), shapeOpacity ) )
2524  {
2525  shapeOpacity = 100.0 * exprVal.toDouble();
2526  }
2527 
2528  drawShape = ( drawShape && shapeOpacity > 0 ); // size is not taken into account (could be)
2529 
2530  if ( !drawShape )
2531  {
2532  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
2533  dataDefinedValues.remove( QgsPalLayerSettings::ShapeOpacity );
2534  return; // don't bother evaluating values that won't be used
2535  }
2536 
2537  // data defined shape kind?
2538  QgsTextBackgroundSettings::ShapeType shapeKind = background.type();
2539  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShapeKind, context.expressionContext() );
2540  if ( exprVal.isValid() )
2541  {
2542  QString skind = exprVal.toString().trimmed();
2543  QgsDebugMsgLevel( QString( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
2544 
2545  if ( !skind.isEmpty() )
2546  {
2547  // "Rectangle"
2549 
2550  if ( skind.compare( QLatin1String( "Square" ), Qt::CaseInsensitive ) == 0 )
2551  {
2553  }
2554  else if ( skind.compare( QLatin1String( "Ellipse" ), Qt::CaseInsensitive ) == 0 )
2555  {
2557  }
2558  else if ( skind.compare( QLatin1String( "Circle" ), Qt::CaseInsensitive ) == 0 )
2559  {
2561  }
2562  else if ( skind.compare( QLatin1String( "SVG" ), Qt::CaseInsensitive ) == 0 )
2563  {
2565  }
2566  shapeKind = shpkind;
2567  dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant( static_cast< int >( shpkind ) ) );
2568  }
2569  }
2570 
2571  // data defined shape SVG path?
2572  QString svgPath = background.svgFile();
2573  context.expressionContext().setOriginalValueVariable( svgPath );
2574  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShapeSVGFile, context.expressionContext() );
2575  if ( exprVal.isValid() )
2576  {
2577  QString svgfile = exprVal.toString().trimmed();
2578  QgsDebugMsgLevel( QString( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
2579 
2580  // '' empty paths are allowed
2581  svgPath = QgsSymbolLayerUtils::svgSymbolNameToPath( svgfile, context.pathResolver() );
2582  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgPath ) );
2583  }
2584 
2585  // data defined shape size type?
2586  QgsTextBackgroundSettings::SizeType shpSizeType = background.sizeType();
2587  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShapeSizeType, context.expressionContext() );
2588  if ( exprVal.isValid() )
2589  {
2590  QString stype = exprVal.toString().trimmed();
2591  QgsDebugMsgLevel( QString( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
2592 
2593  if ( !stype.isEmpty() )
2594  {
2595  // "Buffer"
2597 
2598  if ( stype.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
2599  {
2601  }
2602  shpSizeType = sizType;
2603  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant( static_cast< int >( sizType ) ) );
2604  }
2605  }
2606 
2607  // data defined shape size X? (SVGs only use X for sizing)
2608  double ddShpSizeX = background.size().width();
2609  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeX, exprVal, context.expressionContext(), ddShpSizeX ) )
2610  {
2611  ddShpSizeX = exprVal.toDouble();
2612  }
2613 
2614  // data defined shape size Y?
2615  double ddShpSizeY = background.size().height();
2616  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeY, exprVal, context.expressionContext(), ddShpSizeY ) )
2617  {
2618  ddShpSizeY = exprVal.toDouble();
2619  }
2620 
2621  // don't continue under certain circumstances (e.g. size is fixed)
2622  bool skip = false;
2623  if ( shapeKind == QgsTextBackgroundSettings::ShapeSVG
2624  && ( svgPath.isEmpty()
2625  || ( !svgPath.isEmpty()
2626  && shpSizeType == QgsTextBackgroundSettings::SizeFixed
2627  && ddShpSizeX == 0.0 ) ) )
2628  {
2629  skip = true;
2630  }
2631  if ( shapeKind != QgsTextBackgroundSettings::ShapeSVG
2632  && shpSizeType == QgsTextBackgroundSettings::SizeFixed
2633  && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
2634  {
2635  skip = true;
2636  }
2637 
2638  if ( skip )
2639  {
2640  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
2641  dataDefinedValues.remove( QgsPalLayerSettings::ShapeOpacity );
2642  dataDefinedValues.remove( QgsPalLayerSettings::ShapeKind );
2643  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSVGFile );
2644  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeX );
2645  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeY );
2646  return; // don't bother evaluating values that won't be used
2647  }
2648 
2649  // data defined shape size units?
2650  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeSizeUnits, exprVal, context.expressionContext() );
2651 
2652  // data defined shape rotation type?
2653  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShapeRotationType, context.expressionContext() );
2654  if ( exprVal.isValid() )
2655  {
2656  QString rotstr = exprVal.toString().trimmed();
2657  QgsDebugMsgLevel( QString( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
2658 
2659  if ( !rotstr.isEmpty() )
2660  {
2661  // "Sync"
2663 
2664  if ( rotstr.compare( QLatin1String( "Offset" ), Qt::CaseInsensitive ) == 0 )
2665  {
2667  }
2668  else if ( rotstr.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
2669  {
2671  }
2672  dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant( static_cast< int >( rottype ) ) );
2673  }
2674  }
2675 
2676  // data defined shape rotation?
2677  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShapeRotation, exprVal, context.expressionContext(), background.rotation() );
2678 
2679  // data defined shape offset?
2680  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeOffset, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodePoint( background.offset() ) );
2681 
2682  // data defined shape offset units?
2683  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeOffsetUnits, exprVal, context.expressionContext() );
2684 
2685  // data defined shape radii?
2686  dataDefinedValEval( DDSizeF, QgsPalLayerSettings::ShapeRadii, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeSize( background.radii() ) );
2687 
2688  // data defined shape radii units?
2689  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeRadiiUnits, exprVal, context.expressionContext() );
2690 
2691  // data defined shape blend mode?
2692  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShapeBlendMode, exprVal, context.expressionContext() );
2693 
2694  // data defined shape fill color?
2695  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShapeFillColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( background.fillColor() ) );
2696 
2697  // data defined shape stroke color?
2698  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShapeStrokeColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( background.strokeColor() ) );
2699 
2700  // data defined shape stroke width?
2701  dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShapeStrokeWidth, exprVal, context.expressionContext(), background.strokeWidth() );
2702 
2703  // data defined shape stroke width units?
2704  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeStrokeWidthUnits, exprVal, context.expressionContext() );
2705 
2706  // data defined shape join style?
2707  dataDefinedValEval( DDJoinStyle, QgsPalLayerSettings::ShapeJoinStyle, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodePenJoinStyle( background.joinStyle() ) );
2708 
2709 }
2710 
2711 void QgsPalLayerSettings::parseDropShadow( QgsRenderContext &context )
2712 {
2713  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2714 
2715  QgsTextShadowSettings shadow = mFormat.shadow();
2716 
2717  // data defined draw shadow?
2718  bool drawShadow = shadow.enabled();
2719  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShadowDraw, exprVal, context.expressionContext(), drawShadow ) )
2720  {
2721  drawShadow = exprVal.toBool();
2722  }
2723 
2724  if ( !drawShadow )
2725  {
2726  return;
2727  }
2728 
2729  // data defined shadow transparency?
2730  double shadowOpacity = shadow.opacity() * 100;
2731  if ( dataDefinedValEval( DDOpacity, QgsPalLayerSettings::ShadowOpacity, exprVal, context.expressionContext(), shadowOpacity ) )
2732  {
2733  shadowOpacity = exprVal.toDouble();
2734  }
2735 
2736  // data defined shadow offset distance?
2737  double shadowOffDist = shadow.offsetDistance();
2738  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowOffsetDist, exprVal, context.expressionContext(), shadowOffDist ) )
2739  {
2740  shadowOffDist = exprVal.toDouble();
2741  }
2742 
2743  // data defined shadow offset distance?
2744  double shadowRad = shadow.blurRadius();
2745  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRad ) )
2746  {
2747  shadowRad = exprVal.toDouble();
2748  }
2749 
2750  drawShadow = ( drawShadow && shadowOpacity > 0 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
2751 
2752  if ( !drawShadow )
2753  {
2754  dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
2755  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOpacity );
2756  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOffsetDist );
2757  dataDefinedValues.remove( QgsPalLayerSettings::ShadowRadius );
2758  return; // don't bother evaluating values that won't be used
2759  }
2760 
2761  // data defined shadow under type?
2762  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShadowUnder, context.expressionContext() );
2763  if ( exprVal.isValid() )
2764  {
2765  QString str = exprVal.toString().trimmed();
2766  QgsDebugMsgLevel( QString( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
2767 
2768  if ( !str.isEmpty() )
2769  {
2770  // "Lowest"
2772 
2773  if ( str.compare( QLatin1String( "Text" ), Qt::CaseInsensitive ) == 0 )
2774  {
2776  }
2777  else if ( str.compare( QLatin1String( "Buffer" ), Qt::CaseInsensitive ) == 0 )
2778  {
2780  }
2781  else if ( str.compare( QLatin1String( "Background" ), Qt::CaseInsensitive ) == 0 )
2782  {
2784  }
2785  dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant( static_cast< int >( shdwtype ) ) );
2786  }
2787  }
2788 
2789  // data defined shadow offset angle?
2790  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShadowOffsetAngle, exprVal, context.expressionContext(), shadow.offsetAngle() );
2791 
2792  // data defined shadow offset units?
2793  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowOffsetUnits, exprVal, context.expressionContext() );
2794 
2795  // data defined shadow radius?
2796  dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadow.blurRadius() );
2797 
2798  // data defined shadow radius units?
2799  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowRadiusUnits, exprVal, context.expressionContext() );
2800 
2801  // data defined shadow scale? ( gui bounds to 0-2000, no upper bound here )
2802  dataDefinedValEval( DDIntPos, QgsPalLayerSettings::ShadowScale, exprVal, context.expressionContext(), shadow.scale() );
2803 
2804  // data defined shadow color?
2805  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShadowColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( shadow.color() ) );
2806 
2807  // data defined shadow blend mode?
2808  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShadowBlendMode, exprVal, context.expressionContext() );
2809 }
2810 
2811 // -------------
2812 
2813 
2815 {
2816  return layer->labelsEnabled() || layer->diagramsEnabled();
2817 }
2818 
2819 
2820 bool QgsPalLabeling::geometryRequiresPreparation( const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, const QgsGeometry &clipGeometry )
2821 {
2822  if ( geometry.isNull() )
2823  {
2824  return false;
2825  }
2826 
2827  //requires reprojection
2828  if ( ct.isValid() && !ct.isShortCircuited() )
2829  return true;
2830 
2831  //requires rotation
2832  const QgsMapToPixel &m2p = context.mapToPixel();
2833  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
2834  return true;
2835 
2836  //requires clip
2837  if ( !clipGeometry.isNull() && !clipGeometry.boundingBox().contains( geometry.boundingBox() ) )
2838  return true;
2839 
2840  //requires fixing
2841  if ( geometry.type() == QgsWkbTypes::PolygonGeometry && !geometry.isGeosValid() )
2842  return true;
2843 
2844  return false;
2845 }
2846 
2847 QStringList QgsPalLabeling::splitToLines( const QString &text, const QString &wrapCharacter )
2848 {
2849  QStringList multiLineSplit;
2850  if ( !wrapCharacter.isEmpty() && wrapCharacter != QLatin1String( "\n" ) )
2851  {
2852  //wrap on both the wrapchr and new line characters
2853  Q_FOREACH ( const QString &line, text.split( wrapCharacter ) )
2854  {
2855  multiLineSplit.append( line.split( '\n' ) );
2856  }
2857  }
2858  else
2859  {
2860  multiLineSplit = text.split( '\n' );
2861  }
2862 
2863  return multiLineSplit;
2864 }
2865 
2866 QStringList QgsPalLabeling::splitToGraphemes( const QString &text )
2867 {
2868  QStringList graphemes;
2869  QTextBoundaryFinder boundaryFinder( QTextBoundaryFinder::Grapheme, text );
2870  int currentBoundary = -1;
2871  int previousBoundary = 0;
2872  while ( ( currentBoundary = boundaryFinder.toNextBoundary() ) > 0 )
2873  {
2874  graphemes << text.mid( previousBoundary, currentBoundary - previousBoundary );
2875  previousBoundary = currentBoundary;
2876  }
2877  return graphemes;
2878 }
2879 
2881 {
2882  if ( geometry.isNull() )
2883  {
2884  return QgsGeometry();
2885  }
2886 
2887  //don't modify the feature's geometry so that geometry based expressions keep working
2888  QgsGeometry geom = geometry;
2889 
2890  //reproject the geometry if necessary
2891  if ( ct.isValid() && !ct.isShortCircuited() )
2892  {
2893  try
2894  {
2895  geom.transform( ct );
2896  }
2897  catch ( QgsCsException &cse )
2898  {
2899  Q_UNUSED( cse );
2900  QgsDebugMsgLevel( QString( "Ignoring feature due to transformation exception" ), 4 );
2901  return QgsGeometry();
2902  }
2903  // geometry transforms may result in nan points, remove these
2904  geom.filterVertices( []( const QgsPoint & point )->bool
2905  {
2906  return std::isfinite( point.x() ) && std::isfinite( point.y() );
2907  } );
2908  if ( QgsCurvePolygon *cp = qgsgeometry_cast< QgsCurvePolygon * >( geom.get() ) )
2909  cp->removeInvalidRings();
2910  }
2911 
2912  // Rotate the geometry if needed, before clipping
2913  const QgsMapToPixel &m2p = context.mapToPixel();
2914  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
2915  {
2916  QgsPointXY center = context.extent().center();
2917 
2918  if ( ct.isValid() && !ct.isShortCircuited() )
2919  {
2920  try
2921  {
2922  center = ct.transform( center );
2923  }
2924  catch ( QgsCsException &cse )
2925  {
2926  Q_UNUSED( cse );
2927  QgsDebugMsgLevel( QString( "Ignoring feature due to transformation exception" ), 4 );
2928  return QgsGeometry();
2929  }
2930  }
2931 
2932  if ( geom.rotate( m2p.mapRotation(), center ) )
2933  {
2934  QgsDebugMsg( QString( "Error rotating geometry" ).arg( geom.asWkt() ) );
2935  return QgsGeometry();
2936  }
2937  }
2938 
2939  // fix invalid polygons
2940  if ( geom.type() == QgsWkbTypes::PolygonGeometry && !geom.isGeosValid() )
2941  {
2942  QgsGeometry validGeom = geom.makeValid();
2943  if ( validGeom.isNull() )
2944  {
2945  QgsDebugMsg( QString( "Could not repair geometry: %1" ).arg( validGeom.lastError() ) );
2946  return QgsGeometry();
2947  }
2948  geom = validGeom;
2949  }
2950 
2951  if ( !clipGeometry.isNull() &&
2952  ( ( qgsDoubleNear( m2p.mapRotation(), 0 ) && !clipGeometry.boundingBox().contains( geom.boundingBox() ) )
2953  || ( !qgsDoubleNear( m2p.mapRotation(), 0 ) && !clipGeometry.contains( geom ) ) ) )
2954  {
2955  QgsGeometry clipGeom = geom.intersection( clipGeometry ); // creates new geometry
2956  if ( clipGeom.isNull() )
2957  {
2958  return QgsGeometry();
2959  }
2960  geom = clipGeom;
2961  }
2962 
2963  return geom;
2964 }
2965 
2966 bool QgsPalLabeling::checkMinimumSizeMM( const QgsRenderContext &context, const QgsGeometry &geom, double minSize )
2967 {
2968  if ( minSize <= 0 )
2969  {
2970  return true;
2971  }
2972 
2973  if ( geom.isNull() )
2974  {
2975  return false;
2976  }
2977 
2978  QgsWkbTypes::GeometryType featureType = geom.type();
2979  if ( featureType == QgsWkbTypes::PointGeometry ) //minimum size does not apply to point features
2980  {
2981  return true;
2982  }
2983 
2984  double mapUnitsPerMM = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
2985  if ( featureType == QgsWkbTypes::LineGeometry )
2986  {
2987  double length = geom.length();
2988  if ( length >= 0.0 )
2989  {
2990  return ( length >= ( minSize * mapUnitsPerMM ) );
2991  }
2992  }
2993  else if ( featureType == QgsWkbTypes::PolygonGeometry )
2994  {
2995  double area = geom.area();
2996  if ( area >= 0.0 )
2997  {
2998  return ( std::sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
2999  }
3000  }
3001  return true; //should never be reached. Return true in this case to label such geometries anyway.
3002 }
3003 
3004 
3005 void QgsPalLabeling::dataDefinedTextStyle( QgsPalLayerSettings &tmpLyr,
3006  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3007 {
3008  QgsTextFormat format = tmpLyr.format();
3009  bool changed = false;
3010 
3011  //font color
3012  if ( ddValues.contains( QgsPalLayerSettings::Color ) )
3013  {
3014  QVariant ddColor = ddValues.value( QgsPalLayerSettings::Color );
3015  format.setColor( ddColor.value<QColor>() );
3016  changed = true;
3017  }
3018 
3019  //font transparency
3020  if ( ddValues.contains( QgsPalLayerSettings::FontOpacity ) )
3021  {
3022  format.setOpacity( ddValues.value( QgsPalLayerSettings::FontOpacity ).toDouble() / 100.0 );
3023  changed = true;
3024  }
3025 
3026  //font blend mode
3027  if ( ddValues.contains( QgsPalLayerSettings::FontBlendMode ) )
3028  {
3029  format.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::FontBlendMode ).toInt() ) );
3030  changed = true;
3031  }
3032 
3033  if ( changed )
3034  {
3035  tmpLyr.setFormat( format );
3036  }
3037 }
3038 
3039 void QgsPalLabeling::dataDefinedTextFormatting( QgsPalLayerSettings &tmpLyr,
3040  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3041 {
3042  if ( ddValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
3043  {
3044  tmpLyr.wrapChar = ddValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
3045  }
3046 
3047  if ( !tmpLyr.wrapChar.isEmpty() || tmpLyr.getLabelExpression()->expression().contains( QLatin1String( "wordwrap" ) ) )
3048  {
3049 
3050  if ( ddValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
3051  {
3052  QgsTextFormat format = tmpLyr.format();
3053  format.setLineHeight( ddValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble() );
3054  tmpLyr.setFormat( format );
3055  }
3056 
3057  if ( ddValues.contains( QgsPalLayerSettings::MultiLineAlignment ) )
3058  {
3059  tmpLyr.multilineAlign = static_cast< QgsPalLayerSettings::MultiLineAlign >( ddValues.value( QgsPalLayerSettings::MultiLineAlignment ).toInt() );
3060  }
3061 
3062  }
3063 
3064  if ( ddValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
3065  {
3066  tmpLyr.addDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
3067  }
3068 
3069  if ( tmpLyr.addDirectionSymbol )
3070  {
3071 
3072  if ( ddValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
3073  {
3074  tmpLyr.leftDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
3075  }
3076  if ( ddValues.contains( QgsPalLayerSettings::DirSymbRight ) )
3077  {
3078  tmpLyr.rightDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
3079  }
3080 
3081  if ( ddValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
3082  {
3083  tmpLyr.placeDirectionSymbol = static_cast< QgsPalLayerSettings::DirectionSymbols >( ddValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt() );
3084  }
3085 
3086  if ( ddValues.contains( QgsPalLayerSettings::DirSymbReverse ) )
3087  {
3088  tmpLyr.reverseDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbReverse ).toBool();
3089  }
3090 
3091  }
3092 }
3093 
3094 void QgsPalLabeling::dataDefinedTextBuffer( QgsPalLayerSettings &tmpLyr,
3095  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3096 {
3097  QgsTextBufferSettings buffer = tmpLyr.format().buffer();
3098  bool changed = false;
3099 
3100  //buffer draw
3101  if ( ddValues.contains( QgsPalLayerSettings::BufferDraw ) )
3102  {
3103  buffer.setEnabled( ddValues.value( QgsPalLayerSettings::BufferDraw ).toBool() );
3104  changed = true;
3105  }
3106 
3107  if ( !buffer.enabled() )
3108  {
3109  if ( changed )
3110  {
3111  QgsTextFormat format = tmpLyr.format();
3112  format.setBuffer( buffer );
3113  tmpLyr.setFormat( format );
3114  }
3115 
3116  // tmpLyr.bufferSize > 0.0 && tmpLyr.bufferTransp < 100 figured in during evaluation
3117  return; // don't continue looking for unused values
3118  }
3119 
3120  //buffer size
3121  if ( ddValues.contains( QgsPalLayerSettings::BufferSize ) )
3122  {
3123  buffer.setSize( ddValues.value( QgsPalLayerSettings::BufferSize ).toDouble() );
3124  changed = true;
3125  }
3126 
3127  //buffer opacity
3128  if ( ddValues.contains( QgsPalLayerSettings::BufferOpacity ) )
3129  {
3130  buffer.setOpacity( ddValues.value( QgsPalLayerSettings::BufferOpacity ).toDouble() / 100.0 );
3131  changed = true;
3132  }
3133 
3134  //buffer size units
3135  if ( ddValues.contains( QgsPalLayerSettings::BufferUnit ) )
3136  {
3137  QgsUnitTypes::RenderUnit bufunit = static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::BufferUnit ).toInt() );
3138  buffer.setSizeUnit( bufunit );
3139  changed = true;
3140  }
3141 
3142  //buffer color
3143  if ( ddValues.contains( QgsPalLayerSettings::BufferColor ) )
3144  {
3145  QVariant ddColor = ddValues.value( QgsPalLayerSettings::BufferColor );
3146  buffer.setColor( ddColor.value<QColor>() );
3147  changed = true;
3148  }
3149 
3150  //buffer pen join style
3151  if ( ddValues.contains( QgsPalLayerSettings::BufferJoinStyle ) )
3152  {
3153  buffer.setJoinStyle( static_cast< Qt::PenJoinStyle >( ddValues.value( QgsPalLayerSettings::BufferJoinStyle ).toInt() ) );
3154  changed = true;
3155  }
3156 
3157  //buffer blend mode
3158  if ( ddValues.contains( QgsPalLayerSettings::BufferBlendMode ) )
3159  {
3160  buffer.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::BufferBlendMode ).toInt() ) );
3161  changed = true;
3162  }
3163 
3164  if ( changed )
3165  {
3166  QgsTextFormat format = tmpLyr.format();
3167  format.setBuffer( buffer );
3168  tmpLyr.setFormat( format );
3169  }
3170 }
3171 
3172 void QgsPalLabeling::dataDefinedShapeBackground( QgsPalLayerSettings &tmpLyr,
3173  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3174 {
3175  QgsTextBackgroundSettings background = tmpLyr.format().background();
3176  bool changed = false;
3177 
3178  //shape draw
3179  if ( ddValues.contains( QgsPalLayerSettings::ShapeDraw ) )
3180  {
3181  background.setEnabled( ddValues.value( QgsPalLayerSettings::ShapeDraw ).toBool() );
3182  changed = true;
3183  }
3184 
3185  if ( !background.enabled() )
3186  {
3187  if ( changed )
3188  {
3189  QgsTextFormat format = tmpLyr.format();
3190  format.setBackground( background );
3191  tmpLyr.setFormat( format );
3192  }
3193  return; // don't continue looking for unused values
3194  }
3195 
3196  if ( ddValues.contains( QgsPalLayerSettings::ShapeKind ) )
3197  {
3198  background.setType( static_cast< QgsTextBackgroundSettings::ShapeType >( ddValues.value( QgsPalLayerSettings::ShapeKind ).toInt() ) );
3199  changed = true;
3200  }
3201 
3202  if ( ddValues.contains( QgsPalLayerSettings::ShapeSVGFile ) )
3203  {
3204  background.setSvgFile( ddValues.value( QgsPalLayerSettings::ShapeSVGFile ).toString() );
3205  changed = true;
3206  }
3207 
3208  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeType ) )
3209  {
3210  background.setSizeType( static_cast< QgsTextBackgroundSettings::SizeType >( ddValues.value( QgsPalLayerSettings::ShapeSizeType ).toInt() ) );
3211  changed = true;
3212  }
3213 
3214  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeX ) )
3215  {
3216  QSizeF size = background.size();
3217  size.setWidth( ddValues.value( QgsPalLayerSettings::ShapeSizeX ).toDouble() );
3218  background.setSize( size );
3219  changed = true;
3220  }
3221  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeY ) )
3222  {
3223  QSizeF size = background.size();
3224  size.setHeight( ddValues.value( QgsPalLayerSettings::ShapeSizeY ).toDouble() );
3225  background.setSize( size );
3226  changed = true;
3227  }
3228 
3229  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeUnits ) )
3230  {
3231  background.setSizeUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeSizeUnits ).toInt() ) );
3232  changed = true;
3233  }
3234 
3235  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotationType ) )
3236  {
3237  background.setRotationType( static_cast< QgsTextBackgroundSettings::RotationType >( ddValues.value( QgsPalLayerSettings::ShapeRotationType ).toInt() ) );
3238  changed = true;
3239  }
3240 
3241  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotation ) )
3242  {
3243  background.setRotation( ddValues.value( QgsPalLayerSettings::ShapeRotation ).toDouble() );
3244  changed = true;
3245  }
3246 
3247  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffset ) )
3248  {
3249  background.setOffset( ddValues.value( QgsPalLayerSettings::ShapeOffset ).toPointF() );
3250  changed = true;
3251  }
3252 
3253  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffsetUnits ) )
3254  {
3255  background.setOffsetUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeOffsetUnits ).toInt() ) );
3256  changed = true;
3257  }
3258 
3259  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadii ) )
3260  {
3261  background.setRadii( ddValues.value( QgsPalLayerSettings::ShapeRadii ).toSizeF() );
3262  changed = true;
3263  }
3264 
3265  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadiiUnits ) )
3266  {
3267  background.setRadiiUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeRadiiUnits ).toInt() ) );
3268  changed = true;
3269  }
3270 
3271  if ( ddValues.contains( QgsPalLayerSettings::ShapeBlendMode ) )
3272  {
3273  background.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::ShapeBlendMode ).toInt() ) );
3274  changed = true;
3275  }
3276 
3277  if ( ddValues.contains( QgsPalLayerSettings::ShapeFillColor ) )
3278  {
3279  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeFillColor );
3280  background.setFillColor( ddColor.value<QColor>() );
3281  changed = true;
3282  }
3283 
3284  if ( ddValues.contains( QgsPalLayerSettings::ShapeStrokeColor ) )
3285  {
3286  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeStrokeColor );
3287  background.setStrokeColor( ddColor.value<QColor>() );
3288  changed = true;
3289  }
3290 
3291  if ( ddValues.contains( QgsPalLayerSettings::ShapeOpacity ) )
3292  {
3293  background.setOpacity( ddValues.value( QgsPalLayerSettings::ShapeOpacity ).toDouble() / 100.0 );
3294  changed = true;
3295  }
3296 
3297  if ( ddValues.contains( QgsPalLayerSettings::ShapeStrokeWidth ) )
3298  {
3299  background.setStrokeWidth( ddValues.value( QgsPalLayerSettings::ShapeStrokeWidth ).toDouble() );
3300  changed = true;
3301  }
3302 
3303  if ( ddValues.contains( QgsPalLayerSettings::ShapeStrokeWidthUnits ) )
3304  {
3305  background.setStrokeWidthUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeStrokeWidthUnits ).toInt() ) );
3306  changed = true;
3307  }
3308 
3309  if ( ddValues.contains( QgsPalLayerSettings::ShapeJoinStyle ) )
3310  {
3311  background.setJoinStyle( static_cast< Qt::PenJoinStyle >( ddValues.value( QgsPalLayerSettings::ShapeJoinStyle ).toInt() ) );
3312  changed = true;
3313  }
3314 
3315  if ( changed )
3316  {
3317  QgsTextFormat format = tmpLyr.format();
3318  format.setBackground( background );
3319  tmpLyr.setFormat( format );
3320  }
3321 }
3322 
3323 void QgsPalLabeling::dataDefinedDropShadow( QgsPalLayerSettings &tmpLyr,
3324  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3325 {
3326  QgsTextShadowSettings shadow = tmpLyr.format().shadow();
3327  bool changed = false;
3328 
3329  //shadow draw
3330  if ( ddValues.contains( QgsPalLayerSettings::ShadowDraw ) )
3331  {
3332  shadow.setEnabled( ddValues.value( QgsPalLayerSettings::ShadowDraw ).toBool() );
3333  changed = true;
3334  }
3335 
3336  if ( !shadow.enabled() )
3337  {
3338  if ( changed )
3339  {
3340  QgsTextFormat format = tmpLyr.format();
3341  format.setShadow( shadow );
3342  tmpLyr.setFormat( format );
3343  }
3344  return; // don't continue looking for unused values
3345  }
3346 
3347  if ( ddValues.contains( QgsPalLayerSettings::ShadowUnder ) )
3348  {
3349  shadow.setShadowPlacement( static_cast< QgsTextShadowSettings::ShadowPlacement >( ddValues.value( QgsPalLayerSettings::ShadowUnder ).toInt() ) );
3350  changed = true;
3351  }
3352 
3353  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetAngle ) )
3354  {
3355  shadow.setOffsetAngle( ddValues.value( QgsPalLayerSettings::ShadowOffsetAngle ).toInt() );
3356  changed = true;
3357  }
3358 
3359  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetDist ) )
3360  {
3361  shadow.setOffsetDistance( ddValues.value( QgsPalLayerSettings::ShadowOffsetDist ).toDouble() );
3362  changed = true;
3363  }
3364 
3365  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetUnits ) )
3366  {
3367  shadow.setOffsetUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShadowOffsetUnits ).toInt() ) );
3368  changed = true;
3369  }
3370 
3371  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadius ) )
3372  {
3373  shadow.setBlurRadius( ddValues.value( QgsPalLayerSettings::ShadowRadius ).toDouble() );
3374  changed = true;
3375  }
3376 
3377  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadiusUnits ) )
3378  {
3379  shadow.setBlurRadiusUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShadowRadiusUnits ).toInt() ) );
3380  changed = true;
3381  }
3382 
3383  if ( ddValues.contains( QgsPalLayerSettings::ShadowColor ) )
3384  {
3385  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShadowColor );
3386  shadow.setColor( ddColor.value<QColor>() );
3387  changed = true;
3388  }
3389 
3390  if ( ddValues.contains( QgsPalLayerSettings::ShadowOpacity ) )
3391  {
3392  shadow.setOpacity( ddValues.value( QgsPalLayerSettings::ShadowOpacity ).toDouble() / 100.0 );
3393  changed = true;
3394  }
3395 
3396  if ( ddValues.contains( QgsPalLayerSettings::ShadowScale ) )
3397  {
3398  shadow.setScale( ddValues.value( QgsPalLayerSettings::ShadowScale ).toInt() );
3399  changed = true;
3400  }
3401 
3402 
3403  if ( ddValues.contains( QgsPalLayerSettings::ShadowBlendMode ) )
3404  {
3405  shadow.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::ShadowBlendMode ).toInt() ) );
3406  changed = true;
3407  }
3408 
3409  if ( changed )
3410  {
3411  QgsTextFormat format = tmpLyr.format();
3412  format.setShadow( shadow );
3413  tmpLyr.setFormat( format );
3414  }
3415 }
3416 
3417 
3418 void QgsPalLabeling::drawLabelCandidateRect( pal::LabelPosition *lp, QPainter *painter, const QgsMapToPixel *xform, QList<QgsLabelCandidate> *candidates )
3419 {
3420  QgsPointXY outPt = xform->transform( lp->getX(), lp->getY() );
3421 
3422  painter->save();
3423 
3424 #if 0 // TODO: generalize some of this
3425  double w = lp->getWidth();
3426  double h = lp->getHeight();
3427  double cx = lp->getX() + w / 2.0;
3428  double cy = lp->getY() + h / 2.0;
3429  double scale = 1.0 / xform->mapUnitsPerPixel();
3430  double rotation = xform->mapRotation();
3431  double sw = w * scale;
3432  double sh = h * scale;
3433  QRectF rect( -sw / 2, -sh / 2, sw, sh );
3434 
3435  painter->translate( xform->transform( QPointF( cx, cy ) ).toQPointF() );
3436  if ( rotation )
3437  {
3438  // Only if not horizontal
3439  if ( lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT &&
3440  lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT_OVER &&
3441  lp->getFeaturePart()->getLayer()->getArrangement() != P_HORIZ )
3442  {
3443  painter->rotate( rotation );
3444  }
3445  }
3446  painter->translate( rect.bottomLeft() );
3447  painter->rotate( -lp->getAlpha() * 180 / M_PI );
3448  painter->translate( -rect.bottomLeft() );
3449 #else
3450  QgsPointXY outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
3451  QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
3452  painter->translate( QPointF( outPt.x(), outPt.y() ) );
3453  painter->rotate( -lp->getAlpha() * 180 / M_PI );
3454 #endif
3455 
3456  if ( lp->conflictsWithObstacle() )
3457  {
3458  painter->setPen( QColor( 255, 0, 0, 64 ) );
3459  }
3460  else
3461  {
3462  painter->setPen( QColor( 0, 0, 0, 64 ) );
3463  }
3464  painter->drawRect( rect );
3465  painter->restore();
3466 
3467  // save the rect
3468  rect.moveTo( outPt.x(), outPt.y() );
3469  if ( candidates )
3470  candidates->append( QgsLabelCandidate( rect, lp->cost() * 1000 ) );
3471 
3472  // show all parts of the multipart label
3473  if ( lp->getNextPart() )
3474  drawLabelCandidateRect( lp->getNextPart(), painter, xform, candidates );
3475 }
3476 
3478 {
3479  mLabelSearchTree = new QgsLabelSearchTree();
3480 }
3481 
3483 {
3484  delete mLabelSearchTree;
3485  mLabelSearchTree = nullptr;
3486 }
3487 
3488 QList<QgsLabelPosition> QgsLabelingResults::labelsAtPosition( const QgsPointXY &p ) const
3489 {
3490  QList<QgsLabelPosition> positions;
3491 
3492  QList<QgsLabelPosition *> positionPointers;
3493  if ( mLabelSearchTree )
3494  {
3495  mLabelSearchTree->label( p, positionPointers );
3496  QList<QgsLabelPosition *>::const_iterator pointerIt = positionPointers.constBegin();
3497  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
3498  {
3499  positions.push_back( QgsLabelPosition( **pointerIt ) );
3500  }
3501  }
3502 
3503  return positions;
3504 }
3505 
3506 QList<QgsLabelPosition> QgsLabelingResults::labelsWithinRect( const QgsRectangle &r ) const
3507 {
3508  QList<QgsLabelPosition> positions;
3509 
3510  QList<QgsLabelPosition *> positionPointers;
3511  if ( mLabelSearchTree )
3512  {
3513  mLabelSearchTree->labelsInRect( r, positionPointers );
3514  QList<QgsLabelPosition *>::const_iterator pointerIt = positionPointers.constBegin();
3515  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
3516  {
3517  positions.push_back( QgsLabelPosition( **pointerIt ) );
3518  }
3519  }
3520 
3521  return positions;
3522 }
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
Label below point, slightly right of center.
Render units (eg mm/pixels/map units)
Definition: qgsproperty.h:63
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsFeatureId id
Definition: qgsfeature.h:71
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the labeling property definitions.
double xOffset
Horizontal offset of label.
The class is used as a container of context for various read/write operations on other objects...
Shape transparency (deprecated)
QColor strokeColor() const
Returns the color used for outlining the background shape.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
Definition: qgsrectangle.h:335
void setScale(int scale)
Sets the scaling used for the drop shadow (in percentage of original size).
double convertToMapUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to map units.
Shape size is determined by adding a buffer margin around text.
void setLineHeight(double height)
Sets the line height for text.
void setRadiiUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s radii.
A rectangle specified with double values.
Definition: qgsrectangle.h:40
void setStrokeWidth(double width)
Sets the width of the shape&#39;s stroke (stroke).
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
double rendererScale() const
Returns the renderer map scale.
Label on bottom-left of point.
double y
Definition: qgspoint.h:42
RotationType
Methods for determining the rotation of the background shape.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
double maxCurvedCharAngleOut
Maximum angle between outside curved label characters (valid range -20.0 to -95.0) ...
double maximumScale
The maximum map scale (i.e.
QString leftDirectionSymbol
String to use for left direction arrows.
void setOpacity(double opacity)
Sets the text&#39;s opacity.
Positive integer values (including 0)
Definition: qgsproperty.h:55
void setOffsetUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shadow&#39;s offset.
QgsUnitTypes::RenderUnit repeatDistanceUnit
Units for repeating labels for a single feature.
QSizeF radii() const
Returns the radii used for rounding the corners of shapes.
QSizeF size() const
Returns the size of the background shape.
double opacity() const
Returns the text&#39;s opacity.
const QgsVectorSimplifyMethod & vectorSimplifyMethod() const
Added in QGIS v2.4.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the shape&#39;s size.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
QPointF offset() const
Returns the offset used for drawing the background shape.
QColor fillColor() const
Returns the color used for filing the background shape.
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry() )...
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double...
static QString encodeSize(QSizeF size)
Encodes a QSizeF to a string.
void registerFeature(QgsFeature &f, QgsRenderContext &context, QgsLabelFeature **labelFeature=nullptr, QgsGeometry obstacleGeometry=QgsGeometry())
Register a feature for labeling.
double angleOffset
Label rotation, in degrees clockwise.
virtual bool readXml(const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions)
Reads property collection state from an XML element.
bool formatNumbers
Set to true to format numeric label text as numbers (e.g.
A class to query the labeling structure at a given point (small wrapper around pal RTree class) ...
void readXml(QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
Draw shadow under buffer.
UpsideDownLabels upsidedownLabels
Controls whether upside down labels are displayed and how they are handled.
double obstacleFactor
Obstacle factor, where 1.0 = default, < 1.0 more likely to be covered by labels, 1.0 less likely to be covered
Label on top-left of point.
SimplifyAlgorithm simplifyAlgorithm() const
Gets the local simplification algorithm of the vector layer managed.
void filterVertices(const std::function< bool(const QgsPoint &) > &filter)
Filters the vertices from the geometry in place, removing any which do not return true for the filter...
ShadowPlacement
Placement positions for text shadow.
Place direction symbols on below label.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsPointXY transform(const QgsPointXY &point, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
QgsTextShadowSettings & shadow()
Returns a reference to the text drop shadow settings.
double blurRadius() const
Returns the blur radius for the shadow.
static void drawLabelCandidateRect(pal::LabelPosition *lp, QPainter *painter, const QgsMapToPixel *xform, QList< QgsLabelCandidate > *candidates=nullptr)
double getY(int i=0) const
Returns the down-left y coordinate.
double y
Definition: qgspointxy.h:48
double opacity() const
Returns the background shape&#39;s opacity.
void setStrokeColor(const QColor &color)
Sets the color used for outlining the background shape.
void setSize(double size)
Sets the size of the buffer.
A class to represent a 2D point.
Definition: qgspointxy.h:43
double strokeWidth() const
Returns the width of the shape&#39;s stroke (stroke).
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:251
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the buffer size.
OperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
double offsetDistance() const
Returns the distance for offsetting the position of the shadow from the text.
bool obstacle
True if features for layer are obstacles to labels of other layers.
int decimals
Number of decimal places to show for numeric labels.
void setJoinStyle(Qt::PenJoinStyle style)
Sets the join style used for drawing the buffer.
double repeatDistance
Distance for repeating labels for a single feature.
QVariant evaluate()
Evaluate the feature and return the result.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
QuadrantPosition quadOffset
Sets the quadrant in which to offset labels from feature.
QgsUnitTypes::RenderUnit offsetUnits
Units for offsets of label.
void setOffsetUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s offset.
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
Shadow transparency (deprecated)
QColor color() const
Returns the color that text will be rendered in.
double yOffset
Vertical offset of label.
Class that adds extra information to QgsLabelFeature for text labels.
Curve polygon geometry type.
QString evalErrorString() const
Returns evaluation error.
void setBlurRadius(double blurRadius)
Sets the blur radius for the shadow.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void setDefinedFont(const QFont &f)
Sets font to be used for rendering.
void setOpacity(double opacity)
Sets the shadow&#39;s opacity.
X-coordinate data defined label position.
Min scale (deprecated, for old project compatibility only)
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
Label on top of point, slightly right of center.
bool addDirectionSymbol
If true, &#39;<&#39; or &#39;>&#39; (or custom strings set via leftDirectionSymbol and rightDirectionSymbol) will be ...
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:104
bool drawLabels
Whether to draw labels for this layer.
void setBackground(const QgsTextBackgroundSettings &backgroundSettings)
Sets the text&#39;s background settings.q.
bool mergeLines
True if connected line features with identical label text should be merged prior to generating label ...
Color with alpha channel.
Definition: qgsproperty.h:64
void setJoinStyle(Qt::PenJoinStyle style)
Sets the join style used for drawing the background shape.
QgsMapUnitScale repeatDistanceMapUnitScale
Map unit scale for repeating labels for a single feature.
MultiLineAlign multilineAlign
Horizontal alignment of multi-line labels.
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
FeaturePart * getFeaturePart()
Returns the feature corresponding to this labelposition.
QgsGeometry intersection(const QgsGeometry &geometry) const
Returns a geometry representing the points shared by this geometry and other.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
Positive double value (including 0)
Definition: qgsproperty.h:58
Container for settings relating to a text background object.
Percentage of another measurement (e.g., canvas size, feature size)
Definition: qgsunittypes.h:105
static int sizeToPixel(double size, const QgsRenderContext &c, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &mapUnitScale=QgsMapUnitScale())
Calculates pixel size (considering output size should be in pixel or map units, scale factors and opt...
void setBlurRadiusUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shadow&#39;s blur radius.
QVector< PredefinedPointPosition > predefinedPositionOrder
Ordered list of predefined label positions for points.
SimplifyAlgorithm
Types of simplification algorithms that can be used.
static QStringList splitToLines(const QString &text, const QString &wrapCharacter)
Splits a text string to a list of separate lines, using a specified wrap character.
QString parserErrorString() const
Returns parser error.
double maxCurvedCharAngleIn
Maximum angle between inside curved label characters (valid range 20.0 to 60.0).
QgsCoordinateTransform ct
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
bool reverseDirectionSymbol
True if direction symbols should be reversed.
Rotation (value between 0-360 degrees)
Definition: qgsproperty.h:60
double maxScale
The maximum scale, or 0.0 if unset.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
bool isGeosValid() const
Checks validity of the geometry using GEOS.
ShapeType type() const
Returns the type of background shape (e.g., square, ellipse, SVG).
void setOffset(QPointF offset)
Sets the offset used for drawing the background shape.
Any string value.
Definition: qgsproperty.h:61
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
double zIndex
Z-Index of label, where labels with a higher z-index are rendered on top of labels with a lower z-ind...
double mapRotation() const
Returns current map rotation in degrees.
void setHasFixedPosition(bool enabled)
Sets whether the label should use a fixed position instead of being automatically placed...
QgsStringReplacementCollection substitutions
Substitution collection for automatic text substitution with labels.
Qt::PenJoinStyle joinStyle() const
Returns the buffer join style.
static QString encodeColor(const QColor &color)
static bool staticWillUseLayer(QgsVectorLayer *layer)
called to find out whether the layer is used for labeling
void writeXml(QDomElement &elem, QDomDocument &doc) const
Writes the collection state to an XML element.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection...
Place direction symbols on left/right of label.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:36
QgsPointXY transform(const QgsPointXY &p) const
Transform the point from map (world) coordinates to device coordinates.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the background shape.
Shape rotation is a fixed angle.
Property
Data definable properties.
double cost() const
Returns the candidate label position&#39;s geographical cost.
No simplification can be applied.
ObstacleType obstacleType
Controls how features act as obstacles for labels.
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:148
bool displayAll
If true, all features will be labelled even when overlaps occur.
QColor color() const
Returns the color of the drop shadow.
void setIsObstacle(bool enabled)
Sets whether the feature will act as an obstacle for labels.
void setOffsetDistance(double distance)
Sets the distance for offsetting the position of the shadow from the text.
Label on left of point.
static QString capitalize(const QString &string, Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
Offset distance applies from point geometry.
QList< QgsLabelPosition > labelsAtPosition(const QgsPointXY &p) const
Returns infos about labels at a given (map) position.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
double tolerance() const
Gets the tolerance of simplification in map units. Represents the maximum distance in map units betwe...
const QgsRectangle & extent() const
int fontMaxPixelSize
Maximum pixel size for showing rendered map unit labels (1 - 10000).
static bool fontFamilyOnSystem(const QString &family)
Check whether font family is on system in a quick manner, which does not compare [foundry].
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent...
Maximum map scale (ie most "zoomed in")
The geometries can be fully simplified by its BoundingBox.
double getHeight() const
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the drop shadow.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:201
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context)
Write settings into a DOM element.
void setColor(const QColor &color)
Sets the color for the drop shadow.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setDataDefinedValues(const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > &values)
Sets data-defined values.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:79
void setEnabled(bool enabled)
Sets whether the text shadow will be drawn.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
Horizontal alignment for data defined label position (Left, Center, Right)
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
LabelPosition * getNextPart() const
const QgsMapToPixel * xform
Property requires a numeric value.
Definition: qgsproperty.h:98
void setColor(const QColor &color)
Sets the color that text will be rendered in.
void calculateInfo(bool curvedLabeling, QFontMetricsF *fm, const QgsMapToPixel *xform, double fontScale, double maxinangle, double maxoutangle)
calculate data for info(). setDefinedFont() must have been called already.
Draw shadow under text.
QString svgFile() const
Returns the absolute path to the background SVG file, if set.
Qt::PenJoinStyle joinStyle() const
Returns the join style used for drawing the background shape.
QgsTextBackgroundSettings & background()
Returns a reference to the text background settings.
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
Upside-down labels (90 <= angle < 270) are shown upright.
SizeType
Methods for determining the background shape size.
double opacity() const
Returns the buffer opacity.
OffsetType
Behavior modifier for label offset and distance, only applies in some label placement modes...
QgsFeature * mCurFeat
static QPainter::CompositionMode decodeBlendMode(const QString &s)
void calculateLabelSize(const QFontMetricsF *fm, QString text, double &labelX, double &labelY, QgsFeature *f=nullptr, QgsRenderContext *context=nullptr)
void removeCustomProperty(const QString &key)
Remove a custom property from layer.
void setSize(QSizeF size)
Sets the size of the background shape.
void clear() override
Removes all properties from the collection.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
double mapUnitsPerPixel() const
Returns current map units per pixel.
A store for object properties.
Definition: qgsproperty.h:229
void setRadii(QSizeF radii)
Sets the radii used for rounding the corners of shapes.
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspointxy.h:190
Implementation of GeometrySimplifier using the "MapToPixel" algorithm.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
Double value (including negative values)
Definition: qgsproperty.h:57
QgsWkbTypes::GeometryType type() const
Returns type of the geometry as a QgsWkbTypes::GeometryType.
Minimum map scale (ie most "zoomed out")
Convert just the first letter of each word to uppercase, leave the rest untouched.
double length() const
Returns the length of geometry using GEOS.
Convert all characters to uppercase.
void setSizeType(SizeType type)
Sets the method used to determine the size of the background shape (e.g., fixed size or buffer around...
const QgsTextFormat & format() const
Returns the label text formatting settings, e.g., font settings, buffer settings, etc...
double x
Definition: qgspointxy.h:47
Definition for a property.
Definition: qgsproperty.h:46
QList< QgsLabelPosition > labelsWithinRect(const QgsRectangle &r) const
Returns infos about labels within a given (map) rectangle.
Capitalization
Capitalization options.
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
int maxNumLabels
The maximum number of labels which should be drawn for this layer.
void setType(ShapeType type)
Sets the type of background shape to draw (e.g., square, ellipse, SVG).
Place direction symbols on above label.
Draw shadow below all text components.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QString expression() const
Returns the original, unmodified expression string.
QgsExpressionContext & expressionContext()
Gets the expression context.
QString asWkt(int precision=17) const
Exports the geometry to WKT.
Label rotation (deprecated, for old project compatibility only)
static QSizeF decodeSize(const QString &string)
Decodes a QSizeF from a string.
double rotation() const
Returns the rotation for the background shape, in degrees clockwise.
bool plusSign
Whether &#39;+&#39; signs should be prepended to positive numeric labels.
double lineHeight() const
Returns the line height for text.
void setEnabled(bool enabled)
Sets whether the text background will be drawn.
unsigned int placementFlags
int offsetAngle() const
Returns the angle for offsetting the position of the shadow from the text.
ShapeType
Background shape types.
QString wrapChar
Wrapping character string.
QgsPalLayerSettings & operator=(const QgsPalLayerSettings &s)
copy operator - only copies the permanent members
bool valueAsBool(int key, const QgsExpressionContext &context, bool defaultValue=false, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an boolean...
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:137
Placement
Placement modes which determine how label candidates are generated for a feature. ...
static QString encodePredefinedPositionOrder(const QVector< QgsPalLayerSettings::PredefinedPointPosition > &positions)
Encodes an ordered list of predefined point label positions to a string.
QString rightDirectionSymbol
String to use for right direction arrows.
bool preserveRotation
True if label rotation should be preserved during label pin/unpin operations.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string...
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsExpression * getLabelExpression()
Returns the QgsExpression for this label settings.
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point...
This class contains information how to simplify geometries fetched from a vector layer.
Contains information about the context of a rendering operation.
Shape rotation is offset from text rotation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
bool centroidWhole
True if feature centroid should be calculated from the whole feature, or false if only the visible pa...
bool scaleVisibility
Set to true to limit label visibility to a range of scales.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the size.
The QgsLabelFeature class describes a feature that should be used within the labeling engine...
const QgsMapToPixel & mapToPixel() const
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0)
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
Definition: qgsgeos.cpp:166
QgsGeometry extentGeom
double getAlpha() const
Returns the angle to rotate text (in rad).
Property requires a string value.
Definition: qgsproperty.h:91
bool enabled() const
Returns whether the shadow is enabled.
Mixed case, ie no change.
QgsMapUnitScale distMapUnitScale
Map unit scale for label feature distance.
bool fitInPolygonOnly
True if only labels which completely fit within a polygon are allowed.
double getWidth() const
Container for settings relating to a text shadow.
QColor color() const
Returns the color of the buffer.
bool conflictsWithObstacle() const
Returns whether the position is marked as conflicting with an obstacle feature.
double getX(int i=0) const
Returns the down-left x coordinate.
double size() const
Returns the size of the buffer.
Container for settings relating to a text buffer.
QgsGeometry makeValid() const
Attempts to make an invalid geometry valid without losing vertices.
bool forceLocalOptimization() const
Gets where the simplification executes, after fetch the geometries from provider, or when supported...
void setFillColor(const QColor &color)
Sets the color used for filing the background shape.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
OffsetType offsetType
Offset type for layer (only applies in certain placement modes)
double dist
Distance from feature to the label.
double size() const
Returns the size for rendered text.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
bool useSubstitutions
True if substitutions should be applied.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s stroke width.
bool enabled() const
Returns whether the background is enabled.
static QStringList splitToGraphemes(const QString &text)
Splits a text string to a list of graphemes, which are the smallest allowable character divisions in ...
SimplifyHints simplifyHints() const
Gets the simplification hints of the vector layer managed.
Class for doing transforms between two map coordinate systems.
SizeType sizeType() const
Returns the method used to determine the size of the background shape (e.g., fixed size or buffer aro...
LabelPosition is a candidate feature label position.
Definition: labelposition.h:55
bool enabled() const
Returns whether the buffer is enabled.
int scale() const
Returns the scaling used for the drop shadow (in percentage of original size).
Shape rotation is synced with text rotation.
void setShadowPlacement(QgsTextShadowSettings::ShadowPlacement placement)
Sets the placement for the drop shadow.
Label on right of point.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the text.
void setBuffer(const QgsTextBufferSettings &bufferSettings)
Sets the text&#39;s buffer settings.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
bool limitNumLabels
True if the number of labels drawn should be limited.
static bool geometryRequiresPreparation(const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, const QgsGeometry &clipGeometry=QgsGeometry())
Checks whether a geometry requires preparation before registration with PAL.
Draw shadow under background shape.
bool isExpression
True if this label is made from a expression string, e.g., FieldName || &#39;mm&#39;.
Convert all characters to lowercase.
void setShadow(const QgsTextShadowSettings &shadowSettings)
Sets the text&#39;s drop shadow settings.
static QgsGeometry prepareGeometry(const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, const QgsGeometry &clipGeometry=QgsGeometry())
Prepares a geometry for registration with PAL.
void readXml(const QDomElement &elem)
Reads the collection state from an XML element.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
static QVector< QgsPalLayerSettings::PredefinedPointPosition > decodePredefinedPositionOrder(const QString &positionString)
Decodes a string to an ordered list of predefined point label positions.
QString process(const QString &input) const
Processes a given input string, applying any valid replacements which should be made using QgsStringR...
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s path from its name.
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:229
double area() const
Returns the area of the geometry using GEOS.
Y-coordinate data defined label position.
Container for all settings relating to text rendering.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the size of rendered text.
Path to an SVG file.
Definition: qgsproperty.h:77
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the buffer.
bool centroidInside
True if centroid positioned labels must be placed inside their corresponding feature polygon...
Max scale (deprecated, for old project compatibility only)
bool contains(const QgsPointXY *p) const
Tests for containment of a point (uses GEOS)
void setSvgFile(const QString &file)
Sets the path to the background SVG file.
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Represents a vector layer which manages a vector based data sets.
double minScale
The minimum scale, or 0.0 if unset.
void setOpacity(double opacity)
Sets the buffer opacity.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:255
virtual bool writeXml(QDomElement &collectionElem, const QgsPropertiesDefinition &definitions) const
Writes the current state of the property collection into an XML element.
Square - buffered sizes only.
QString updateDataDefinedString(const QString &value)
int valueAsInt(int key, const QgsExpressionContext &context, int defaultValue=0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an integer...
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
double minFeatureSize
Minimum feature size (in millimeters) for a feature to be labelled.
Vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top) ...
QString namedStyle() const
Returns the named style for the font used for rendering text (e.g., "bold").
QFont font() const
Returns the font used for rendering text.
QgsPointXY toMapCoordinatesF(double x, double y) const
Transform device coordinates to map (world) coordinates.
void setRotationType(RotationType type)
Sets the method used for rotating the background shape.
int fontMinPixelSize
Minimum pixel size for showing rendered map unit labels (1 - 1000).
void setColor(const QColor &color)
Sets the color for the buffer.
int priority
Label priority.
bool labelPerPart
True if every part of a multi-part feature should be labeled.
QgsUnitTypes::RenderUnit distUnits
Units the distance from feature to the label.
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
double opacity() const
Returns the shadow&#39;s opacity.
QgsMapUnitScale labelOffsetMapUnitScale
Map unit scale for label offset.
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
2D size (width/height different)
Definition: qgsproperty.h:70
The QgsMargins class defines the four margins of a rectangle.
Definition: qgsmargins.h:37
void setOpacity(double opacity)
Sets the background shape&#39;s opacity.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:100
bool diagramsEnabled() const
Returns whether the layer contains diagrams which are enabled and should be drawn.
static QColor decodeColor(const QString &str)
Buffer transparency (deprecated)
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:208
Text transparency (deprecated)
double minimumScale
The minimum map scale (i.e.
QString fieldName
Name of field (or an expression) to use for label text.
bool fontLimitPixelSize
True if label sizes should be limited by pixel size.
ObstacleType
Valid obstacle types, which affect how features within the layer will act as obstacles for labels...
DirectionSymbols placeDirectionSymbol
Placement option for direction symbols.
void setOffsetAngle(int angle)
Sets the angle for offsetting the position of the shadow from the text.
void readFromLayer(QgsVectorLayer *layer)
Reads settings from a layer&#39;s custom properties (for QGIS 2.x projects).
Color with no alpha channel.
Definition: qgsproperty.h:65
void setRotation(double rotation)
Sets the rotation for the background shape, in degrees clockwise.
double x
Definition: qgspoint.h:41