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