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