QGIS API Documentation  3.0.2-Girona (307d082)
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->fields();
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  case DDSizeF:
2190  {
2191  QString ptstr = exprVal.toString().trimmed();
2192 
2193  if ( !ptstr.isEmpty() )
2194  {
2195  dataDefinedValues.insert( p, QVariant( QgsSymbolLayerUtils::decodeSize( ptstr ) ) );
2196  return true;
2197  }
2198  return false;
2199  }
2200  }
2201  }
2202  return false;
2203 }
2204 
2205 void QgsPalLayerSettings::parseTextStyle( QFont &labelFont,
2206  QgsUnitTypes::RenderUnit fontunits,
2207  QgsRenderContext &context )
2208 {
2209  // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
2210 
2211  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2212 
2213  // Two ways to generate new data defined font:
2214  // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
2215  // 2) Family + named style (bold or italic is ignored)
2216 
2217  // data defined font family?
2218  QString ddFontFamily;
2219  context.expressionContext().setOriginalValueVariable( labelFont.family() );
2220  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Family, context.expressionContext() );
2221  if ( exprVal.isValid() )
2222  {
2223  QString family = exprVal.toString().trimmed();
2224  QgsDebugMsgLevel( QString( "exprVal Font family:%1" ).arg( family ), 4 );
2225 
2226  if ( labelFont.family() != family )
2227  {
2228  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
2229  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
2230  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
2231  {
2232  ddFontFamily = family;
2233  }
2234  }
2235  }
2236 
2237  // data defined named font style?
2238  QString ddFontStyle;
2239  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::FontStyle, context.expressionContext() );
2240  if ( exprVal.isValid() )
2241  {
2242  QString fontstyle = exprVal.toString().trimmed();
2243  QgsDebugMsgLevel( QString( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
2244  ddFontStyle = fontstyle;
2245  }
2246 
2247  // data defined bold font style?
2248  context.expressionContext().setOriginalValueVariable( labelFont.bold() );
2249  bool ddBold = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Bold, context.expressionContext(), false );
2250 
2251  // data defined italic font style?
2252  context.expressionContext().setOriginalValueVariable( labelFont.italic() );
2253  bool ddItalic = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Italic, context.expressionContext(), false );
2254 
2255  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
2256  // (currently defaults to what has been read in from layer settings)
2257  QFont newFont;
2258  QFont appFont = QApplication::font();
2259  bool newFontBuilt = false;
2260  if ( ddBold || ddItalic )
2261  {
2262  // new font needs built, since existing style needs removed
2263  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
2264  newFontBuilt = true;
2265  newFont.setBold( ddBold );
2266  newFont.setItalic( ddItalic );
2267  }
2268  else if ( !ddFontStyle.isEmpty()
2269  && ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
2270  {
2271  if ( !ddFontFamily.isEmpty() )
2272  {
2273  // both family and style are different, build font from database
2274  QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
2275  if ( appFont != styledfont )
2276  {
2277  newFont = styledfont;
2278  newFontBuilt = true;
2279  }
2280  }
2281 
2282  // update the font face style
2283  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
2284  }
2285  else if ( !ddFontFamily.isEmpty() )
2286  {
2287  if ( ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
2288  {
2289  // just family is different, build font from database
2290  QFont styledfont = mFontDB.font( ddFontFamily, mFormat.namedStyle(), appFont.pointSize() );
2291  if ( appFont != styledfont )
2292  {
2293  newFont = styledfont;
2294  newFontBuilt = true;
2295  }
2296  }
2297  else
2298  {
2299  newFont = QFont( ddFontFamily );
2300  newFontBuilt = true;
2301  }
2302  }
2303 
2304  if ( newFontBuilt )
2305  {
2306  // copy over existing font settings
2307  //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
2308  newFont.setPixelSize( labelFont.pixelSize() );
2309  newFont.setUnderline( labelFont.underline() );
2310  newFont.setStrikeOut( labelFont.strikeOut() );
2311  newFont.setWordSpacing( labelFont.wordSpacing() );
2312  newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
2313 
2314  labelFont = newFont;
2315  }
2316 
2317  // data defined word spacing?
2318  double wordspace = labelFont.wordSpacing();
2319  context.expressionContext().setOriginalValueVariable( wordspace );
2320  wordspace = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::FontWordSpacing, context.expressionContext(), wordspace );
2321  labelFont.setWordSpacing( context.convertToPainterUnits( wordspace, fontunits, mFormat.sizeMapUnitScale() ) );
2322 
2323  // data defined letter spacing?
2324  double letterspace = labelFont.letterSpacing();
2325  context.expressionContext().setOriginalValueVariable( letterspace );
2326  letterspace = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::FontLetterSpacing, context.expressionContext(), letterspace );
2327  labelFont.setLetterSpacing( QFont::AbsoluteSpacing, context.convertToPainterUnits( letterspace, fontunits, mFormat.sizeMapUnitScale() ) );
2328 
2329  // data defined strikeout font style?
2330  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Strikeout ) )
2331  {
2332  context.expressionContext().setOriginalValueVariable( labelFont.strikeOut() );
2333  bool strikeout = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Strikeout, context.expressionContext(), false );
2334  labelFont.setStrikeOut( strikeout );
2335  }
2336 
2337  // data defined underline font style?
2338  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Underline ) )
2339  {
2340  context.expressionContext().setOriginalValueVariable( labelFont.underline() );
2341  bool underline = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Underline, context.expressionContext(), false );
2342  labelFont.setUnderline( underline );
2343  }
2344 
2345  // pass the rest on to QgsPalLabeling::drawLabeling
2346 
2347  // data defined font color?
2348  dataDefinedValEval( DDColor, QgsPalLayerSettings::Color, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( mFormat.color() ) );
2349 
2350  // data defined font opacity?
2351  dataDefinedValEval( DDOpacity, QgsPalLayerSettings::FontOpacity, exprVal, context.expressionContext(), mFormat.opacity() * 100 );
2352 
2353  // data defined font blend mode?
2354  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::FontBlendMode, exprVal, context.expressionContext() );
2355 
2356 }
2357 
2358 void QgsPalLayerSettings::parseTextBuffer( QgsRenderContext &context )
2359 {
2360  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2361 
2362  QgsTextBufferSettings buffer = mFormat.buffer();
2363 
2364  // data defined draw buffer?
2365  bool drawBuffer = mFormat.buffer().enabled();
2366  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::BufferDraw, exprVal, context.expressionContext(), buffer.enabled() ) )
2367  {
2368  drawBuffer = exprVal.toBool();
2369  }
2370 
2371  if ( !drawBuffer )
2372  {
2373  return;
2374  }
2375 
2376  // data defined buffer size?
2377  double bufrSize = buffer.size();
2378  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::BufferSize, exprVal, context.expressionContext(), buffer.size() ) )
2379  {
2380  bufrSize = exprVal.toDouble();
2381  }
2382 
2383  // data defined buffer transparency?
2384  double bufferOpacity = buffer.opacity() * 100;
2385  if ( dataDefinedValEval( DDOpacity, QgsPalLayerSettings::BufferOpacity, exprVal, context.expressionContext(), bufferOpacity ) )
2386  {
2387  bufferOpacity = exprVal.toDouble();
2388  }
2389 
2390  drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufferOpacity > 0 );
2391 
2392  if ( !drawBuffer )
2393  {
2394  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
2395  dataDefinedValues.remove( QgsPalLayerSettings::BufferSize );
2396  dataDefinedValues.remove( QgsPalLayerSettings::BufferOpacity );
2397  return; // don't bother evaluating values that won't be used
2398  }
2399 
2400  // data defined buffer units?
2401  dataDefinedValEval( DDUnits, QgsPalLayerSettings::BufferUnit, exprVal, context.expressionContext() );
2402 
2403  // data defined buffer color?
2404  dataDefinedValEval( DDColor, QgsPalLayerSettings::BufferColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( buffer.color() ) );
2405 
2406  // data defined buffer pen join style?
2407  dataDefinedValEval( DDJoinStyle, QgsPalLayerSettings::BufferJoinStyle, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodePenJoinStyle( buffer.joinStyle() ) );
2408 
2409  // data defined buffer blend mode?
2410  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::BufferBlendMode, exprVal, context.expressionContext() );
2411 }
2412 
2413 void QgsPalLayerSettings::parseTextFormatting( QgsRenderContext &context )
2414 {
2415  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2416 
2417  // data defined multiline wrap character?
2418  QString wrapchr = wrapChar;
2419  if ( dataDefinedValEval( DDString, QgsPalLayerSettings::MultiLineWrapChar, exprVal, context.expressionContext(), wrapChar ) )
2420  {
2421  wrapchr = exprVal.toString();
2422  }
2423 
2424  // data defined multiline height?
2425  dataDefinedValEval( DDDouble, QgsPalLayerSettings::MultiLineHeight, exprVal, context.expressionContext() );
2426 
2427  // data defined multiline text align?
2428  context.expressionContext().setOriginalValueVariable( mFormat.lineHeight() );
2429  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::MultiLineAlignment, context.expressionContext() );
2430  if ( exprVal.isValid() )
2431  {
2432  QString str = exprVal.toString().trimmed();
2433  QgsDebugMsgLevel( QString( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
2434 
2435  if ( !str.isEmpty() )
2436  {
2437  // "Left"
2439 
2440  if ( str.compare( QLatin1String( "Center" ), Qt::CaseInsensitive ) == 0 )
2441  {
2443  }
2444  else if ( str.compare( QLatin1String( "Right" ), Qt::CaseInsensitive ) == 0 )
2445  {
2446  aligntype = QgsPalLayerSettings::MultiRight;
2447  }
2448  else if ( str.compare( QLatin1String( "Follow" ), Qt::CaseInsensitive ) == 0 )
2449  {
2451  }
2452  dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant( static_cast< int >( aligntype ) ) );
2453  }
2454  }
2455 
2456  // data defined direction symbol?
2457  bool drawDirSymb = addDirectionSymbol;
2458  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbDraw, exprVal, context.expressionContext(), addDirectionSymbol ) )
2459  {
2460  drawDirSymb = exprVal.toBool();
2461  }
2462 
2463  if ( drawDirSymb )
2464  {
2465  // data defined direction left symbol?
2466  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbLeft, exprVal, context.expressionContext(), leftDirectionSymbol );
2467 
2468  // data defined direction right symbol?
2469  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbRight, exprVal, context.expressionContext(), rightDirectionSymbol );
2470 
2471  // data defined direction symbol placement?
2472  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::DirSymbPlacement, context.expressionContext() );
2473  if ( exprVal.isValid() )
2474  {
2475  QString str = exprVal.toString().trimmed();
2476  QgsDebugMsgLevel( QString( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
2477 
2478  if ( !str.isEmpty() )
2479  {
2480  // "LeftRight"
2482 
2483  if ( str.compare( QLatin1String( "Above" ), Qt::CaseInsensitive ) == 0 )
2484  {
2486  }
2487  else if ( str.compare( QLatin1String( "Below" ), Qt::CaseInsensitive ) == 0 )
2488  {
2490  }
2491  dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant( static_cast< int >( placetype ) ) );
2492  }
2493  }
2494 
2495  // data defined direction symbol reversed?
2496  dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbReverse, exprVal, context.expressionContext(), reverseDirectionSymbol );
2497  }
2498 
2499  // formatting for numbers is inline with generation of base label text and not passed to label painting
2500 }
2501 
2502 void QgsPalLayerSettings::parseShapeBackground( QgsRenderContext &context )
2503 {
2504  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2505 
2506  QgsTextBackgroundSettings background = mFormat.background();
2507 
2508  // data defined draw shape?
2509  bool drawShape = background.enabled();
2510  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShapeDraw, exprVal, context.expressionContext(), drawShape ) )
2511  {
2512  drawShape = exprVal.toBool();
2513  }
2514 
2515  if ( !drawShape )
2516  {
2517  return;
2518  }
2519 
2520  // data defined shape transparency?
2521  double shapeOpacity = background.opacity() * 100;
2522  if ( dataDefinedValEval( DDOpacity, QgsPalLayerSettings::ShapeOpacity, exprVal, context.expressionContext(), shapeOpacity ) )
2523  {
2524  shapeOpacity = 100.0 * exprVal.toDouble();
2525  }
2526 
2527  drawShape = ( drawShape && shapeOpacity > 0 ); // size is not taken into account (could be)
2528 
2529  if ( !drawShape )
2530  {
2531  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
2532  dataDefinedValues.remove( QgsPalLayerSettings::ShapeOpacity );
2533  return; // don't bother evaluating values that won't be used
2534  }
2535 
2536  // data defined shape kind?
2537  QgsTextBackgroundSettings::ShapeType shapeKind = background.type();
2538  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShapeKind, context.expressionContext() );
2539  if ( exprVal.isValid() )
2540  {
2541  QString skind = exprVal.toString().trimmed();
2542  QgsDebugMsgLevel( QString( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
2543 
2544  if ( !skind.isEmpty() )
2545  {
2546  // "Rectangle"
2548 
2549  if ( skind.compare( QLatin1String( "Square" ), Qt::CaseInsensitive ) == 0 )
2550  {
2552  }
2553  else if ( skind.compare( QLatin1String( "Ellipse" ), Qt::CaseInsensitive ) == 0 )
2554  {
2556  }
2557  else if ( skind.compare( QLatin1String( "Circle" ), Qt::CaseInsensitive ) == 0 )
2558  {
2560  }
2561  else if ( skind.compare( QLatin1String( "SVG" ), Qt::CaseInsensitive ) == 0 )
2562  {
2564  }
2565  shapeKind = shpkind;
2566  dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant( static_cast< int >( shpkind ) ) );
2567  }
2568  }
2569 
2570  // data defined shape SVG path?
2571  QString svgPath = background.svgFile();
2572  context.expressionContext().setOriginalValueVariable( svgPath );
2573  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShapeSVGFile, context.expressionContext() );
2574  if ( exprVal.isValid() )
2575  {
2576  QString svgfile = exprVal.toString().trimmed();
2577  QgsDebugMsgLevel( QString( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
2578 
2579  // '' empty paths are allowed
2580  svgPath = QgsSymbolLayerUtils::svgSymbolNameToPath( svgfile, context.pathResolver() );
2581  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgPath ) );
2582  }
2583 
2584  // data defined shape size type?
2585  QgsTextBackgroundSettings::SizeType shpSizeType = background.sizeType();
2586  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShapeSizeType, context.expressionContext() );
2587  if ( exprVal.isValid() )
2588  {
2589  QString stype = exprVal.toString().trimmed();
2590  QgsDebugMsgLevel( QString( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
2591 
2592  if ( !stype.isEmpty() )
2593  {
2594  // "Buffer"
2596 
2597  if ( stype.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
2598  {
2600  }
2601  shpSizeType = sizType;
2602  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant( static_cast< int >( sizType ) ) );
2603  }
2604  }
2605 
2606  // data defined shape size X? (SVGs only use X for sizing)
2607  double ddShpSizeX = background.size().width();
2608  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeX, exprVal, context.expressionContext(), ddShpSizeX ) )
2609  {
2610  ddShpSizeX = exprVal.toDouble();
2611  }
2612 
2613  // data defined shape size Y?
2614  double ddShpSizeY = background.size().height();
2615  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeY, exprVal, context.expressionContext(), ddShpSizeY ) )
2616  {
2617  ddShpSizeY = exprVal.toDouble();
2618  }
2619 
2620  // don't continue under certain circumstances (e.g. size is fixed)
2621  bool skip = false;
2622  if ( shapeKind == QgsTextBackgroundSettings::ShapeSVG
2623  && ( svgPath.isEmpty()
2624  || ( !svgPath.isEmpty()
2625  && shpSizeType == QgsTextBackgroundSettings::SizeFixed
2626  && ddShpSizeX == 0.0 ) ) )
2627  {
2628  skip = true;
2629  }
2630  if ( shapeKind != QgsTextBackgroundSettings::ShapeSVG
2631  && shpSizeType == QgsTextBackgroundSettings::SizeFixed
2632  && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
2633  {
2634  skip = true;
2635  }
2636 
2637  if ( skip )
2638  {
2639  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
2640  dataDefinedValues.remove( QgsPalLayerSettings::ShapeOpacity );
2641  dataDefinedValues.remove( QgsPalLayerSettings::ShapeKind );
2642  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSVGFile );
2643  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeX );
2644  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeY );
2645  return; // don't bother evaluating values that won't be used
2646  }
2647 
2648  // data defined shape size units?
2649  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeSizeUnits, exprVal, context.expressionContext() );
2650 
2651  // data defined shape rotation type?
2652  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShapeRotationType, context.expressionContext() );
2653  if ( exprVal.isValid() )
2654  {
2655  QString rotstr = exprVal.toString().trimmed();
2656  QgsDebugMsgLevel( QString( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
2657 
2658  if ( !rotstr.isEmpty() )
2659  {
2660  // "Sync"
2662 
2663  if ( rotstr.compare( QLatin1String( "Offset" ), Qt::CaseInsensitive ) == 0 )
2664  {
2666  }
2667  else if ( rotstr.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
2668  {
2670  }
2671  dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant( static_cast< int >( rottype ) ) );
2672  }
2673  }
2674 
2675  // data defined shape rotation?
2676  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShapeRotation, exprVal, context.expressionContext(), background.rotation() );
2677 
2678  // data defined shape offset?
2679  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeOffset, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodePoint( background.offset() ) );
2680 
2681  // data defined shape offset units?
2682  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeOffsetUnits, exprVal, context.expressionContext() );
2683 
2684  // data defined shape radii?
2685  dataDefinedValEval( DDSizeF, QgsPalLayerSettings::ShapeRadii, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeSize( background.radii() ) );
2686 
2687  // data defined shape radii units?
2688  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeRadiiUnits, exprVal, context.expressionContext() );
2689 
2690  // data defined shape blend mode?
2691  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShapeBlendMode, exprVal, context.expressionContext() );
2692 
2693  // data defined shape fill color?
2694  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShapeFillColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( background.fillColor() ) );
2695 
2696  // data defined shape stroke color?
2697  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShapeStrokeColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( background.strokeColor() ) );
2698 
2699  // data defined shape stroke width?
2700  dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShapeStrokeWidth, exprVal, context.expressionContext(), background.strokeWidth() );
2701 
2702  // data defined shape stroke width units?
2703  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeStrokeWidthUnits, exprVal, context.expressionContext() );
2704 
2705  // data defined shape join style?
2706  dataDefinedValEval( DDJoinStyle, QgsPalLayerSettings::ShapeJoinStyle, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodePenJoinStyle( background.joinStyle() ) );
2707 
2708 }
2709 
2710 void QgsPalLayerSettings::parseDropShadow( QgsRenderContext &context )
2711 {
2712  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2713 
2714  QgsTextShadowSettings shadow = mFormat.shadow();
2715 
2716  // data defined draw shadow?
2717  bool drawShadow = shadow.enabled();
2718  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShadowDraw, exprVal, context.expressionContext(), drawShadow ) )
2719  {
2720  drawShadow = exprVal.toBool();
2721  }
2722 
2723  if ( !drawShadow )
2724  {
2725  return;
2726  }
2727 
2728  // data defined shadow transparency?
2729  double shadowOpacity = shadow.opacity() * 100;
2730  if ( dataDefinedValEval( DDOpacity, QgsPalLayerSettings::ShadowOpacity, exprVal, context.expressionContext(), shadowOpacity ) )
2731  {
2732  shadowOpacity = exprVal.toDouble();
2733  }
2734 
2735  // data defined shadow offset distance?
2736  double shadowOffDist = shadow.offsetDistance();
2737  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowOffsetDist, exprVal, context.expressionContext(), shadowOffDist ) )
2738  {
2739  shadowOffDist = exprVal.toDouble();
2740  }
2741 
2742  // data defined shadow offset distance?
2743  double shadowRad = shadow.blurRadius();
2744  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRad ) )
2745  {
2746  shadowRad = exprVal.toDouble();
2747  }
2748 
2749  drawShadow = ( drawShadow && shadowOpacity > 0 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
2750 
2751  if ( !drawShadow )
2752  {
2753  dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
2754  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOpacity );
2755  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOffsetDist );
2756  dataDefinedValues.remove( QgsPalLayerSettings::ShadowRadius );
2757  return; // don't bother evaluating values that won't be used
2758  }
2759 
2760  // data defined shadow under type?
2761  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShadowUnder, context.expressionContext() );
2762  if ( exprVal.isValid() )
2763  {
2764  QString str = exprVal.toString().trimmed();
2765  QgsDebugMsgLevel( QString( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
2766 
2767  if ( !str.isEmpty() )
2768  {
2769  // "Lowest"
2771 
2772  if ( str.compare( QLatin1String( "Text" ), Qt::CaseInsensitive ) == 0 )
2773  {
2775  }
2776  else if ( str.compare( QLatin1String( "Buffer" ), Qt::CaseInsensitive ) == 0 )
2777  {
2779  }
2780  else if ( str.compare( QLatin1String( "Background" ), Qt::CaseInsensitive ) == 0 )
2781  {
2783  }
2784  dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant( static_cast< int >( shdwtype ) ) );
2785  }
2786  }
2787 
2788  // data defined shadow offset angle?
2789  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShadowOffsetAngle, exprVal, context.expressionContext(), shadow.offsetAngle() );
2790 
2791  // data defined shadow offset units?
2792  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowOffsetUnits, exprVal, context.expressionContext() );
2793 
2794  // data defined shadow radius?
2795  dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadow.blurRadius() );
2796 
2797  // data defined shadow radius units?
2798  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowRadiusUnits, exprVal, context.expressionContext() );
2799 
2800  // data defined shadow scale? ( gui bounds to 0-2000, no upper bound here )
2801  dataDefinedValEval( DDIntPos, QgsPalLayerSettings::ShadowScale, exprVal, context.expressionContext(), shadow.scale() );
2802 
2803  // data defined shadow color?
2804  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShadowColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( shadow.color() ) );
2805 
2806  // data defined shadow blend mode?
2807  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShadowBlendMode, exprVal, context.expressionContext() );
2808 }
2809 
2810 // -------------
2811 
2812 
2814 {
2815  return layer->labelsEnabled() || layer->diagramsEnabled();
2816 }
2817 
2818 
2819 bool QgsPalLabeling::geometryRequiresPreparation( const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, const QgsGeometry &clipGeometry )
2820 {
2821  if ( geometry.isNull() )
2822  {
2823  return false;
2824  }
2825 
2826  //requires reprojection
2827  if ( ct.isValid() && !ct.isShortCircuited() )
2828  return true;
2829 
2830  //requires rotation
2831  const QgsMapToPixel &m2p = context.mapToPixel();
2832  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
2833  return true;
2834 
2835  //requires clip
2836  if ( !clipGeometry.isNull() && !clipGeometry.boundingBox().contains( geometry.boundingBox() ) )
2837  return true;
2838 
2839  //requires fixing
2840  if ( geometry.type() == QgsWkbTypes::PolygonGeometry && !geometry.isGeosValid() )
2841  return true;
2842 
2843  return false;
2844 }
2845 
2846 QStringList QgsPalLabeling::splitToLines( const QString &text, const QString &wrapCharacter )
2847 {
2848  QStringList multiLineSplit;
2849  if ( !wrapCharacter.isEmpty() && wrapCharacter != QLatin1String( "\n" ) )
2850  {
2851  //wrap on both the wrapchr and new line characters
2852  Q_FOREACH ( const QString &line, text.split( wrapCharacter ) )
2853  {
2854  multiLineSplit.append( line.split( '\n' ) );
2855  }
2856  }
2857  else
2858  {
2859  multiLineSplit = text.split( '\n' );
2860  }
2861 
2862  return multiLineSplit;
2863 }
2864 
2865 QStringList QgsPalLabeling::splitToGraphemes( const QString &text )
2866 {
2867  QStringList graphemes;
2868  QTextBoundaryFinder boundaryFinder( QTextBoundaryFinder::Grapheme, text );
2869  int currentBoundary = -1;
2870  int previousBoundary = 0;
2871  while ( ( currentBoundary = boundaryFinder.toNextBoundary() ) > 0 )
2872  {
2873  graphemes << text.mid( previousBoundary, currentBoundary - previousBoundary );
2874  previousBoundary = currentBoundary;
2875  }
2876  return graphemes;
2877 }
2878 
2880 {
2881  if ( geometry.isNull() )
2882  {
2883  return QgsGeometry();
2884  }
2885 
2886  //don't modify the feature's geometry so that geometry based expressions keep working
2887  QgsGeometry geom = geometry;
2888 
2889  //reproject the geometry if necessary
2890  if ( ct.isValid() && !ct.isShortCircuited() )
2891  {
2892  try
2893  {
2894  geom.transform( ct );
2895  }
2896  catch ( QgsCsException &cse )
2897  {
2898  Q_UNUSED( cse );
2899  QgsDebugMsgLevel( QString( "Ignoring feature due to transformation exception" ), 4 );
2900  return QgsGeometry();
2901  }
2902  }
2903 
2904  // Rotate the geometry if needed, before clipping
2905  const QgsMapToPixel &m2p = context.mapToPixel();
2906  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
2907  {
2908  QgsPointXY center = context.extent().center();
2909 
2910  if ( ct.isValid() && !ct.isShortCircuited() )
2911  {
2912  try
2913  {
2914  center = ct.transform( center );
2915  }
2916  catch ( QgsCsException &cse )
2917  {
2918  Q_UNUSED( cse );
2919  QgsDebugMsgLevel( QString( "Ignoring feature due to transformation exception" ), 4 );
2920  return QgsGeometry();
2921  }
2922  }
2923 
2924  if ( geom.rotate( m2p.mapRotation(), center ) )
2925  {
2926  QgsDebugMsg( QString( "Error rotating geometry" ).arg( geom.asWkt() ) );
2927  return QgsGeometry();
2928  }
2929  }
2930 
2931  // fix invalid polygons
2932  if ( geom.type() == QgsWkbTypes::PolygonGeometry && !geom.isGeosValid() )
2933  {
2934  QgsGeometry validGeom = geom.makeValid();
2935  if ( validGeom.isNull() )
2936  {
2937  QgsDebugMsg( QString( "Could not repair geometry: %1" ).arg( validGeom.lastError() ) );
2938  return QgsGeometry();
2939  }
2940  geom = validGeom;
2941  }
2942 
2943  if ( !clipGeometry.isNull() &&
2944  ( ( qgsDoubleNear( m2p.mapRotation(), 0 ) && !clipGeometry.boundingBox().contains( geom.boundingBox() ) )
2945  || ( !qgsDoubleNear( m2p.mapRotation(), 0 ) && !clipGeometry.contains( geom ) ) ) )
2946  {
2947  QgsGeometry clipGeom = geom.intersection( clipGeometry ); // creates new geometry
2948  if ( clipGeom.isNull() )
2949  {
2950  return QgsGeometry();
2951  }
2952  geom = clipGeom;
2953  }
2954 
2955  return geom;
2956 }
2957 
2958 bool QgsPalLabeling::checkMinimumSizeMM( const QgsRenderContext &context, const QgsGeometry &geom, double minSize )
2959 {
2960  if ( minSize <= 0 )
2961  {
2962  return true;
2963  }
2964 
2965  if ( geom.isNull() )
2966  {
2967  return false;
2968  }
2969 
2970  QgsWkbTypes::GeometryType featureType = geom.type();
2971  if ( featureType == QgsWkbTypes::PointGeometry ) //minimum size does not apply to point features
2972  {
2973  return true;
2974  }
2975 
2976  double mapUnitsPerMM = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
2977  if ( featureType == QgsWkbTypes::LineGeometry )
2978  {
2979  double length = geom.length();
2980  if ( length >= 0.0 )
2981  {
2982  return ( length >= ( minSize * mapUnitsPerMM ) );
2983  }
2984  }
2985  else if ( featureType == QgsWkbTypes::PolygonGeometry )
2986  {
2987  double area = geom.area();
2988  if ( area >= 0.0 )
2989  {
2990  return ( std::sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
2991  }
2992  }
2993  return true; //should never be reached. Return true in this case to label such geometries anyway.
2994 }
2995 
2996 
2997 void QgsPalLabeling::dataDefinedTextStyle( QgsPalLayerSettings &tmpLyr,
2998  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
2999 {
3000  QgsTextFormat format = tmpLyr.format();
3001  bool changed = false;
3002 
3003  //font color
3004  if ( ddValues.contains( QgsPalLayerSettings::Color ) )
3005  {
3006  QVariant ddColor = ddValues.value( QgsPalLayerSettings::Color );
3007  format.setColor( ddColor.value<QColor>() );
3008  changed = true;
3009  }
3010 
3011  //font transparency
3012  if ( ddValues.contains( QgsPalLayerSettings::FontOpacity ) )
3013  {
3014  format.setOpacity( ddValues.value( QgsPalLayerSettings::FontOpacity ).toDouble() / 100.0 );
3015  changed = true;
3016  }
3017 
3018  //font blend mode
3019  if ( ddValues.contains( QgsPalLayerSettings::FontBlendMode ) )
3020  {
3021  format.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::FontBlendMode ).toInt() ) );
3022  changed = true;
3023  }
3024 
3025  if ( changed )
3026  {
3027  tmpLyr.setFormat( format );
3028  }
3029 }
3030 
3031 void QgsPalLabeling::dataDefinedTextFormatting( QgsPalLayerSettings &tmpLyr,
3032  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3033 {
3034  if ( ddValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
3035  {
3036  tmpLyr.wrapChar = ddValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
3037  }
3038 
3039  if ( !tmpLyr.wrapChar.isEmpty() || tmpLyr.getLabelExpression()->expression().contains( QLatin1String( "wordwrap" ) ) )
3040  {
3041 
3042  if ( ddValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
3043  {
3044  QgsTextFormat format = tmpLyr.format();
3045  format.setLineHeight( ddValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble() );
3046  tmpLyr.setFormat( format );
3047  }
3048 
3049  if ( ddValues.contains( QgsPalLayerSettings::MultiLineAlignment ) )
3050  {
3051  tmpLyr.multilineAlign = static_cast< QgsPalLayerSettings::MultiLineAlign >( ddValues.value( QgsPalLayerSettings::MultiLineAlignment ).toInt() );
3052  }
3053 
3054  }
3055 
3056  if ( ddValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
3057  {
3058  tmpLyr.addDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
3059  }
3060 
3061  if ( tmpLyr.addDirectionSymbol )
3062  {
3063 
3064  if ( ddValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
3065  {
3066  tmpLyr.leftDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
3067  }
3068  if ( ddValues.contains( QgsPalLayerSettings::DirSymbRight ) )
3069  {
3070  tmpLyr.rightDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
3071  }
3072 
3073  if ( ddValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
3074  {
3075  tmpLyr.placeDirectionSymbol = static_cast< QgsPalLayerSettings::DirectionSymbols >( ddValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt() );
3076  }
3077 
3078  if ( ddValues.contains( QgsPalLayerSettings::DirSymbReverse ) )
3079  {
3080  tmpLyr.reverseDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbReverse ).toBool();
3081  }
3082 
3083  }
3084 }
3085 
3086 void QgsPalLabeling::dataDefinedTextBuffer( QgsPalLayerSettings &tmpLyr,
3087  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3088 {
3089  QgsTextBufferSettings buffer = tmpLyr.format().buffer();
3090  bool changed = false;
3091 
3092  //buffer draw
3093  if ( ddValues.contains( QgsPalLayerSettings::BufferDraw ) )
3094  {
3095  buffer.setEnabled( ddValues.value( QgsPalLayerSettings::BufferDraw ).toBool() );
3096  changed = true;
3097  }
3098 
3099  if ( !buffer.enabled() )
3100  {
3101  if ( changed )
3102  {
3103  QgsTextFormat format = tmpLyr.format();
3104  format.setBuffer( buffer );
3105  tmpLyr.setFormat( format );
3106  }
3107 
3108  // tmpLyr.bufferSize > 0.0 && tmpLyr.bufferTransp < 100 figured in during evaluation
3109  return; // don't continue looking for unused values
3110  }
3111 
3112  //buffer size
3113  if ( ddValues.contains( QgsPalLayerSettings::BufferSize ) )
3114  {
3115  buffer.setSize( ddValues.value( QgsPalLayerSettings::BufferSize ).toDouble() );
3116  changed = true;
3117  }
3118 
3119  //buffer opacity
3120  if ( ddValues.contains( QgsPalLayerSettings::BufferOpacity ) )
3121  {
3122  buffer.setOpacity( ddValues.value( QgsPalLayerSettings::BufferOpacity ).toDouble() / 100.0 );
3123  changed = true;
3124  }
3125 
3126  //buffer size units
3127  if ( ddValues.contains( QgsPalLayerSettings::BufferUnit ) )
3128  {
3129  QgsUnitTypes::RenderUnit bufunit = static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::BufferUnit ).toInt() );
3130  buffer.setSizeUnit( bufunit );
3131  changed = true;
3132  }
3133 
3134  //buffer color
3135  if ( ddValues.contains( QgsPalLayerSettings::BufferColor ) )
3136  {
3137  QVariant ddColor = ddValues.value( QgsPalLayerSettings::BufferColor );
3138  buffer.setColor( ddColor.value<QColor>() );
3139  changed = true;
3140  }
3141 
3142  //buffer pen join style
3143  if ( ddValues.contains( QgsPalLayerSettings::BufferJoinStyle ) )
3144  {
3145  buffer.setJoinStyle( static_cast< Qt::PenJoinStyle >( ddValues.value( QgsPalLayerSettings::BufferJoinStyle ).toInt() ) );
3146  changed = true;
3147  }
3148 
3149  //buffer blend mode
3150  if ( ddValues.contains( QgsPalLayerSettings::BufferBlendMode ) )
3151  {
3152  buffer.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::BufferBlendMode ).toInt() ) );
3153  changed = true;
3154  }
3155 
3156  if ( changed )
3157  {
3158  QgsTextFormat format = tmpLyr.format();
3159  format.setBuffer( buffer );
3160  tmpLyr.setFormat( format );
3161  }
3162 }
3163 
3164 void QgsPalLabeling::dataDefinedShapeBackground( QgsPalLayerSettings &tmpLyr,
3165  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3166 {
3167  QgsTextBackgroundSettings background = tmpLyr.format().background();
3168  bool changed = false;
3169 
3170  //shape draw
3171  if ( ddValues.contains( QgsPalLayerSettings::ShapeDraw ) )
3172  {
3173  background.setEnabled( ddValues.value( QgsPalLayerSettings::ShapeDraw ).toBool() );
3174  changed = true;
3175  }
3176 
3177  if ( !background.enabled() )
3178  {
3179  if ( changed )
3180  {
3181  QgsTextFormat format = tmpLyr.format();
3182  format.setBackground( background );
3183  tmpLyr.setFormat( format );
3184  }
3185  return; // don't continue looking for unused values
3186  }
3187 
3188  if ( ddValues.contains( QgsPalLayerSettings::ShapeKind ) )
3189  {
3190  background.setType( static_cast< QgsTextBackgroundSettings::ShapeType >( ddValues.value( QgsPalLayerSettings::ShapeKind ).toInt() ) );
3191  changed = true;
3192  }
3193 
3194  if ( ddValues.contains( QgsPalLayerSettings::ShapeSVGFile ) )
3195  {
3196  background.setSvgFile( ddValues.value( QgsPalLayerSettings::ShapeSVGFile ).toString() );
3197  changed = true;
3198  }
3199 
3200  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeType ) )
3201  {
3202  background.setSizeType( static_cast< QgsTextBackgroundSettings::SizeType >( ddValues.value( QgsPalLayerSettings::ShapeSizeType ).toInt() ) );
3203  changed = true;
3204  }
3205 
3206  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeX ) )
3207  {
3208  QSizeF size = background.size();
3209  size.setWidth( ddValues.value( QgsPalLayerSettings::ShapeSizeX ).toDouble() );
3210  background.setSize( size );
3211  changed = true;
3212  }
3213  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeY ) )
3214  {
3215  QSizeF size = background.size();
3216  size.setHeight( ddValues.value( QgsPalLayerSettings::ShapeSizeY ).toDouble() );
3217  background.setSize( size );
3218  changed = true;
3219  }
3220 
3221  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeUnits ) )
3222  {
3223  background.setSizeUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeSizeUnits ).toInt() ) );
3224  changed = true;
3225  }
3226 
3227  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotationType ) )
3228  {
3229  background.setRotationType( static_cast< QgsTextBackgroundSettings::RotationType >( ddValues.value( QgsPalLayerSettings::ShapeRotationType ).toInt() ) );
3230  changed = true;
3231  }
3232 
3233  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotation ) )
3234  {
3235  background.setRotation( ddValues.value( QgsPalLayerSettings::ShapeRotation ).toDouble() );
3236  changed = true;
3237  }
3238 
3239  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffset ) )
3240  {
3241  background.setOffset( ddValues.value( QgsPalLayerSettings::ShapeOffset ).toPointF() );
3242  changed = true;
3243  }
3244 
3245  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffsetUnits ) )
3246  {
3247  background.setOffsetUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeOffsetUnits ).toInt() ) );
3248  changed = true;
3249  }
3250 
3251  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadii ) )
3252  {
3253  background.setRadii( ddValues.value( QgsPalLayerSettings::ShapeRadii ).toSizeF() );
3254  changed = true;
3255  }
3256 
3257  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadiiUnits ) )
3258  {
3259  background.setRadiiUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeRadiiUnits ).toInt() ) );
3260  changed = true;
3261  }
3262 
3263  if ( ddValues.contains( QgsPalLayerSettings::ShapeBlendMode ) )
3264  {
3265  background.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::ShapeBlendMode ).toInt() ) );
3266  changed = true;
3267  }
3268 
3269  if ( ddValues.contains( QgsPalLayerSettings::ShapeFillColor ) )
3270  {
3271  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeFillColor );
3272  background.setFillColor( ddColor.value<QColor>() );
3273  changed = true;
3274  }
3275 
3276  if ( ddValues.contains( QgsPalLayerSettings::ShapeStrokeColor ) )
3277  {
3278  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeStrokeColor );
3279  background.setStrokeColor( ddColor.value<QColor>() );
3280  changed = true;
3281  }
3282 
3283  if ( ddValues.contains( QgsPalLayerSettings::ShapeOpacity ) )
3284  {
3285  background.setOpacity( ddValues.value( QgsPalLayerSettings::ShapeOpacity ).toDouble() / 100.0 );
3286  changed = true;
3287  }
3288 
3289  if ( ddValues.contains( QgsPalLayerSettings::ShapeStrokeWidth ) )
3290  {
3291  background.setStrokeWidth( ddValues.value( QgsPalLayerSettings::ShapeStrokeWidth ).toDouble() );
3292  changed = true;
3293  }
3294 
3295  if ( ddValues.contains( QgsPalLayerSettings::ShapeStrokeWidthUnits ) )
3296  {
3297  background.setStrokeWidthUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeStrokeWidthUnits ).toInt() ) );
3298  changed = true;
3299  }
3300 
3301  if ( ddValues.contains( QgsPalLayerSettings::ShapeJoinStyle ) )
3302  {
3303  background.setJoinStyle( static_cast< Qt::PenJoinStyle >( ddValues.value( QgsPalLayerSettings::ShapeJoinStyle ).toInt() ) );
3304  changed = true;
3305  }
3306 
3307  if ( changed )
3308  {
3309  QgsTextFormat format = tmpLyr.format();
3310  format.setBackground( background );
3311  tmpLyr.setFormat( format );
3312  }
3313 }
3314 
3315 void QgsPalLabeling::dataDefinedDropShadow( QgsPalLayerSettings &tmpLyr,
3316  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3317 {
3318  QgsTextShadowSettings shadow = tmpLyr.format().shadow();
3319  bool changed = false;
3320 
3321  //shadow draw
3322  if ( ddValues.contains( QgsPalLayerSettings::ShadowDraw ) )
3323  {
3324  shadow.setEnabled( ddValues.value( QgsPalLayerSettings::ShadowDraw ).toBool() );
3325  changed = true;
3326  }
3327 
3328  if ( !shadow.enabled() )
3329  {
3330  if ( changed )
3331  {
3332  QgsTextFormat format = tmpLyr.format();
3333  format.setShadow( shadow );
3334  tmpLyr.setFormat( format );
3335  }
3336  return; // don't continue looking for unused values
3337  }
3338 
3339  if ( ddValues.contains( QgsPalLayerSettings::ShadowUnder ) )
3340  {
3341  shadow.setShadowPlacement( static_cast< QgsTextShadowSettings::ShadowPlacement >( ddValues.value( QgsPalLayerSettings::ShadowUnder ).toInt() ) );
3342  changed = true;
3343  }
3344 
3345  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetAngle ) )
3346  {
3347  shadow.setOffsetAngle( ddValues.value( QgsPalLayerSettings::ShadowOffsetAngle ).toInt() );
3348  changed = true;
3349  }
3350 
3351  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetDist ) )
3352  {
3353  shadow.setOffsetDistance( ddValues.value( QgsPalLayerSettings::ShadowOffsetDist ).toDouble() );
3354  changed = true;
3355  }
3356 
3357  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetUnits ) )
3358  {
3359  shadow.setOffsetUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShadowOffsetUnits ).toInt() ) );
3360  changed = true;
3361  }
3362 
3363  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadius ) )
3364  {
3365  shadow.setBlurRadius( ddValues.value( QgsPalLayerSettings::ShadowRadius ).toDouble() );
3366  changed = true;
3367  }
3368 
3369  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadiusUnits ) )
3370  {
3371  shadow.setBlurRadiusUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShadowRadiusUnits ).toInt() ) );
3372  changed = true;
3373  }
3374 
3375  if ( ddValues.contains( QgsPalLayerSettings::ShadowColor ) )
3376  {
3377  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShadowColor );
3378  shadow.setColor( ddColor.value<QColor>() );
3379  changed = true;
3380  }
3381 
3382  if ( ddValues.contains( QgsPalLayerSettings::ShadowOpacity ) )
3383  {
3384  shadow.setOpacity( ddValues.value( QgsPalLayerSettings::ShadowOpacity ).toDouble() / 100.0 );
3385  changed = true;
3386  }
3387 
3388  if ( ddValues.contains( QgsPalLayerSettings::ShadowScale ) )
3389  {
3390  shadow.setScale( ddValues.value( QgsPalLayerSettings::ShadowScale ).toInt() );
3391  changed = true;
3392  }
3393 
3394 
3395  if ( ddValues.contains( QgsPalLayerSettings::ShadowBlendMode ) )
3396  {
3397  shadow.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::ShadowBlendMode ).toInt() ) );
3398  changed = true;
3399  }
3400 
3401  if ( changed )
3402  {
3403  QgsTextFormat format = tmpLyr.format();
3404  format.setShadow( shadow );
3405  tmpLyr.setFormat( format );
3406  }
3407 }
3408 
3409 
3410 void QgsPalLabeling::drawLabelCandidateRect( pal::LabelPosition *lp, QPainter *painter, const QgsMapToPixel *xform, QList<QgsLabelCandidate> *candidates )
3411 {
3412  QgsPointXY outPt = xform->transform( lp->getX(), lp->getY() );
3413 
3414  painter->save();
3415 
3416 #if 0 // TODO: generalize some of this
3417  double w = lp->getWidth();
3418  double h = lp->getHeight();
3419  double cx = lp->getX() + w / 2.0;
3420  double cy = lp->getY() + h / 2.0;
3421  double scale = 1.0 / xform->mapUnitsPerPixel();
3422  double rotation = xform->mapRotation();
3423  double sw = w * scale;
3424  double sh = h * scale;
3425  QRectF rect( -sw / 2, -sh / 2, sw, sh );
3426 
3427  painter->translate( xform->transform( QPointF( cx, cy ) ).toQPointF() );
3428  if ( rotation )
3429  {
3430  // Only if not horizontal
3431  if ( lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT &&
3432  lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT_OVER &&
3433  lp->getFeaturePart()->getLayer()->getArrangement() != P_HORIZ )
3434  {
3435  painter->rotate( rotation );
3436  }
3437  }
3438  painter->translate( rect.bottomLeft() );
3439  painter->rotate( -lp->getAlpha() * 180 / M_PI );
3440  painter->translate( -rect.bottomLeft() );
3441 #else
3442  QgsPointXY outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
3443  QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
3444  painter->translate( QPointF( outPt.x(), outPt.y() ) );
3445  painter->rotate( -lp->getAlpha() * 180 / M_PI );
3446 #endif
3447 
3448  if ( lp->conflictsWithObstacle() )
3449  {
3450  painter->setPen( QColor( 255, 0, 0, 64 ) );
3451  }
3452  else
3453  {
3454  painter->setPen( QColor( 0, 0, 0, 64 ) );
3455  }
3456  painter->drawRect( rect );
3457  painter->restore();
3458 
3459  // save the rect
3460  rect.moveTo( outPt.x(), outPt.y() );
3461  if ( candidates )
3462  candidates->append( QgsLabelCandidate( rect, lp->cost() * 1000 ) );
3463 
3464  // show all parts of the multipart label
3465  if ( lp->getNextPart() )
3466  drawLabelCandidateRect( lp->getNextPart(), painter, xform, candidates );
3467 }
3468 
3470 {
3471  mLabelSearchTree = new QgsLabelSearchTree();
3472 }
3473 
3475 {
3476  delete mLabelSearchTree;
3477  mLabelSearchTree = nullptr;
3478 }
3479 
3480 QList<QgsLabelPosition> QgsLabelingResults::labelsAtPosition( const QgsPointXY &p ) const
3481 {
3482  QList<QgsLabelPosition> positions;
3483 
3484  QList<QgsLabelPosition *> positionPointers;
3485  if ( mLabelSearchTree )
3486  {
3487  mLabelSearchTree->label( p, positionPointers );
3488  QList<QgsLabelPosition *>::const_iterator pointerIt = positionPointers.constBegin();
3489  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
3490  {
3491  positions.push_back( QgsLabelPosition( **pointerIt ) );
3492  }
3493  }
3494 
3495  return positions;
3496 }
3497 
3498 QList<QgsLabelPosition> QgsLabelingResults::labelsWithinRect( const QgsRectangle &r ) const
3499 {
3500  QList<QgsLabelPosition> positions;
3501 
3502  QList<QgsLabelPosition *> positionPointers;
3503  if ( mLabelSearchTree )
3504  {
3505  mLabelSearchTree->labelsInRect( r, positionPointers );
3506  QList<QgsLabelPosition *>::const_iterator pointerIt = positionPointers.constBegin();
3507  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
3508  {
3509  positions.push_back( QgsLabelPosition( **pointerIt ) );
3510  }
3511  }
3512 
3513  return positions;
3514 }
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.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
double maxCurvedCharAngleOut
Maximum angle between outside curved label characters (valid range -20.0 to -95.0) ...
double maximumScale
The maximum map scale (i.e.
QString leftDirectionSymbol
String to use for left direction arrows.
void setOpacity(double opacity)
Sets the text&#39;s opacity.
Positive integer values (including 0)
Definition: qgsproperty.h:55
void setOffsetUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shadow&#39;s offset.
QgsUnitTypes::RenderUnit repeatDistanceUnit
Units for repeating labels for a single feature.
QSizeF radii() const
Returns the radii used for rounding the corners of shapes.
QSizeF size() const
Returns the size of the background shape.
double opacity() const
Returns the text&#39;s opacity.
const QgsVectorSimplifyMethod & vectorSimplifyMethod() const
Added in QGIS v2.4.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the shape&#39;s size.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
QPointF offset() const
Returns the offset used for drawing the background shape.
QColor fillColor() const
Returns the color used for filing the background shape.
bool isNull() const
Returns true if the geometry is null (ie, contains no underlying geometry accessible via geometry() )...
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double...
static QString encodeSize(QSizeF size)
Encodes a QSizeF to a string.
void registerFeature(QgsFeature &f, QgsRenderContext &context, QgsLabelFeature **labelFeature=nullptr, QgsGeometry obstacleGeometry=QgsGeometry())
Register a feature for labeling.
double angleOffset
Label rotation, in degrees clockwise.
virtual bool readXml(const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions)
Reads property collection state from an XML element.
bool formatNumbers
Set to true to format numeric label text as numbers (e.g.
A class to query the labeling structure at a given point (small 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:38
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).
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.
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.
static QString svgSymbolNameToPath(QString name, const QgsPathResolver &pathResolver)
Get SVG symbol&#39;s path from its name.
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:251
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
bool reverseDirectionSymbol
True if direction symbols should be reversed.
Rotation (value between 0-360 degrees)
Definition: qgsproperty.h:60
double maxScale
The maximum scale, or 0.0 if unset.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
bool isGeosValid() const
Checks validity of the geometry using GEOS.
ShapeType type() const
Returns the type of background shape (e.g., square, ellipse, SVG).
void setOffset(QPointF offset)
Sets the offset used for drawing the background shape.
Any string value.
Definition: qgsproperty.h:61
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, e.g., font settings, buffer settings, etc.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
double zIndex
Z-Index of label, where labels with a higher z-index are rendered on top of labels with a lower z-ind...
double mapRotation() const
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.
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:39
double tolerance() const
Gets the tolerance of simplification in map units. Represents the maximum distance in map units betwe...
const QgsRectangle & extent() const
int fontMaxPixelSize
Maximum pixel size for showing rendered map unit labels (1 - 10000).
static bool fontFamilyOnSystem(const QString &family)
Check whether font family is on system in a quick manner, which does not compare [foundry].
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent...
Maximum map scale (ie most "zoomed in")
The geometries can be fully simplified by its BoundingBox.
double getHeight() const
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the drop shadow.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:142
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 setSize(QSizeF size)
Sets the size of the background shape.
void clear() override
Removes all properties from the collection.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
double mapUnitsPerPixel() const
Return current map units per pixel.
A store for object properties.
Definition: qgsproperty.h:229
void setRadii(QSizeF radii)
Sets the radii used for rounding the corners of shapes.
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspointxy.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
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
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)
static QSizeF decodeSize(const QString &string)
Decodes a QSizeF from a string.
double rotation() const
Returns the rotation for the background shape, in degrees clockwise.
bool plusSign
Whether &#39;+&#39; signs should be prepended to positive numeric labels.
double lineHeight() const
Returns the line height for text.
void setEnabled(bool enabled)
Sets whether the text background will be drawn.
unsigned int placementFlags
int offsetAngle() const
Returns the angle for offsetting the position of the shadow from the text.
ShapeType
Background shape types.
QString wrapChar
Wrapping character string.
QgsPalLayerSettings & operator=(const QgsPalLayerSettings &s)
copy operator - only copies the permanent members
bool valueAsBool(int key, const QgsExpressionContext &context, bool defaultValue=false, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an boolean...
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:137
Placement
Placement modes which determine how label candidates are generated for a feature. ...
static QString encodePredefinedPositionOrder(const QVector< QgsPalLayerSettings::PredefinedPointPosition > &positions)
Encodes an ordered list of predefined point label positions to a string.
QString rightDirectionSymbol
String to use for right direction arrows.
bool preserveRotation
True if label rotation should be preserved during label pin/unpin operations.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string...
QString lastError() const
Returns an error string referring to the last error encountered either when this geometry was created...
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
QgsExpression * getLabelExpression()
Returns the QgsExpression for this label settings.
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point...
This class contains information how to simplify geometries fetched from a vector layer.
Contains information about the context of a rendering operation.
Shape rotation is offset from text rotation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
bool centroidWhole
True if feature centroid should be calculated from the whole feature, or false if only the visible pa...
bool scaleVisibility
Set to true to limit label visibility to a range of scales.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the size.
The QgsLabelFeature class describes a feature that should be used within the labeling engine...
const QgsMapToPixel & mapToPixel() const
QgsGeometry extentGeom
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.
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...
Container for settings relating to a text buffer.
QgsGeometry makeValid() const
Attempts to make an invalid geometry valid without losing vertices.
bool forceLocalOptimization() const
Gets where the simplification executes, after fetch the geometries from provider, or when supported...
void setFillColor(const QColor &color)
Sets the color used for filing the background shape.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
OffsetType offsetType
Offset type for layer (only applies in certain placement modes)
double dist
Distance from feature to the label.
double size() const
Returns the size for rendered text.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
bool useSubstitutions
True if substitutions should be applied.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s stroke width.
bool enabled() const
Returns whether the background is enabled.
static QStringList splitToGraphemes(const QString &text)
Splits a text string to a list of graphemes, which are the smallest allowable character divisions in ...
SimplifyHints simplifyHints() const
Gets the simplification hints of the vector layer managed.
Class for doing transforms between two map coordinate systems.
SizeType sizeType() const
Returns the method used to determine the size of the background shape (e.g., fixed size or buffer aro...
LabelPosition is a candidate feature label position.
Definition: labelposition.h:55
bool enabled() const
Returns whether the buffer is enabled.
int scale() const
Returns the scaling used for the drop shadow (in percentage of original size).
Shape rotation is synced with text rotation.
void setShadowPlacement(QgsTextShadowSettings::ShadowPlacement placement)
Sets the placement for the drop shadow.
Label on right of point.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the text.
void setBuffer(const QgsTextBufferSettings &bufferSettings)
Sets the text&#39;s buffer settings.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
bool limitNumLabels
True if the number of labels drawn should be limited.
static bool geometryRequiresPreparation(const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, const QgsGeometry &clipGeometry=QgsGeometry())
Checks whether a geometry requires preparation before registration with PAL.
Draw shadow under background shape.
bool isExpression
True if this label is made from a expression string, e.g., FieldName || &#39;mm&#39;.
Convert all characters to lowercase.
void setShadow(const QgsTextShadowSettings &shadowSettings)
Sets the text&#39;s drop shadow settings.
static QgsGeometry prepareGeometry(const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, const QgsGeometry &clipGeometry=QgsGeometry())
Prepares a geometry for registration with PAL.
void readXml(const QDomElement &elem)
Reads the collection state from an XML element.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
static QVector< QgsPalLayerSettings::PredefinedPointPosition > decodePredefinedPositionOrder(const QString &positionString)
Decodes a string to an ordered list of predefined point label positions.
QString process(const QString &input) const
Processes a given input string, applying any valid replacements which should be made using QgsStringR...
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:170
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)
int valueAsInt(int key, const QgsExpressionContext &context, int defaultValue=0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an integer...
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
double minFeatureSize
Minimum feature size (in millimeters) for a feature to be labelled.
Vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top) ...
QString namedStyle() const
Returns the named style for the font used for rendering text (e.g., "bold").
QFont font() const
Returns the font used for rendering text.
QgsPointXY toMapCoordinatesF(double x, double y) const
Transform device coordinates to map (world) coordinates.
void setRotationType(RotationType type)
Sets the method used for rotating the background shape.
int fontMinPixelSize
Minimum pixel size for showing rendered map unit labels (1 - 1000).
void setColor(const QColor &color)
Sets the color for the buffer.
int priority
Label priority.
bool labelPerPart
True if every part of a multi-part feature should be labeled.
QgsUnitTypes::RenderUnit distUnits
Units the distance from feature to the label.
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
double opacity() const
Returns the shadow&#39;s opacity.
QgsMapUnitScale labelOffsetMapUnitScale
Map unit scale for label offset.
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
2D size (width/height different)
Definition: qgsproperty.h:70
The QgsMargins class defines the four margins of a rectangle.
Definition: qgsmargins.h:37
void setOpacity(double opacity)
Sets the background shape&#39;s opacity.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:100
bool diagramsEnabled() const
Returns whether the layer contains diagrams which are enabled and should be drawn.
static QColor decodeColor(const QString &str)
Buffer transparency (deprecated)
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:149
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.