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