QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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 #include "qgsapplication.h"
23 
24 #include <list>
25 
26 #include "pal/pal.h"
27 #include "pal/feature.h"
28 #include "pal/layer.h"
29 #include "pal/palexception.h"
30 #include "pal/problem.h"
31 #include "pal/labelposition.h"
32 
33 #include <cmath>
34 
35 #include <QApplication>
36 #include <QByteArray>
37 #include <QString>
38 #include <QFontMetrics>
39 #include <QTime>
40 #include <QPainter>
41 #include <QDesktopWidget>
42 
43 #include "diagram/qgsdiagram.h"
44 #include "qgsdiagramrenderer.h"
45 #include "qgsfontutils.h"
46 #include "qgslabelsearchtree.h"
47 #include "qgsexpression.h"
48 #include "qgslabelingengine.h"
49 #include "qgsvectorlayerlabeling.h"
50 #include "qgstextrendererutils.h"
51 #include "qgstextfragment.h"
52 
53 #include "qgslogger.h"
54 #include "qgsvectorlayer.h"
55 #include "qgsvectordataprovider.h"
58 #include "qgsgeometry.h"
59 #include "qgsmarkersymbollayer.h"
60 #include "qgspainting.h"
61 #include "qgsproject.h"
62 #include "qgsproperty.h"
63 #include "qgssymbollayerutils.h"
65 #include "qgscurvepolygon.h"
66 #include "qgsmessagelog.h"
67 #include "qgsgeometrycollection.h"
68 #include "callouts/qgscallout.h"
70 #include <QMessageBox>
71 
72 using namespace pal;
73 
74 // -------------
75 
76 /* ND: Default point label position priority. These are set to match variants of the ideal placement priority described
77  in "Making Maps", Krygier & Wood (2011) (p216),
78  "Elements of Cartography", Robinson et al (1995)
79  and "Designing Better Maps", Brewer (2005) (p76)
80  Note that while they agree on positions 1-4, 5-8 are more contentious so I've selected these placements
81  based on my preferences, and to follow Krygier and Wood's placements more closer. (I'm not going to disagree
82  with Denis Wood on anything cartography related...!)
83 */
84 typedef QVector< QgsPalLayerSettings::PredefinedPointPosition > PredefinedPointPositionVector;
86 {
95 } ) )
96 //debugging only - don't use these placements by default
97 /* << QgsPalLayerSettings::TopSlightlyLeft
98 << QgsPalLayerSettings::BottomSlightlyLeft;
99 << QgsPalLayerSettings::TopMiddle
100 << QgsPalLayerSettings::BottomMiddle;*/
101 
102 Q_GLOBAL_STATIC( QgsPropertiesDefinition, sPropertyDefinitions )
103 
104 void QgsPalLayerSettings::initPropertyDefinitions()
105 {
106  if ( !sPropertyDefinitions()->isEmpty() )
107  return;
108 
109  const QString origin = QStringLiteral( "labeling" );
110 
111  *sPropertyDefinitions() = QgsPropertiesDefinition
112  {
113  { QgsPalLayerSettings::Size, QgsPropertyDefinition( "Size", QObject::tr( "Font size" ), QgsPropertyDefinition::DoublePositive, origin ) },
114  { QgsPalLayerSettings::Bold, QgsPropertyDefinition( "Bold", QObject::tr( "Bold style" ), QgsPropertyDefinition::Boolean, origin ) },
115  { QgsPalLayerSettings::Italic, QgsPropertyDefinition( "Italic", QObject::tr( "Italic style" ), QgsPropertyDefinition::Boolean, origin ) },
116  { QgsPalLayerSettings::Underline, QgsPropertyDefinition( "Underline", QObject::tr( "Draw underline" ), QgsPropertyDefinition::Boolean, origin ) },
117  { QgsPalLayerSettings::Color, QgsPropertyDefinition( "Color", QObject::tr( "Text color" ), QgsPropertyDefinition::ColorNoAlpha, origin ) },
118  { QgsPalLayerSettings::Strikeout, QgsPropertyDefinition( "Strikeout", QObject::tr( "Draw strikeout" ), QgsPropertyDefinition::Boolean, origin ) },
119  {
120  QgsPalLayerSettings::Family, QgsPropertyDefinition( "Family", QgsPropertyDefinition::DataTypeString, QObject::tr( "Font family" ), QObject::tr( "string " ) + QObject::tr( "[<b>family</b>|<b>family[foundry]</b>],<br>"
121  "e.g. Helvetica or Helvetica [Cronyx]" ), origin )
122  },
123  {
124  QgsPalLayerSettings::FontStyle, QgsPropertyDefinition( "FontStyle", QgsPropertyDefinition::DataTypeString, QObject::tr( "Font style" ), QObject::tr( "string " ) + QObject::tr( "[<b>font style name</b>|<b>Ignore</b>],<br>"
125  "e.g. Bold Condensed or Light Italic" ), origin )
126  },
127  { QgsPalLayerSettings::FontSizeUnit, QgsPropertyDefinition( "FontSizeUnit", QObject::tr( "Font size units" ), QgsPropertyDefinition::RenderUnits, origin ) },
128  { QgsPalLayerSettings::FontTransp, QgsPropertyDefinition( "FontTransp", QObject::tr( "Text transparency" ), QgsPropertyDefinition::Opacity, origin ) },
129  { QgsPalLayerSettings::FontOpacity, QgsPropertyDefinition( "FontOpacity", QObject::tr( "Text opacity" ), QgsPropertyDefinition::Opacity, origin ) },
130  { 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 ) },
131  { QgsPalLayerSettings::FontLetterSpacing, QgsPropertyDefinition( "FontLetterSpacing", QObject::tr( "Letter spacing" ), QgsPropertyDefinition::Double, origin ) },
132  { QgsPalLayerSettings::FontWordSpacing, QgsPropertyDefinition( "FontWordSpacing", QObject::tr( "Word spacing" ), QgsPropertyDefinition::Double, origin ) },
133  { QgsPalLayerSettings::FontBlendMode, QgsPropertyDefinition( "FontBlendMode", QObject::tr( "Text blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
134  { QgsPalLayerSettings::MultiLineWrapChar, QgsPropertyDefinition( "MultiLineWrapChar", QObject::tr( "Wrap character" ), QgsPropertyDefinition::String, origin ) },
135  { QgsPalLayerSettings::AutoWrapLength, QgsPropertyDefinition( "AutoWrapLength", QObject::tr( "Automatic word wrap line length" ), QgsPropertyDefinition::IntegerPositive, origin ) },
136  { QgsPalLayerSettings::MultiLineHeight, QgsPropertyDefinition( "MultiLineHeight", QObject::tr( "Line height" ), QgsPropertyDefinition::DoublePositive, origin ) },
137  { 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 ) },
138  { QgsPalLayerSettings::TextOrientation, QgsPropertyDefinition( "TextOrientation", QgsPropertyDefinition::DataTypeString, QObject::tr( "Text orientation" ), QObject::tr( "string " ) + "[<b>horizontal</b>|<b>vertical</b>]", origin ) },
139  { QgsPalLayerSettings::DirSymbDraw, QgsPropertyDefinition( "DirSymbDraw", QObject::tr( "Draw direction symbol" ), QgsPropertyDefinition::Boolean, origin ) },
140  { QgsPalLayerSettings::DirSymbLeft, QgsPropertyDefinition( "DirSymbLeft", QObject::tr( "Left direction symbol" ), QgsPropertyDefinition::String, origin ) },
141  { QgsPalLayerSettings::DirSymbRight, QgsPropertyDefinition( "DirSymbRight", QObject::tr( "Right direction symbol" ), QgsPropertyDefinition::String, origin ) },
142  { QgsPalLayerSettings::DirSymbPlacement, QgsPropertyDefinition( "DirSymbPlacement", QgsPropertyDefinition::DataTypeString, QObject::tr( "Direction symbol placement" ), QObject::tr( "string " ) + "[<b>LeftRight</b>|<b>Above</b>|<b>Below</b>]", origin ) },
143  { QgsPalLayerSettings::DirSymbReverse, QgsPropertyDefinition( "DirSymbReverse", QObject::tr( "Reverse direction symbol" ), QgsPropertyDefinition::Boolean, origin ) },
144  { QgsPalLayerSettings::NumFormat, QgsPropertyDefinition( "NumFormat", QObject::tr( "Format as number" ), QgsPropertyDefinition::Boolean, origin ) },
145  { QgsPalLayerSettings::NumDecimals, QgsPropertyDefinition( "NumDecimals", QObject::tr( "Number of decimal places" ), QgsPropertyDefinition::IntegerPositive, origin ) },
146  { QgsPalLayerSettings::NumPlusSign, QgsPropertyDefinition( "NumPlusSign", QObject::tr( "Draw + sign" ), QgsPropertyDefinition::Boolean, origin ) },
147  { QgsPalLayerSettings::BufferDraw, QgsPropertyDefinition( "BufferDraw", QObject::tr( "Draw buffer" ), QgsPropertyDefinition::Boolean, origin ) },
148  { QgsPalLayerSettings::BufferSize, QgsPropertyDefinition( "BufferSize", QObject::tr( "Symbol size" ), QgsPropertyDefinition::DoublePositive, origin ) },
149  { QgsPalLayerSettings::BufferUnit, QgsPropertyDefinition( "BufferUnit", QObject::tr( "Buffer units" ), QgsPropertyDefinition::RenderUnits, origin ) },
150  { QgsPalLayerSettings::BufferColor, QgsPropertyDefinition( "BufferColor", QObject::tr( "Buffer color" ), QgsPropertyDefinition::ColorNoAlpha, origin ) },
151  { QgsPalLayerSettings::BufferTransp, QgsPropertyDefinition( "BufferTransp", QObject::tr( "Buffer transparency" ), QgsPropertyDefinition::Opacity, origin ) },
152  { QgsPalLayerSettings::BufferOpacity, QgsPropertyDefinition( "BufferOpacity", QObject::tr( "Buffer opacity" ), QgsPropertyDefinition::Opacity, origin ) },
153  { QgsPalLayerSettings::BufferJoinStyle, QgsPropertyDefinition( "BufferJoinStyle", QObject::tr( "Buffer join style" ), QgsPropertyDefinition::PenJoinStyle, origin ) },
154  { QgsPalLayerSettings::BufferBlendMode, QgsPropertyDefinition( "BufferBlendMode", QObject::tr( "Buffer blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
155 
156  { QgsPalLayerSettings::MaskEnabled, QgsPropertyDefinition( "MaskEnabled", QObject::tr( "Enable mask" ), QgsPropertyDefinition::Boolean, origin ) },
157  { QgsPalLayerSettings::MaskBufferSize, QgsPropertyDefinition( "MaskBufferSize", QObject::tr( "Mask buffer size" ), QgsPropertyDefinition::DoublePositive, origin ) },
158  { QgsPalLayerSettings::MaskBufferUnit, QgsPropertyDefinition( "MaskBufferUnit", QObject::tr( "Mask buffer unit" ), QgsPropertyDefinition::RenderUnits, origin ) },
159  { QgsPalLayerSettings::MaskOpacity, QgsPropertyDefinition( "MaskOpacity", QObject::tr( "Mask opacity" ), QgsPropertyDefinition::Opacity, origin ) },
160  { QgsPalLayerSettings::MaskJoinStyle, QgsPropertyDefinition( "MaskJoinStyle", QObject::tr( "Mask join style" ), QgsPropertyDefinition::PenJoinStyle, origin ) },
161 
162  { QgsPalLayerSettings::ShapeDraw, QgsPropertyDefinition( "ShapeDraw", QObject::tr( "Draw shape" ), QgsPropertyDefinition::Boolean, origin ) },
163  {
164  QgsPalLayerSettings::ShapeKind, QgsPropertyDefinition( "ShapeKind", QgsPropertyDefinition::DataTypeString, QObject::tr( "Shape type" ), QObject::tr( "string " ) + QStringLiteral( "[<b>Rectangle</b>|<b>Square</b>|<br>"
165  "<b>Ellipse</b>|<b>Circle</b>|<b>SVG</b>]" ), origin )
166  },
167  { QgsPalLayerSettings::ShapeSVGFile, QgsPropertyDefinition( "ShapeSVGFile", QObject::tr( "Shape SVG path" ), QgsPropertyDefinition::SvgPath, origin ) },
168  { QgsPalLayerSettings::ShapeSizeType, QgsPropertyDefinition( "ShapeSizeType", QgsPropertyDefinition::DataTypeString, QObject::tr( "Shape size type" ), QObject::tr( "string " ) + "[<b>Buffer</b>|<b>Fixed</b>]", origin ) },
169  { QgsPalLayerSettings::ShapeSizeX, QgsPropertyDefinition( "ShapeSizeX", QObject::tr( "Shape size (X)" ), QgsPropertyDefinition::Double, origin ) },
170  { QgsPalLayerSettings::ShapeSizeY, QgsPropertyDefinition( "ShapeSizeY", QObject::tr( "Shape size (Y)" ), QgsPropertyDefinition::Double, origin ) },
171  { QgsPalLayerSettings::ShapeSizeUnits, QgsPropertyDefinition( "ShapeSizeUnits", QObject::tr( "Shape size units" ), QgsPropertyDefinition::RenderUnits, origin ) },
172  { QgsPalLayerSettings::ShapeRotationType, QgsPropertyDefinition( "ShapeRotationType", QgsPropertyDefinition::DataTypeString, QObject::tr( "Shape rotation type" ), QObject::tr( "string " ) + "[<b>Sync</b>|<b>Offset</b>|<b>Fixed</b>]", origin ) },
173  { QgsPalLayerSettings::ShapeRotation, QgsPropertyDefinition( "ShapeRotation", QObject::tr( "Shape rotation" ), QgsPropertyDefinition::Rotation, origin ) },
174  { QgsPalLayerSettings::ShapeOffset, QgsPropertyDefinition( "ShapeOffset", QObject::tr( "Shape offset" ), QgsPropertyDefinition::Offset, origin ) },
175  { QgsPalLayerSettings::ShapeOffsetUnits, QgsPropertyDefinition( "ShapeOffsetUnits", QObject::tr( "Shape offset units" ), QgsPropertyDefinition::RenderUnits, origin ) },
176  { QgsPalLayerSettings::ShapeRadii, QgsPropertyDefinition( "ShapeRadii", QObject::tr( "Shape radii" ), QgsPropertyDefinition::Size2D, origin ) },
177  { QgsPalLayerSettings::ShapeRadiiUnits, QgsPropertyDefinition( "ShapeRadiiUnits", QObject::tr( "Symbol radii units" ), QgsPropertyDefinition::RenderUnits, origin ) },
178  { QgsPalLayerSettings::ShapeTransparency, QgsPropertyDefinition( "ShapeTransparency", QObject::tr( "Shape transparency" ), QgsPropertyDefinition::Opacity, origin ) },
179  { QgsPalLayerSettings::ShapeOpacity, QgsPropertyDefinition( "ShapeOpacity", QObject::tr( "Shape opacity" ), QgsPropertyDefinition::Opacity, origin ) },
180  { QgsPalLayerSettings::ShapeBlendMode, QgsPropertyDefinition( "ShapeBlendMode", QObject::tr( "Shape blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
181  { QgsPalLayerSettings::ShapeFillColor, QgsPropertyDefinition( "ShapeFillColor", QObject::tr( "Shape fill color" ), QgsPropertyDefinition::ColorWithAlpha, origin ) },
182  { QgsPalLayerSettings::ShapeStrokeColor, QgsPropertyDefinition( "ShapeBorderColor", QObject::tr( "Shape stroke color" ), QgsPropertyDefinition::ColorWithAlpha, origin ) },
183  { QgsPalLayerSettings::ShapeStrokeWidth, QgsPropertyDefinition( "ShapeBorderWidth", QObject::tr( "Shape stroke width" ), QgsPropertyDefinition::StrokeWidth, origin ) },
184  { QgsPalLayerSettings::ShapeStrokeWidthUnits, QgsPropertyDefinition( "ShapeBorderWidthUnits", QObject::tr( "Shape stroke width units" ), QgsPropertyDefinition::RenderUnits, origin ) },
185  { QgsPalLayerSettings::ShapeJoinStyle, QgsPropertyDefinition( "ShapeJoinStyle", QObject::tr( "Shape join style" ), QgsPropertyDefinition::PenJoinStyle, origin ) },
186  { QgsPalLayerSettings::ShadowDraw, QgsPropertyDefinition( "ShadowDraw", QObject::tr( "Draw shadow" ), QgsPropertyDefinition::Boolean, origin ) },
187  {
188  QgsPalLayerSettings::ShadowUnder, QgsPropertyDefinition( "ShadowUnder", QgsPropertyDefinition::DataTypeString, QObject::tr( "Symbol size" ), QObject::tr( "string " ) + QStringLiteral( "[<b>Lowest</b>|<b>Text</b>|<br>"
189  "<b>Buffer</b>|<b>Background</b>]" ), origin )
190  },
191  { QgsPalLayerSettings::ShadowOffsetAngle, QgsPropertyDefinition( "ShadowOffsetAngle", QObject::tr( "Shadow offset angle" ), QgsPropertyDefinition::Rotation, origin ) },
192  { QgsPalLayerSettings::ShadowOffsetDist, QgsPropertyDefinition( "ShadowOffsetDist", QObject::tr( "Shadow offset distance" ), QgsPropertyDefinition::DoublePositive, origin ) },
193  { QgsPalLayerSettings::ShadowOffsetUnits, QgsPropertyDefinition( "ShadowOffsetUnits", QObject::tr( "Shadow offset units" ), QgsPropertyDefinition::RenderUnits, origin ) },
194  { QgsPalLayerSettings::ShadowRadius, QgsPropertyDefinition( "ShadowRadius", QObject::tr( "Shadow blur radius" ), QgsPropertyDefinition::DoublePositive, origin ) },
195  { QgsPalLayerSettings::ShadowRadiusUnits, QgsPropertyDefinition( "ShadowRadiusUnits", QObject::tr( "Shadow blur units" ), QgsPropertyDefinition::RenderUnits, origin ) },
196  { QgsPalLayerSettings::ShadowTransparency, QgsPropertyDefinition( "ShadowTransparency", QObject::tr( "Shadow transparency" ), QgsPropertyDefinition::Opacity, origin ) },
197  { QgsPalLayerSettings::ShadowOpacity, QgsPropertyDefinition( "ShadowOpacity", QObject::tr( "Shadow opacity" ), QgsPropertyDefinition::Opacity, origin ) },
198  { QgsPalLayerSettings::ShadowScale, QgsPropertyDefinition( "ShadowScale", QObject::tr( "Shadow scale" ), QgsPropertyDefinition::IntegerPositive, origin ) },
199  { QgsPalLayerSettings::ShadowColor, QgsPropertyDefinition( "ShadowColor", QObject::tr( "Shadow color" ), QgsPropertyDefinition::ColorNoAlpha, origin ) },
200  { QgsPalLayerSettings::ShadowBlendMode, QgsPropertyDefinition( "ShadowBlendMode", QObject::tr( "Shadow blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
201 
202  { QgsPalLayerSettings::CentroidWhole, QgsPropertyDefinition( "CentroidWhole", QgsPropertyDefinition::DataTypeString, QObject::tr( "Centroid of whole shape" ), QObject::tr( "string " ) + "[<b>Visible</b>|<b>Whole</b>]", origin ) },
203  {
204  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>"
205  "<b>3</b>=Left|<b>4</b>=Over|<b>5</b>=Right|<br>"
206  "<b>6</b>=Below Left|<b>7</b>=Below|<b>8</b>=Below Right]" ), origin )
207  },
208  { QgsPalLayerSettings::OffsetXY, QgsPropertyDefinition( "OffsetXY", QObject::tr( "Offset" ), QgsPropertyDefinition::Offset, origin ) },
209  { QgsPalLayerSettings::OffsetUnits, QgsPropertyDefinition( "OffsetUnits", QObject::tr( "Offset units" ), QgsPropertyDefinition::RenderUnits, origin ) },
210  { QgsPalLayerSettings::LabelDistance, QgsPropertyDefinition( "LabelDistance", QObject::tr( "Label distance" ), QgsPropertyDefinition::Double, origin ) },
211  { QgsPalLayerSettings::DistanceUnits, QgsPropertyDefinition( "DistanceUnits", QObject::tr( "Label distance units" ), QgsPropertyDefinition::RenderUnits, origin ) },
212  { QgsPalLayerSettings::OffsetRotation, QgsPropertyDefinition( "OffsetRotation", QObject::tr( "Offset rotation" ), QgsPropertyDefinition::Rotation, origin ) },
213  { 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 ) },
214  { QgsPalLayerSettings::RepeatDistance, QgsPropertyDefinition( "RepeatDistance", QObject::tr( "Repeat distance" ), QgsPropertyDefinition::DoublePositive, origin ) },
215  { QgsPalLayerSettings::RepeatDistanceUnit, QgsPropertyDefinition( "RepeatDistanceUnit", QObject::tr( "Repeat distance unit" ), QgsPropertyDefinition::RenderUnits, origin ) },
216  { QgsPalLayerSettings::OverrunDistance, QgsPropertyDefinition( "OverrunDistance", QObject::tr( "Overrun distance" ), QgsPropertyDefinition::DoublePositive, origin ) },
217  { QgsPalLayerSettings::Priority, QgsPropertyDefinition( "Priority", QgsPropertyDefinition::DataTypeNumeric, QObject::tr( "Label priority" ), QObject::tr( "double [0.0-10.0]" ), origin ) },
218  { QgsPalLayerSettings::IsObstacle, QgsPropertyDefinition( "IsObstacle", QObject::tr( "Feature is a label obstacle" ), QgsPropertyDefinition::Boolean, origin ) },
219  { QgsPalLayerSettings::ObstacleFactor, QgsPropertyDefinition( "ObstacleFactor", QgsPropertyDefinition::DataTypeNumeric, QObject::tr( "Obstacle factor" ), QObject::tr( "double [0.0-10.0]" ), origin ) },
220  {
221  QgsPalLayerSettings::PredefinedPositionOrder, QgsPropertyDefinition( "PredefinedPositionOrder", QgsPropertyDefinition::DataTypeString, QObject::tr( "Predefined position order" ), QObject::tr( "Comma separated list of placements in order of priority<br>" )
222  + QStringLiteral( "[<b>TL</b>=Top left|<b>TSL</b>=Top, slightly left|<b>T</b>=Top middle|<br>"
223  "<b>TSR</b>=Top, slightly right|<b>TR</b>=Top right|<br>"
224  "<b>L</b>=Left|<b>R</b>=Right|<br>"
225  "<b>BL</b>=Bottom left|<b>BSL</b>=Bottom, slightly left|<b>B</b>=Bottom middle|<br>"
226  "<b>BSR</b>=Bottom, slightly right|<b>BR</b>=Bottom right]" ), origin )
227  },
228  {
229  QgsPalLayerSettings::LinePlacementOptions, QgsPropertyDefinition( "LinePlacementFlags", QgsPropertyDefinition::DataTypeString, QObject::tr( "Line placement options" ), QObject::tr( "Comma separated list of placement options<br>" )
230  + QStringLiteral( "[<b>OL</b>=On line|<b>AL</b>=Above line|<b>BL</b>=Below line|<br>"
231  "<b>LO</b>=Respect line orientation]" ), origin )
232  },
233  { QgsPalLayerSettings::PolygonLabelOutside, QgsPropertyDefinition( "PolygonLabelOutside", QgsPropertyDefinition::DataTypeString, QObject::tr( "Label outside polygons" ), QObject::tr( "string " ) + "[<b>yes</b> (allow placing outside)|<b>no</b> (never place outside)|<b>force</b> (always place outside)]", origin ) },
234  { QgsPalLayerSettings::PositionX, QgsPropertyDefinition( "PositionX", QObject::tr( "Position (X)" ), QgsPropertyDefinition::Double, origin ) },
235  { QgsPalLayerSettings::PositionY, QgsPropertyDefinition( "PositionY", QObject::tr( "Position (Y)" ), QgsPropertyDefinition::Double, origin ) },
236  { QgsPalLayerSettings::Hali, QgsPropertyDefinition( "Hali", QgsPropertyDefinition::DataTypeString, QObject::tr( "Horizontal alignment" ), QObject::tr( "string " ) + "[<b>Left</b>|<b>Center</b>|<b>Right</b>]", origin ) },
237  {
238  QgsPalLayerSettings::Vali, QgsPropertyDefinition( "Vali", QgsPropertyDefinition::DataTypeString, QObject::tr( "Vertical alignment" ), QObject::tr( "string " ) + QStringLiteral( "[<b>Bottom</b>|<b>Base</b>|<br>"
239  "<b>Half</b>|<b>Cap</b>|<b>Top</b>]" ), origin )
240  },
241  { QgsPalLayerSettings::Rotation, QgsPropertyDefinition( "Rotation", QObject::tr( "Label rotation (deprecated)" ), QgsPropertyDefinition::Rotation, origin ) },
242  { QgsPalLayerSettings::LabelRotation, QgsPropertyDefinition( "LabelRotation", QObject::tr( "Label rotation" ), QgsPropertyDefinition::Rotation, origin ) },
243  { QgsPalLayerSettings::ScaleVisibility, QgsPropertyDefinition( "ScaleVisibility", QObject::tr( "Scale based visibility" ), QgsPropertyDefinition::Boolean, origin ) },
244  { QgsPalLayerSettings::MinScale, QgsPropertyDefinition( "MinScale", QObject::tr( "Minimum scale (denominator)" ), QgsPropertyDefinition::Double, origin ) },
245  { QgsPalLayerSettings::MaxScale, QgsPropertyDefinition( "MaxScale", QObject::tr( "Maximum scale (denominator)" ), QgsPropertyDefinition::Double, origin ) },
246  { QgsPalLayerSettings::MinimumScale, QgsPropertyDefinition( "MinimumScale", QObject::tr( "Minimum scale (denominator)" ), QgsPropertyDefinition::Double, origin ) },
247  { QgsPalLayerSettings::MaximumScale, QgsPropertyDefinition( "MaximumScale", QObject::tr( "Maximum scale (denominator)" ), QgsPropertyDefinition::Double, origin ) },
248 
249  { QgsPalLayerSettings::FontLimitPixel, QgsPropertyDefinition( "FontLimitPixel", QObject::tr( "Limit font pixel size" ), QgsPropertyDefinition::Boolean, origin ) },
250  { QgsPalLayerSettings::FontMinPixel, QgsPropertyDefinition( "FontMinPixel", QObject::tr( "Minimum pixel size" ), QgsPropertyDefinition::IntegerPositive, origin ) },
251  { QgsPalLayerSettings::FontMaxPixel, QgsPropertyDefinition( "FontMaxPixel", QObject::tr( "Maximum pixel size" ), QgsPropertyDefinition::IntegerPositive, origin ) },
252  { QgsPalLayerSettings::ZIndex, QgsPropertyDefinition( "ZIndex", QObject::tr( "Label z-index" ), QgsPropertyDefinition::Double, origin ) },
253  { QgsPalLayerSettings::Show, QgsPropertyDefinition( "Show", QObject::tr( "Show label" ), QgsPropertyDefinition::Boolean, origin ) },
254  { QgsPalLayerSettings::AlwaysShow, QgsPropertyDefinition( "AlwaysShow", QObject::tr( "Always show label" ), QgsPropertyDefinition::Boolean, origin ) },
255  { QgsPalLayerSettings::CalloutDraw, QgsPropertyDefinition( "CalloutDraw", QObject::tr( "Draw callout" ), QgsPropertyDefinition::Boolean, origin ) },
256  { QgsPalLayerSettings::LabelAllParts, QgsPropertyDefinition( "LabelAllParts", QObject::tr( "Label all parts" ), QgsPropertyDefinition::Boolean, origin ) },
257  };
258 }
259 
260 Q_NOWARN_DEPRECATED_PUSH // because of deprecated members
262  : predefinedPositionOrder( *DEFAULT_PLACEMENT_ORDER() )
263  , mCallout( QgsApplication::calloutRegistry()->defaultCallout() )
264 {
265  initPropertyDefinitions();
266 }
268 
269 Q_NOWARN_DEPRECATED_PUSH // because of deprecated members
271  : fieldIndex( 0 )
272  , mDataDefinedProperties( s.mDataDefinedProperties )
273 {
274  *this = s;
275 }
277 
279 {
280  if ( this == &s )
281  return *this;
282 
283  // copy only permanent stuff
284 
286 
287  // text style
288  fieldName = s.fieldName;
295 
296  // text formatting
297  wrapChar = s.wrapChar;
307  decimals = s.decimals;
308  plusSign = s.plusSign;
309 
310  // placement
311  placement = s.placement;
313  mPolygonPlacementFlags = s.mPolygonPlacementFlags;
319  xOffset = s.xOffset;
320  yOffset = s.yOffset;
323  dist = s.dist;
325  distUnits = s.distUnits;
331  priority = s.priority;
338 
339  // rendering
348 
351  zIndex = s.zIndex;
352 
353  mFormat = s.mFormat;
354  mDataDefinedProperties = s.mDataDefinedProperties;
355 
356  mCallout.reset( s.mCallout ? s.mCallout->clone() : nullptr );
357 
358  mObstacleSettings = s.mObstacleSettings;
359  mThinningSettings = s.mThinningSettings;
360 
364  layerType = s.layerType;
365 
366  return *this;
367 }
368 
369 bool QgsPalLayerSettings::prepare( QgsRenderContext &context, QSet<QString> &attributeNames, const QgsFields &fields, const QgsMapSettings &mapSettings, const QgsCoordinateReferenceSystem &crs )
370 {
371  if ( drawLabels )
372  {
373  if ( fieldName.isEmpty() )
374  {
375  return false;
376  }
377 
378  if ( isExpression )
379  {
380  QgsExpression exp( fieldName );
381  if ( exp.hasEvalError() )
382  {
383  QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 );
384  return false;
385  }
386  }
387  else
388  {
389  // If we aren't an expression, we check to see if we can find the column.
390  if ( fields.lookupField( fieldName ) == -1 )
391  {
392  return false;
393  }
394  }
395  }
396 
397  mCurFields = fields;
398 
399  if ( drawLabels || mObstacleSettings.isObstacle() )
400  {
401  if ( drawLabels )
402  {
403  // add field indices for label's text, from expression or field
404  if ( isExpression )
405  {
406  // prepare expression for use in QgsPalLayerSettings::registerFeature()
408  exp->prepare( &context.expressionContext() );
409  if ( exp->hasEvalError() )
410  {
411  QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 );
412  }
413  const auto referencedColumns = exp->referencedColumns();
414  for ( const QString &name : referencedColumns )
415  {
416  attributeNames.insert( name );
417  }
418  }
419  else
420  {
421  attributeNames.insert( fieldName );
422  }
423  }
424 
425  mDataDefinedProperties.prepare( context.expressionContext() );
426  // add field indices of data defined expression or field
427  attributeNames.unite( dataDefinedProperties().referencedFields( context.expressionContext() ) );
428  }
429 
430  // NOW INITIALIZE QgsPalLayerSettings
431 
432  // TODO: ideally these (non-configuration) members should get out of QgsPalLayerSettings to QgsVectorLayerLabelProvider::prepare
433  // (together with registerFeature() & related methods) and QgsPalLayerSettings just stores config
434 
435  // save the pal layer to our layer context (with some additional info)
436  fieldIndex = fields.lookupField( fieldName );
437 
438  xform = &mapSettings.mapToPixel();
440  if ( context.coordinateTransform().isValid() )
441  // this is context for layer rendering
442  ct = context.coordinateTransform();
443  else
444  {
445  // otherwise fall back to creating our own CT
446  ct = QgsCoordinateTransform( crs, mapSettings.destinationCrs(), mapSettings.transformContext() );
447  }
448  ptZero = xform->toMapCoordinates( 0, 0 );
449  ptOne = xform->toMapCoordinates( 1, 0 );
450 
451  // rect for clipping
453  if ( !qgsDoubleNear( mapSettings.rotation(), 0.0 ) )
454  {
455  //PAL features are prerotated, so extent also needs to be unrotated
456  extentGeom.rotate( -mapSettings.rotation(), mapSettings.visibleExtent().center() );
457  }
458 
459  mFeatsSendingToPal = 0;
460 
462  {
463  mGeometryGeneratorExpression = QgsExpression( geometryGenerator );
464  mGeometryGeneratorExpression.prepare( &context.expressionContext() );
465  if ( mGeometryGeneratorExpression.hasParserError() )
466  {
467  QgsMessageLog::logMessage( mGeometryGeneratorExpression.parserErrorString(), QObject::tr( "Labeling" ) );
468  return false;
469  }
470 
471  const auto referencedColumns = mGeometryGeneratorExpression.referencedColumns();
472  for ( const QString &name : referencedColumns )
473  {
474  attributeNames.insert( name );
475  }
476  }
477  attributeNames.unite( mFormat.referencedFields( context ) );
478 
479  if ( mCallout )
480  {
481  const auto referencedColumns = mCallout->referencedFields( context );
482  for ( const QString &name : referencedColumns )
483  {
484  attributeNames.insert( name );
485  }
486  }
487 
488  return true;
489 }
490 
491 QSet<QString> QgsPalLayerSettings::referencedFields( const QgsRenderContext &context ) const
492 {
493  QSet<QString> referenced;
494  if ( drawLabels )
495  {
496  if ( isExpression )
497  {
498  referenced.unite( QgsExpression( fieldName ).referencedColumns() );
499  }
500  else
501  {
502  referenced.insert( fieldName );
503  }
504  }
505 
506  referenced.unite( mFormat.referencedFields( context ) );
507 
508  // calling referencedFields() with ignoreContext=true because in our expression context
509  // we do not have valid QgsFields yet - because of that the field names from expressions
510  // wouldn't get reported
511  referenced.unite( mDataDefinedProperties.referencedFields( context.expressionContext(), true ) );
512 
514  {
515  QgsExpression geomGeneratorExpr( geometryGenerator );
516  referenced.unite( geomGeneratorExpr.referencedColumns() );
517  }
518 
519  if ( mCallout )
520  {
521  referenced.unite( mCallout->referencedFields( context ) );
522  }
523 
524  return referenced;
525 }
526 
528 {
529  if ( mRenderStarted )
530  {
531  qWarning( "Start render called for when a previous render was already underway!!" );
532  return;
533  }
534 
536  {
537  // force horizontal orientation, other orientation modes aren't unsupported for curved placement
539  mDataDefinedProperties.property( QgsPalLayerSettings::TextOrientation ).setActive( false );
540  }
541 
542  if ( mCallout )
543  {
544  mCallout->startRender( context );
545  }
546 
547  mRenderStarted = true;
548 }
549 
551 {
552  if ( !mRenderStarted )
553  {
554  qWarning( "Stop render called for QgsPalLayerSettings without a startRender call!" );
555  return;
556  }
557 
558  if ( mCallout )
559  {
560  mCallout->stopRender( context );
561  }
562 
563  mRenderStarted = false;
564 }
565 
567 {
568  if ( mRenderStarted )
569  {
570  qWarning( "stopRender was not called on QgsPalLayerSettings object!" );
571  }
572 
573  // pal layer is deleted internally in PAL
574 
575  delete expression;
576 }
577 
578 
580 {
581  initPropertyDefinitions();
582  return *sPropertyDefinitions();
583 }
584 
586 {
587  if ( !expression )
588  {
589  expression = new QgsExpression( fieldName );
590  }
591  return expression;
592 }
593 
594 QString updateDataDefinedString( const QString &value )
595 {
596  // TODO: update or remove this when project settings for labeling are migrated to better XML layout
597  QString newValue = value;
598  if ( !value.isEmpty() && !value.contains( QLatin1String( "~~" ) ) )
599  {
600  QStringList values;
601  values << QStringLiteral( "1" ); // all old-style values are active if not empty
602  values << QStringLiteral( "0" );
603  values << QString();
604  values << value; // all old-style values are only field names
605  newValue = values.join( QStringLiteral( "~~" ) );
606  }
607 
608  return newValue;
609 }
610 
611 void QgsPalLayerSettings::readOldDataDefinedProperty( QgsVectorLayer *layer, QgsPalLayerSettings::Property p )
612 {
613  QString newPropertyName = "labeling/dataDefined/" + sPropertyDefinitions()->value( p ).name();
614  QVariant newPropertyField = layer->customProperty( newPropertyName, QVariant() );
615 
616  if ( !newPropertyField.isValid() )
617  return;
618 
619  QString ddString = newPropertyField.toString();
620 
621  if ( !ddString.isEmpty() && ddString != QLatin1String( "0~~0~~~~" ) )
622  {
623  // TODO: update this when project settings for labeling are migrated to better XML layout
624  QString newStyleString = updateDataDefinedString( ddString );
625  QStringList ddv = newStyleString.split( QStringLiteral( "~~" ) );
626 
627  bool active = ddv.at( 0 ).toInt();
628  if ( ddv.at( 1 ).toInt() )
629  {
630  mDataDefinedProperties.setProperty( p, QgsProperty::fromExpression( ddv.at( 2 ), active ) );
631  }
632  else
633  {
634  mDataDefinedProperties.setProperty( p, QgsProperty::fromField( ddv.at( 3 ), active ) );
635  }
636  }
637  else
638  {
639  // remove unused properties
640  layer->removeCustomProperty( newPropertyName );
641  }
642 }
643 
644 void QgsPalLayerSettings::readOldDataDefinedPropertyMap( QgsVectorLayer *layer, QDomElement *parentElem )
645 {
646  if ( !layer && !parentElem )
647  {
648  return;
649  }
650 
651  QgsPropertiesDefinition::const_iterator i = sPropertyDefinitions()->constBegin();
652  for ( ; i != sPropertyDefinitions()->constEnd(); ++i )
653  {
654  if ( layer )
655  {
656  // reading from layer's custom properties
657  readOldDataDefinedProperty( layer, static_cast< Property >( i.key() ) );
658  }
659  else if ( parentElem )
660  {
661  // reading from XML
662  QDomElement e = parentElem->firstChildElement( i.value().name() );
663  if ( !e.isNull() )
664  {
665  bool active = e.attribute( QStringLiteral( "active" ) ).compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
666  bool isExpression = e.attribute( QStringLiteral( "useExpr" ) ).compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
667  if ( isExpression )
668  {
669  mDataDefinedProperties.setProperty( i.key(), QgsProperty::fromExpression( e.attribute( QStringLiteral( "expr" ) ), active ) );
670  }
671  else
672  {
673  mDataDefinedProperties.setProperty( i.key(), QgsProperty::fromField( e.attribute( QStringLiteral( "field" ) ), active ) );
674  }
675  }
676  }
677  }
678 }
679 
680 void QgsPalLayerSettings::readFromLayerCustomProperties( QgsVectorLayer *layer )
681 {
682  if ( layer->customProperty( QStringLiteral( "labeling" ) ).toString() != QLatin1String( "pal" ) )
683  {
684  if ( layer->geometryType() == QgsWkbTypes::PointGeometry )
686 
687  // for polygons the "over point" (over centroid) placement is better than the default
688  // "around point" (around centroid) which is more suitable for points
689  if ( layer->geometryType() == QgsWkbTypes::PolygonGeometry )
691 
692  return; // there's no information available
693  }
694 
695  // NOTE: set defaults for newly added properties, for backwards compatibility
696 
697  drawLabels = layer->customProperty( QStringLiteral( "labeling/drawLabels" ), true ).toBool();
698 
699  mFormat.readFromLayer( layer );
700 
701  // text style
702  fieldName = layer->customProperty( QStringLiteral( "labeling/fieldName" ) ).toString();
703  isExpression = layer->customProperty( QStringLiteral( "labeling/isExpression" ) ).toBool();
705  previewBkgrdColor = QColor( layer->customProperty( QStringLiteral( "labeling/previewBkgrdColor" ), QVariant( "#ffffff" ) ).toString() );
707  QDomDocument doc( QStringLiteral( "substitutions" ) );
708  doc.setContent( layer->customProperty( QStringLiteral( "labeling/substitutions" ) ).toString() );
709  QDomElement replacementElem = doc.firstChildElement( QStringLiteral( "substitutions" ) );
710  substitutions.readXml( replacementElem );
711  useSubstitutions = layer->customProperty( QStringLiteral( "labeling/useSubstitutions" ) ).toBool();
712 
713  // text formatting
714  wrapChar = layer->customProperty( QStringLiteral( "labeling/wrapChar" ) ).toString();
715  autoWrapLength = layer->customProperty( QStringLiteral( "labeling/autoWrapLength" ) ).toInt();
716  useMaxLineLengthForAutoWrap = layer->customProperty( QStringLiteral( "labeling/useMaxLineLengthForAutoWrap" ), QStringLiteral( "1" ) ).toBool();
717 
718  multilineAlign = static_cast< MultiLineAlign >( layer->customProperty( QStringLiteral( "labeling/multilineAlign" ), QVariant( MultiFollowPlacement ) ).toUInt() );
719  addDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/addDirectionSymbol" ) ).toBool();
720  leftDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/leftDirectionSymbol" ), QVariant( "<" ) ).toString();
721  rightDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/rightDirectionSymbol" ), QVariant( ">" ) ).toString();
722  reverseDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/reverseDirectionSymbol" ) ).toBool();
723  placeDirectionSymbol = static_cast< DirectionSymbols >( layer->customProperty( QStringLiteral( "labeling/placeDirectionSymbol" ), QVariant( SymbolLeftRight ) ).toUInt() );
724  formatNumbers = layer->customProperty( QStringLiteral( "labeling/formatNumbers" ) ).toBool();
725  decimals = layer->customProperty( QStringLiteral( "labeling/decimals" ) ).toInt();
726  plusSign = layer->customProperty( QStringLiteral( "labeling/plussign" ) ).toBool();
727 
728  // placement
729  placement = static_cast< Placement >( layer->customProperty( QStringLiteral( "labeling/placement" ) ).toInt() );
730  placementFlags = layer->customProperty( QStringLiteral( "labeling/placementFlags" ) ).toUInt();
731  centroidWhole = layer->customProperty( QStringLiteral( "labeling/centroidWhole" ), QVariant( false ) ).toBool();
732  centroidInside = layer->customProperty( QStringLiteral( "labeling/centroidInside" ), QVariant( false ) ).toBool();
733  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( layer->customProperty( QStringLiteral( "labeling/predefinedPositionOrder" ) ).toString() );
734  if ( predefinedPositionOrder.isEmpty() )
735  predefinedPositionOrder = *DEFAULT_PLACEMENT_ORDER();
736  fitInPolygonOnly = layer->customProperty( QStringLiteral( "labeling/fitInPolygonOnly" ), QVariant( false ) ).toBool();
737  dist = layer->customProperty( QStringLiteral( "labeling/dist" ) ).toDouble();
738  distUnits = layer->customProperty( QStringLiteral( "labeling/distInMapUnits" ) ).toBool() ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters;
739  if ( layer->customProperty( QStringLiteral( "labeling/distMapUnitScale" ) ).toString().isEmpty() )
740  {
741  //fallback to older property
742  double oldMin = layer->customProperty( QStringLiteral( "labeling/distMapUnitMinScale" ), 0.0 ).toDouble();
743  distMapUnitScale.minScale = !qgsDoubleNear( oldMin, 0.0 ) ? 1.0 / oldMin : 0;
744  double oldMax = layer->customProperty( QStringLiteral( "labeling/distMapUnitMaxScale" ), 0.0 ).toDouble();
745  distMapUnitScale.maxScale = !qgsDoubleNear( oldMax, 0.0 ) ? 1.0 / oldMax : 0;
746  }
747  else
748  {
749  distMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/distMapUnitScale" ) ).toString() );
750  }
751  offsetType = static_cast< OffsetType >( layer->customProperty( QStringLiteral( "labeling/offsetType" ), QVariant( FromPoint ) ).toUInt() );
752  quadOffset = static_cast< QuadrantPosition >( layer->customProperty( QStringLiteral( "labeling/quadOffset" ), QVariant( QuadrantOver ) ).toUInt() );
753  xOffset = layer->customProperty( QStringLiteral( "labeling/xOffset" ), QVariant( 0.0 ) ).toDouble();
754  yOffset = layer->customProperty( QStringLiteral( "labeling/yOffset" ), QVariant( 0.0 ) ).toDouble();
755  if ( layer->customProperty( QStringLiteral( "labeling/labelOffsetInMapUnits" ), QVariant( true ) ).toBool() )
757  else
759 
760  if ( layer->customProperty( QStringLiteral( "labeling/labelOffsetMapUnitScale" ) ).toString().isEmpty() )
761  {
762  //fallback to older property
763  double oldMin = layer->customProperty( QStringLiteral( "labeling/labelOffsetMapUnitMinScale" ), 0.0 ).toDouble();
764  labelOffsetMapUnitScale.minScale = !qgsDoubleNear( oldMin, 0.0 ) ? 1.0 / oldMin : 0;
765  double oldMax = layer->customProperty( QStringLiteral( "labeling/labelOffsetMapUnitMaxScale" ), 0.0 ).toDouble();
766  labelOffsetMapUnitScale.maxScale = !qgsDoubleNear( oldMax, 0 ) ? 1.0 / oldMax : 0;
767  }
768  else
769  {
770  labelOffsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/labelOffsetMapUnitScale" ) ).toString() );
771  }
772 
773  QVariant tempAngle = layer->customProperty( QStringLiteral( "labeling/angleOffset" ), QVariant() );
774  if ( tempAngle.isValid() )
775  {
776  double oldAngle = layer->customProperty( QStringLiteral( "labeling/angleOffset" ), QVariant( 0.0 ) ).toDouble();
777  angleOffset = std::fmod( 360 - oldAngle, 360.0 );
778  }
779  else
780  {
781  angleOffset = layer->customProperty( QStringLiteral( "labeling/rotationAngle" ), QVariant( 0.0 ) ).toDouble();
782  }
783 
784  preserveRotation = layer->customProperty( QStringLiteral( "labeling/preserveRotation" ), QVariant( true ) ).toBool();
785  maxCurvedCharAngleIn = layer->customProperty( QStringLiteral( "labeling/maxCurvedCharAngleIn" ), QVariant( 25.0 ) ).toDouble();
786  maxCurvedCharAngleOut = layer->customProperty( QStringLiteral( "labeling/maxCurvedCharAngleOut" ), QVariant( -25.0 ) ).toDouble();
787  priority = layer->customProperty( QStringLiteral( "labeling/priority" ) ).toInt();
788  repeatDistance = layer->customProperty( QStringLiteral( "labeling/repeatDistance" ), 0.0 ).toDouble();
789  switch ( layer->customProperty( QStringLiteral( "labeling/repeatDistanceUnit" ), QVariant( 1 ) ).toUInt() )
790  {
791  case 0:
793  break;
794  case 1:
796  break;
797  case 2:
799  break;
800  case 3:
802  break;
803  }
804  if ( layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitScale" ) ).toString().isEmpty() )
805  {
806  //fallback to older property
807  double oldMin = layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitMinScale" ), 0.0 ).toDouble();
808  repeatDistanceMapUnitScale.minScale = !qgsDoubleNear( oldMin, 0 ) ? 1.0 / oldMin : 0;
809  double oldMax = layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitMaxScale" ), 0.0 ).toDouble();
810  repeatDistanceMapUnitScale.maxScale = !qgsDoubleNear( oldMax, 0 ) ? 1.0 / oldMax : 0;
811  }
812  else
813  {
814  repeatDistanceMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitScale" ) ).toString() );
815  }
816 
817  // rendering
818  double scalemn = layer->customProperty( QStringLiteral( "labeling/scaleMin" ), QVariant( 0 ) ).toDouble();
819  double scalemx = layer->customProperty( QStringLiteral( "labeling/scaleMax" ), QVariant( 0 ) ).toDouble();
820 
821  // fix for scale visibility limits being keyed off of just its values in the past (<2.0)
822  QVariant scalevis = layer->customProperty( QStringLiteral( "labeling/scaleVisibility" ), QVariant() );
823  if ( scalevis.isValid() )
824  {
825  scaleVisibility = scalevis.toBool();
826  maximumScale = scalemn;
827  minimumScale = scalemx;
828  }
829  else if ( scalemn > 0 || scalemx > 0 )
830  {
831  scaleVisibility = true;
832  maximumScale = scalemn;
833  minimumScale = scalemx;
834  }
835  else
836  {
837  // keep scaleMin and scaleMax at new 1.0 defaults (1 and 10000000, were 0 and 0)
838  scaleVisibility = false;
839  }
840 
841 
842  fontLimitPixelSize = layer->customProperty( QStringLiteral( "labeling/fontLimitPixelSize" ), QVariant( false ) ).toBool();
843  fontMinPixelSize = layer->customProperty( QStringLiteral( "labeling/fontMinPixelSize" ), QVariant( 0 ) ).toInt();
844  fontMaxPixelSize = layer->customProperty( QStringLiteral( "labeling/fontMaxPixelSize" ), QVariant( 10000 ) ).toInt();
845  displayAll = layer->customProperty( QStringLiteral( "labeling/displayAll" ), QVariant( false ) ).toBool();
846  upsidedownLabels = static_cast< UpsideDownLabels >( layer->customProperty( QStringLiteral( "labeling/upsidedownLabels" ), QVariant( Upright ) ).toUInt() );
847 
848  labelPerPart = layer->customProperty( QStringLiteral( "labeling/labelPerPart" ) ).toBool();
849  mergeLines = layer->customProperty( QStringLiteral( "labeling/mergeLines" ) ).toBool();
850  mThinningSettings.setMinimumFeatureSize( layer->customProperty( QStringLiteral( "labeling/minFeatureSize" ) ).toDouble() );
851  mThinningSettings.setLimitNumberLabelsEnabled( layer->customProperty( QStringLiteral( "labeling/limitNumLabels" ), QVariant( false ) ).toBool() );
852  mThinningSettings.setMaximumNumberLabels( layer->customProperty( QStringLiteral( "labeling/maxNumLabels" ), QVariant( 2000 ) ).toInt() );
853  mObstacleSettings.setIsObstacle( layer->customProperty( QStringLiteral( "labeling/obstacle" ), QVariant( true ) ).toBool() );
854  mObstacleSettings.setFactor( layer->customProperty( QStringLiteral( "labeling/obstacleFactor" ), QVariant( 1.0 ) ).toDouble() );
855  mObstacleSettings.setType( static_cast< QgsLabelObstacleSettings::ObstacleType >( layer->customProperty( QStringLiteral( "labeling/obstacleType" ), QVariant( PolygonInterior ) ).toUInt() ) );
856  zIndex = layer->customProperty( QStringLiteral( "labeling/zIndex" ), QVariant( 0.0 ) ).toDouble();
857 
858  mDataDefinedProperties.clear();
859  if ( layer->customProperty( QStringLiteral( "labeling/ddProperties" ) ).isValid() )
860  {
861  QDomDocument doc( QStringLiteral( "dd" ) );
862  doc.setContent( layer->customProperty( QStringLiteral( "labeling/ddProperties" ) ).toString() );
863  QDomElement elem = doc.firstChildElement( QStringLiteral( "properties" ) );
864  mDataDefinedProperties.readXml( elem, *sPropertyDefinitions() );
865  }
866  else
867  {
868  // read QGIS 2.x style data defined properties
869  readOldDataDefinedPropertyMap( layer, nullptr );
870  }
871  // upgrade older data defined settings
872  if ( mDataDefinedProperties.isActive( FontTransp ) )
873  {
874  mDataDefinedProperties.setProperty( FontOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( FontTransp ).asExpression() ) ) );
875  mDataDefinedProperties.setProperty( FontTransp, QgsProperty() );
876  }
877  if ( mDataDefinedProperties.isActive( BufferTransp ) )
878  {
879  mDataDefinedProperties.setProperty( BufferOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( BufferTransp ).asExpression() ) ) );
880  mDataDefinedProperties.setProperty( BufferTransp, QgsProperty() );
881  }
882  if ( mDataDefinedProperties.isActive( ShapeTransparency ) )
883  {
884  mDataDefinedProperties.setProperty( ShapeOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( ShapeTransparency ).asExpression() ) ) );
885  mDataDefinedProperties.setProperty( ShapeTransparency, QgsProperty() );
886  }
887  if ( mDataDefinedProperties.isActive( ShadowTransparency ) )
888  {
889  mDataDefinedProperties.setProperty( ShadowOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( ShadowTransparency ).asExpression() ) ) );
890  mDataDefinedProperties.setProperty( ShadowTransparency, QgsProperty() );
891  }
892  if ( mDataDefinedProperties.isActive( Rotation ) )
893  {
894  mDataDefinedProperties.setProperty( LabelRotation, QgsProperty::fromExpression( QStringLiteral( "360 - (%1)" ).arg( mDataDefinedProperties.property( Rotation ).asExpression() ) ) );
895  mDataDefinedProperties.setProperty( Rotation, QgsProperty() );
896  }
897  // older 2.x projects had min/max scale flipped - so change them here.
898  if ( mDataDefinedProperties.isActive( MinScale ) )
899  {
900  mDataDefinedProperties.setProperty( MaximumScale, mDataDefinedProperties.property( MinScale ) );
901  mDataDefinedProperties.setProperty( MinScale, QgsProperty() );
902  }
903  if ( mDataDefinedProperties.isActive( MaxScale ) )
904  {
905  mDataDefinedProperties.setProperty( MinimumScale, mDataDefinedProperties.property( MaxScale ) );
906  mDataDefinedProperties.setProperty( MaxScale, QgsProperty() );
907  }
908 }
909 
910 void QgsPalLayerSettings::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
911 {
912  // text style
913  QDomElement textStyleElem = elem.firstChildElement( QStringLiteral( "text-style" ) );
914  fieldName = textStyleElem.attribute( QStringLiteral( "fieldName" ) );
915  isExpression = textStyleElem.attribute( QStringLiteral( "isExpression" ) ).toInt();
916 
917  mFormat.readXml( elem, context );
919  previewBkgrdColor = QColor( textStyleElem.attribute( QStringLiteral( "previewBkgrdColor" ), QStringLiteral( "#ffffff" ) ) );
921  substitutions.readXml( textStyleElem.firstChildElement( QStringLiteral( "substitutions" ) ) );
922  useSubstitutions = textStyleElem.attribute( QStringLiteral( "useSubstitutions" ) ).toInt();
923 
924  // text formatting
925  QDomElement textFormatElem = elem.firstChildElement( QStringLiteral( "text-format" ) );
926  wrapChar = textFormatElem.attribute( QStringLiteral( "wrapChar" ) );
927  autoWrapLength = textFormatElem.attribute( QStringLiteral( "autoWrapLength" ), QStringLiteral( "0" ) ).toInt();
928  useMaxLineLengthForAutoWrap = textFormatElem.attribute( QStringLiteral( "useMaxLineLengthForAutoWrap" ), QStringLiteral( "1" ) ).toInt();
929  multilineAlign = static_cast< MultiLineAlign >( textFormatElem.attribute( QStringLiteral( "multilineAlign" ), QString::number( MultiFollowPlacement ) ).toUInt() );
930  addDirectionSymbol = textFormatElem.attribute( QStringLiteral( "addDirectionSymbol" ) ).toInt();
931  leftDirectionSymbol = textFormatElem.attribute( QStringLiteral( "leftDirectionSymbol" ), QStringLiteral( "<" ) );
932  rightDirectionSymbol = textFormatElem.attribute( QStringLiteral( "rightDirectionSymbol" ), QStringLiteral( ">" ) );
933  reverseDirectionSymbol = textFormatElem.attribute( QStringLiteral( "reverseDirectionSymbol" ) ).toInt();
934  placeDirectionSymbol = static_cast< DirectionSymbols >( textFormatElem.attribute( QStringLiteral( "placeDirectionSymbol" ), QString::number( SymbolLeftRight ) ).toUInt() );
935  formatNumbers = textFormatElem.attribute( QStringLiteral( "formatNumbers" ) ).toInt();
936  decimals = textFormatElem.attribute( QStringLiteral( "decimals" ) ).toInt();
937  plusSign = textFormatElem.attribute( QStringLiteral( "plussign" ) ).toInt();
938 
939  // placement
940  QDomElement placementElem = elem.firstChildElement( QStringLiteral( "placement" ) );
941  placement = static_cast< Placement >( placementElem.attribute( QStringLiteral( "placement" ) ).toInt() );
942  placementFlags = placementElem.attribute( QStringLiteral( "placementFlags" ) ).toUInt();
943  mPolygonPlacementFlags = static_cast< QgsLabeling::PolygonPlacementFlags >( placementElem.attribute( QStringLiteral( "polygonPlacementFlags" ), QString::number( static_cast< int >( QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon ) ) ).toInt() );
944 
945  centroidWhole = placementElem.attribute( QStringLiteral( "centroidWhole" ), QStringLiteral( "0" ) ).toInt();
946  centroidInside = placementElem.attribute( QStringLiteral( "centroidInside" ), QStringLiteral( "0" ) ).toInt();
947  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( placementElem.attribute( QStringLiteral( "predefinedPositionOrder" ) ) );
948  if ( predefinedPositionOrder.isEmpty() )
949  predefinedPositionOrder = *DEFAULT_PLACEMENT_ORDER();
950  fitInPolygonOnly = placementElem.attribute( QStringLiteral( "fitInPolygonOnly" ), QStringLiteral( "0" ) ).toInt();
951  dist = placementElem.attribute( QStringLiteral( "dist" ) ).toDouble();
952  if ( !placementElem.hasAttribute( QStringLiteral( "distUnits" ) ) )
953  {
954  if ( placementElem.attribute( QStringLiteral( "distInMapUnits" ) ).toInt() )
956  else
958  }
959  else
960  {
961  distUnits = QgsUnitTypes::decodeRenderUnit( placementElem.attribute( QStringLiteral( "distUnits" ) ) );
962  }
963  if ( !placementElem.hasAttribute( QStringLiteral( "distMapUnitScale" ) ) )
964  {
965  //fallback to older property
966  double oldMin = placementElem.attribute( QStringLiteral( "distMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
967  distMapUnitScale.minScale = !qgsDoubleNear( oldMin, 0 ) ? 1.0 / oldMin : 0;
968  double oldMax = placementElem.attribute( QStringLiteral( "distMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
969  distMapUnitScale.maxScale = !qgsDoubleNear( oldMax, 0 ) ? 1.0 / oldMax : 0;
970  }
971  else
972  {
973  distMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( placementElem.attribute( QStringLiteral( "distMapUnitScale" ) ) );
974  }
975  offsetType = static_cast< OffsetType >( placementElem.attribute( QStringLiteral( "offsetType" ), QString::number( FromPoint ) ).toUInt() );
976  quadOffset = static_cast< QuadrantPosition >( placementElem.attribute( QStringLiteral( "quadOffset" ), QString::number( QuadrantOver ) ).toUInt() );
977  xOffset = placementElem.attribute( QStringLiteral( "xOffset" ), QStringLiteral( "0" ) ).toDouble();
978  yOffset = placementElem.attribute( QStringLiteral( "yOffset" ), QStringLiteral( "0" ) ).toDouble();
979  if ( !placementElem.hasAttribute( QStringLiteral( "offsetUnits" ) ) )
980  {
981  offsetUnits = placementElem.attribute( QStringLiteral( "labelOffsetInMapUnits" ), QStringLiteral( "1" ) ).toInt() ? QgsUnitTypes::RenderMapUnits : QgsUnitTypes::RenderMillimeters;
982  }
983  else
984  {
985  offsetUnits = QgsUnitTypes::decodeRenderUnit( placementElem.attribute( QStringLiteral( "offsetUnits" ) ) );
986  }
987  if ( !placementElem.hasAttribute( QStringLiteral( "labelOffsetMapUnitScale" ) ) )
988  {
989  //fallback to older property
990  double oldMin = placementElem.attribute( QStringLiteral( "labelOffsetMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
991  labelOffsetMapUnitScale.minScale = !qgsDoubleNear( oldMin, 0.0 ) ? 1.0 / oldMin : 0;
992  double oldMax = placementElem.attribute( QStringLiteral( "labelOffsetMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
993  labelOffsetMapUnitScale.maxScale = !qgsDoubleNear( oldMax, 0.0 ) ? 1.0 / oldMax : 0;
994  }
995  else
996  {
997  labelOffsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( placementElem.attribute( QStringLiteral( "labelOffsetMapUnitScale" ) ) );
998  }
999 
1000  if ( placementElem.hasAttribute( QStringLiteral( "angleOffset" ) ) )
1001  {
1002  double oldAngle = placementElem.attribute( QStringLiteral( "angleOffset" ), QStringLiteral( "0" ) ).toDouble();
1003  angleOffset = std::fmod( 360 - oldAngle, 360.0 );
1004  }
1005  else
1006  {
1007  angleOffset = placementElem.attribute( QStringLiteral( "rotationAngle" ), QStringLiteral( "0" ) ).toDouble();
1008  }
1009 
1010  preserveRotation = placementElem.attribute( QStringLiteral( "preserveRotation" ), QStringLiteral( "1" ) ).toInt();
1011  maxCurvedCharAngleIn = placementElem.attribute( QStringLiteral( "maxCurvedCharAngleIn" ), QStringLiteral( "25" ) ).toDouble();
1012  maxCurvedCharAngleOut = placementElem.attribute( QStringLiteral( "maxCurvedCharAngleOut" ), QStringLiteral( "-25" ) ).toDouble();
1013  priority = placementElem.attribute( QStringLiteral( "priority" ) ).toInt();
1014  repeatDistance = placementElem.attribute( QStringLiteral( "repeatDistance" ), QStringLiteral( "0" ) ).toDouble();
1015  if ( !placementElem.hasAttribute( QStringLiteral( "repeatDistanceUnits" ) ) )
1016  {
1017  // upgrade old setting
1018  switch ( placementElem.attribute( QStringLiteral( "repeatDistanceUnit" ), QString::number( 1 ) ).toUInt() )
1019  {
1020  case 0:
1022  break;
1023  case 1:
1025  break;
1026  case 2:
1028  break;
1029  case 3:
1031  break;
1032  }
1033  }
1034  else
1035  {
1036  repeatDistanceUnit = QgsUnitTypes::decodeRenderUnit( placementElem.attribute( QStringLiteral( "repeatDistanceUnits" ) ) );
1037  }
1038  if ( !placementElem.hasAttribute( QStringLiteral( "repeatDistanceMapUnitScale" ) ) )
1039  {
1040  //fallback to older property
1041  double oldMin = placementElem.attribute( QStringLiteral( "repeatDistanceMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
1042  repeatDistanceMapUnitScale.minScale = !qgsDoubleNear( oldMin, 0.0 ) ? 1.0 / oldMin : 0;
1043  double oldMax = placementElem.attribute( QStringLiteral( "repeatDistanceMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
1044  repeatDistanceMapUnitScale.maxScale = !qgsDoubleNear( oldMax, 0.0 ) ? 1.0 / oldMax : 0;
1045  }
1046  else
1047  {
1048  repeatDistanceMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( placementElem.attribute( QStringLiteral( "repeatDistanceMapUnitScale" ) ) );
1049  }
1050 
1051  overrunDistance = placementElem.attribute( QStringLiteral( "overrunDistance" ), QStringLiteral( "0" ) ).toDouble();
1052  overrunDistanceUnit = QgsUnitTypes::decodeRenderUnit( placementElem.attribute( QStringLiteral( "overrunDistanceUnit" ) ) );
1053  overrunDistanceMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( placementElem.attribute( QStringLiteral( "overrunDistanceMapUnitScale" ) ) );
1054 
1055  geometryGenerator = placementElem.attribute( QStringLiteral( "geometryGenerator" ) );
1056  geometryGeneratorEnabled = placementElem.attribute( QStringLiteral( "geometryGeneratorEnabled" ) ).toInt();
1057  geometryGeneratorType = qgsEnumKeyToValue( placementElem.attribute( QStringLiteral( "geometryGeneratorType" ) ), QgsWkbTypes::PointGeometry );
1058 
1059  layerType = qgsEnumKeyToValue( placementElem.attribute( QStringLiteral( "layerType" ) ), QgsWkbTypes::UnknownGeometry );
1060 
1061  // rendering
1062  QDomElement renderingElem = elem.firstChildElement( QStringLiteral( "rendering" ) );
1063 
1064  drawLabels = renderingElem.attribute( QStringLiteral( "drawLabels" ), QStringLiteral( "1" ) ).toInt();
1065 
1066  maximumScale = renderingElem.attribute( QStringLiteral( "scaleMin" ), QStringLiteral( "0" ) ).toDouble();
1067  minimumScale = renderingElem.attribute( QStringLiteral( "scaleMax" ), QStringLiteral( "0" ) ).toDouble();
1068  scaleVisibility = renderingElem.attribute( QStringLiteral( "scaleVisibility" ) ).toInt();
1069 
1070  fontLimitPixelSize = renderingElem.attribute( QStringLiteral( "fontLimitPixelSize" ), QStringLiteral( "0" ) ).toInt();
1071  fontMinPixelSize = renderingElem.attribute( QStringLiteral( "fontMinPixelSize" ), QStringLiteral( "0" ) ).toInt();
1072  fontMaxPixelSize = renderingElem.attribute( QStringLiteral( "fontMaxPixelSize" ), QStringLiteral( "10000" ) ).toInt();
1073  displayAll = renderingElem.attribute( QStringLiteral( "displayAll" ), QStringLiteral( "0" ) ).toInt();
1074  upsidedownLabels = static_cast< UpsideDownLabels >( renderingElem.attribute( QStringLiteral( "upsidedownLabels" ), QString::number( Upright ) ).toUInt() );
1075 
1076  labelPerPart = renderingElem.attribute( QStringLiteral( "labelPerPart" ) ).toInt();
1077  mergeLines = renderingElem.attribute( QStringLiteral( "mergeLines" ) ).toInt();
1078  mThinningSettings.setMinimumFeatureSize( renderingElem.attribute( QStringLiteral( "minFeatureSize" ) ).toDouble() );
1079  mThinningSettings.setLimitNumberLabelsEnabled( renderingElem.attribute( QStringLiteral( "limitNumLabels" ), QStringLiteral( "0" ) ).toInt() );
1080  mThinningSettings.setMaximumNumberLabels( renderingElem.attribute( QStringLiteral( "maxNumLabels" ), QStringLiteral( "2000" ) ).toInt() );
1081  mObstacleSettings.setIsObstacle( renderingElem.attribute( QStringLiteral( "obstacle" ), QStringLiteral( "1" ) ).toInt() );
1082  mObstacleSettings.setFactor( renderingElem.attribute( QStringLiteral( "obstacleFactor" ), QStringLiteral( "1" ) ).toDouble() );
1083  mObstacleSettings.setType( static_cast< QgsLabelObstacleSettings::ObstacleType >( renderingElem.attribute( QStringLiteral( "obstacleType" ), QString::number( PolygonInterior ) ).toUInt() ) );
1084  zIndex = renderingElem.attribute( QStringLiteral( "zIndex" ), QStringLiteral( "0.0" ) ).toDouble();
1085 
1086  QDomElement ddElem = elem.firstChildElement( QStringLiteral( "dd_properties" ) );
1087  if ( !ddElem.isNull() )
1088  {
1089  mDataDefinedProperties.readXml( ddElem, *sPropertyDefinitions() );
1090  }
1091  else
1092  {
1093  // upgrade 2.x style dd project
1094  mDataDefinedProperties.clear();
1095  QDomElement ddElem = elem.firstChildElement( QStringLiteral( "data-defined" ) );
1096  readOldDataDefinedPropertyMap( nullptr, &ddElem );
1097  }
1098  // upgrade older data defined settings
1099  if ( mDataDefinedProperties.isActive( FontTransp ) )
1100  {
1101  mDataDefinedProperties.setProperty( FontOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( FontTransp ).asExpression() ) ) );
1102  mDataDefinedProperties.setProperty( FontTransp, QgsProperty() );
1103  }
1104  if ( mDataDefinedProperties.isActive( BufferTransp ) )
1105  {
1106  mDataDefinedProperties.setProperty( BufferOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( BufferTransp ).asExpression() ) ) );
1107  mDataDefinedProperties.setProperty( BufferTransp, QgsProperty() );
1108  }
1109  if ( mDataDefinedProperties.isActive( ShapeTransparency ) )
1110  {
1111  mDataDefinedProperties.setProperty( ShapeOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( ShapeTransparency ).asExpression() ) ) );
1112  mDataDefinedProperties.setProperty( ShapeTransparency, QgsProperty() );
1113  }
1114  if ( mDataDefinedProperties.isActive( ShadowTransparency ) )
1115  {
1116  mDataDefinedProperties.setProperty( ShadowOpacity, QgsProperty::fromExpression( QStringLiteral( "100 - (%1)" ).arg( mDataDefinedProperties.property( ShadowTransparency ).asExpression() ) ) );
1117  mDataDefinedProperties.setProperty( ShadowTransparency, QgsProperty() );
1118  }
1119  if ( mDataDefinedProperties.isActive( Rotation ) )
1120  {
1121  mDataDefinedProperties.setProperty( LabelRotation, QgsProperty::fromExpression( QStringLiteral( "360 - (%1)" ).arg( mDataDefinedProperties.property( Rotation ).asExpression() ) ) );
1122  mDataDefinedProperties.setProperty( Rotation, QgsProperty() );
1123  }
1124  // older 2.x projects had min/max scale flipped - so change them here.
1125  if ( mDataDefinedProperties.isActive( MinScale ) )
1126  {
1127  mDataDefinedProperties.setProperty( MaximumScale, mDataDefinedProperties.property( MinScale ) );
1128  mDataDefinedProperties.setProperty( MinScale, QgsProperty() );
1129  }
1130  if ( mDataDefinedProperties.isActive( MaxScale ) )
1131  {
1132  mDataDefinedProperties.setProperty( MinimumScale, mDataDefinedProperties.property( MaxScale ) );
1133  mDataDefinedProperties.setProperty( MaxScale, QgsProperty() );
1134  }
1135 
1136  // TODO - replace with registry when multiple callout styles exist
1137  const QString calloutType = elem.attribute( QStringLiteral( "calloutType" ) );
1138  if ( calloutType.isEmpty() )
1139  mCallout.reset( QgsApplication::calloutRegistry()->defaultCallout() );
1140  else
1141  {
1142  mCallout.reset( QgsApplication::calloutRegistry()->createCallout( calloutType, elem.firstChildElement( QStringLiteral( "callout" ) ), context ) );
1143  if ( !mCallout )
1144  mCallout.reset( QgsApplication::calloutRegistry()->defaultCallout() );
1145  }
1146 }
1147 
1148 QDomElement QgsPalLayerSettings::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
1149 {
1150  QDomElement textStyleElem = mFormat.writeXml( doc, context );
1151 
1152  // text style
1153  textStyleElem.setAttribute( QStringLiteral( "fieldName" ), fieldName );
1154  textStyleElem.setAttribute( QStringLiteral( "isExpression" ), isExpression );
1155  QDomElement replacementElem = doc.createElement( QStringLiteral( "substitutions" ) );
1156  substitutions.writeXml( replacementElem, doc );
1157  textStyleElem.appendChild( replacementElem );
1158  textStyleElem.setAttribute( QStringLiteral( "useSubstitutions" ), useSubstitutions );
1159 
1160  // text formatting
1161  QDomElement textFormatElem = doc.createElement( QStringLiteral( "text-format" ) );
1162  textFormatElem.setAttribute( QStringLiteral( "wrapChar" ), wrapChar );
1163  textFormatElem.setAttribute( QStringLiteral( "autoWrapLength" ), autoWrapLength );
1164  textFormatElem.setAttribute( QStringLiteral( "useMaxLineLengthForAutoWrap" ), useMaxLineLengthForAutoWrap );
1165  textFormatElem.setAttribute( QStringLiteral( "multilineAlign" ), static_cast< unsigned int >( multilineAlign ) );
1166  textFormatElem.setAttribute( QStringLiteral( "addDirectionSymbol" ), addDirectionSymbol );
1167  textFormatElem.setAttribute( QStringLiteral( "leftDirectionSymbol" ), leftDirectionSymbol );
1168  textFormatElem.setAttribute( QStringLiteral( "rightDirectionSymbol" ), rightDirectionSymbol );
1169  textFormatElem.setAttribute( QStringLiteral( "reverseDirectionSymbol" ), reverseDirectionSymbol );
1170  textFormatElem.setAttribute( QStringLiteral( "placeDirectionSymbol" ), static_cast< unsigned int >( placeDirectionSymbol ) );
1171  textFormatElem.setAttribute( QStringLiteral( "formatNumbers" ), formatNumbers );
1172  textFormatElem.setAttribute( QStringLiteral( "decimals" ), decimals );
1173  textFormatElem.setAttribute( QStringLiteral( "plussign" ), plusSign );
1174 
1175  // placement
1176  QDomElement placementElem = doc.createElement( QStringLiteral( "placement" ) );
1177  placementElem.setAttribute( QStringLiteral( "placement" ), placement );
1178  placementElem.setAttribute( QStringLiteral( "polygonPlacementFlags" ), static_cast< int >( mPolygonPlacementFlags ) );
1179  placementElem.setAttribute( QStringLiteral( "placementFlags" ), static_cast< unsigned int >( placementFlags ) );
1180  placementElem.setAttribute( QStringLiteral( "centroidWhole" ), centroidWhole );
1181  placementElem.setAttribute( QStringLiteral( "centroidInside" ), centroidInside );
1182  placementElem.setAttribute( QStringLiteral( "predefinedPositionOrder" ), QgsLabelingUtils::encodePredefinedPositionOrder( predefinedPositionOrder ) );
1183  placementElem.setAttribute( QStringLiteral( "fitInPolygonOnly" ), fitInPolygonOnly );
1184  placementElem.setAttribute( QStringLiteral( "dist" ), dist );
1185  placementElem.setAttribute( QStringLiteral( "distUnits" ), QgsUnitTypes::encodeUnit( distUnits ) );
1186  placementElem.setAttribute( QStringLiteral( "distMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( distMapUnitScale ) );
1187  placementElem.setAttribute( QStringLiteral( "offsetType" ), static_cast< unsigned int >( offsetType ) );
1188  placementElem.setAttribute( QStringLiteral( "quadOffset" ), static_cast< unsigned int >( quadOffset ) );
1189  placementElem.setAttribute( QStringLiteral( "xOffset" ), xOffset );
1190  placementElem.setAttribute( QStringLiteral( "yOffset" ), yOffset );
1191  placementElem.setAttribute( QStringLiteral( "offsetUnits" ), QgsUnitTypes::encodeUnit( offsetUnits ) );
1192  placementElem.setAttribute( QStringLiteral( "labelOffsetMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( labelOffsetMapUnitScale ) );
1193  placementElem.setAttribute( QStringLiteral( "rotationAngle" ), angleOffset );
1194  placementElem.setAttribute( QStringLiteral( "preserveRotation" ), preserveRotation );
1195  placementElem.setAttribute( QStringLiteral( "maxCurvedCharAngleIn" ), maxCurvedCharAngleIn );
1196  placementElem.setAttribute( QStringLiteral( "maxCurvedCharAngleOut" ), maxCurvedCharAngleOut );
1197  placementElem.setAttribute( QStringLiteral( "priority" ), priority );
1198  placementElem.setAttribute( QStringLiteral( "repeatDistance" ), repeatDistance );
1199  placementElem.setAttribute( QStringLiteral( "repeatDistanceUnits" ), QgsUnitTypes::encodeUnit( repeatDistanceUnit ) );
1200  placementElem.setAttribute( QStringLiteral( "repeatDistanceMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( repeatDistanceMapUnitScale ) );
1201  placementElem.setAttribute( QStringLiteral( "overrunDistance" ), overrunDistance );
1202  placementElem.setAttribute( QStringLiteral( "overrunDistanceUnit" ), QgsUnitTypes::encodeUnit( overrunDistanceUnit ) );
1203  placementElem.setAttribute( QStringLiteral( "overrunDistanceMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( overrunDistanceMapUnitScale ) );
1204 
1205  placementElem.setAttribute( QStringLiteral( "geometryGenerator" ), geometryGenerator );
1206  placementElem.setAttribute( QStringLiteral( "geometryGeneratorEnabled" ), geometryGeneratorEnabled );
1207  const QMetaEnum metaEnum( QMetaEnum::fromType<QgsWkbTypes::GeometryType>() );
1208  placementElem.setAttribute( QStringLiteral( "geometryGeneratorType" ), metaEnum.valueToKey( geometryGeneratorType ) );
1209 
1210  placementElem.setAttribute( QStringLiteral( "layerType" ), metaEnum.valueToKey( layerType ) );
1211 
1212  // rendering
1213  QDomElement renderingElem = doc.createElement( QStringLiteral( "rendering" ) );
1214  renderingElem.setAttribute( QStringLiteral( "drawLabels" ), drawLabels );
1215  renderingElem.setAttribute( QStringLiteral( "scaleVisibility" ), scaleVisibility );
1216  renderingElem.setAttribute( QStringLiteral( "scaleMin" ), maximumScale );
1217  renderingElem.setAttribute( QStringLiteral( "scaleMax" ), minimumScale );
1218  renderingElem.setAttribute( QStringLiteral( "fontLimitPixelSize" ), fontLimitPixelSize );
1219  renderingElem.setAttribute( QStringLiteral( "fontMinPixelSize" ), fontMinPixelSize );
1220  renderingElem.setAttribute( QStringLiteral( "fontMaxPixelSize" ), fontMaxPixelSize );
1221  renderingElem.setAttribute( QStringLiteral( "displayAll" ), displayAll );
1222  renderingElem.setAttribute( QStringLiteral( "upsidedownLabels" ), static_cast< unsigned int >( upsidedownLabels ) );
1223 
1224  renderingElem.setAttribute( QStringLiteral( "labelPerPart" ), labelPerPart );
1225  renderingElem.setAttribute( QStringLiteral( "mergeLines" ), mergeLines );
1226  renderingElem.setAttribute( QStringLiteral( "minFeatureSize" ), mThinningSettings.minimumFeatureSize() );
1227  renderingElem.setAttribute( QStringLiteral( "limitNumLabels" ), mThinningSettings.limitNumberOfLabelsEnabled() );
1228  renderingElem.setAttribute( QStringLiteral( "maxNumLabels" ), mThinningSettings.maximumNumberLabels() );
1229  renderingElem.setAttribute( QStringLiteral( "obstacle" ), mObstacleSettings.isObstacle() );
1230  renderingElem.setAttribute( QStringLiteral( "obstacleFactor" ), mObstacleSettings.factor() );
1231  renderingElem.setAttribute( QStringLiteral( "obstacleType" ), static_cast< unsigned int >( mObstacleSettings.type() ) );
1232  renderingElem.setAttribute( QStringLiteral( "zIndex" ), zIndex );
1233 
1234  QDomElement ddElem = doc.createElement( QStringLiteral( "dd_properties" ) );
1235  mDataDefinedProperties.writeXml( ddElem, *sPropertyDefinitions() );
1236 
1237  QDomElement elem = doc.createElement( QStringLiteral( "settings" ) );
1238  elem.appendChild( textStyleElem );
1239  elem.appendChild( textFormatElem );
1240  elem.appendChild( placementElem );
1241  elem.appendChild( renderingElem );
1242  elem.appendChild( ddElem );
1243 
1244  if ( mCallout )
1245  {
1246  elem.setAttribute( QStringLiteral( "calloutType" ), mCallout->type() );
1247  mCallout->saveProperties( doc, elem, context );
1248  }
1249 
1250  return elem;
1251 }
1252 
1254 {
1255  mCallout.reset( callout );
1256 }
1257 
1258 QPixmap QgsPalLayerSettings::labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText, int padding )
1259 {
1260  // for now, just use format
1261  QgsTextFormat tempFormat = settings.format();
1262  QPixmap pixmap( size );
1263  pixmap.fill( Qt::transparent );
1264  QPainter painter;
1265  painter.begin( &pixmap );
1266 
1267  painter.setRenderHint( QPainter::Antialiasing );
1268 
1269  QRect rect( 0, 0, size.width(), size.height() );
1270 
1271  // shameless eye candy - use a subtle gradient when drawing background
1272  painter.setPen( Qt::NoPen );
1273  QColor background1 = tempFormat.previewBackgroundColor();
1274  if ( ( background1.lightnessF() < 0.7 ) )
1275  {
1276  background1 = background1.darker( 125 );
1277  }
1278  else
1279  {
1280  background1 = background1.lighter( 125 );
1281  }
1282  QColor background2 = tempFormat.previewBackgroundColor();
1283  QLinearGradient linearGrad( QPointF( 0, 0 ), QPointF( 0, rect.height() ) );
1284  linearGrad.setColorAt( 0, background1 );
1285  linearGrad.setColorAt( 1, background2 );
1286  painter.setBrush( QBrush( linearGrad ) );
1287  if ( size.width() > 30 )
1288  {
1289  painter.drawRoundedRect( rect, 6, 6 );
1290  }
1291  else
1292  {
1293  // don't use rounded rect for small previews
1294  painter.drawRect( rect );
1295  }
1296  painter.setBrush( Qt::NoBrush );
1297  painter.setPen( Qt::NoPen );
1298  padding += 1; // move text away from background border
1299 
1300  QgsRenderContext context;
1301  QgsMapToPixel newCoordXForm;
1302  newCoordXForm.setParameters( 1, 0, 0, 0, 0, 0 );
1303  context.setMapToPixel( newCoordXForm );
1304 
1305  context.setScaleFactor( QgsApplication::desktop()->logicalDpiX() / 25.4 );
1306  context.setUseAdvancedEffects( true );
1307  context.setPainter( &painter );
1308 
1309  // slightly inset text to account for buffer/background
1310  double xtrans = 0;
1311  if ( tempFormat.buffer().enabled() )
1312  xtrans = context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() );
1313  if ( tempFormat.background().enabled() && tempFormat.background().sizeType() != QgsTextBackgroundSettings::SizeFixed )
1314  xtrans = std::max( xtrans, context.convertToPainterUnits( tempFormat.background().size().width(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
1315 
1316  double ytrans = 0.0;
1317  if ( tempFormat.buffer().enabled() )
1318  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.buffer().size(), tempFormat.buffer().sizeUnit(), tempFormat.buffer().sizeMapUnitScale() ) );
1319  if ( tempFormat.background().enabled() )
1320  ytrans = std::max( ytrans, context.convertToPainterUnits( tempFormat.background().size().height(), tempFormat.background().sizeUnit(), tempFormat.background().sizeMapUnitScale() ) );
1321 
1322  const QStringList text = QStringList() << ( previewText.isEmpty() ? QObject::tr( "Aa" ) : previewText );
1323  const double textHeight = QgsTextRenderer::textHeight( context, tempFormat, text, QgsTextRenderer::Rect );
1324  QRectF textRect = rect;
1325  textRect.setLeft( xtrans + padding );
1326  textRect.setWidth( rect.width() - xtrans - 2 * padding );
1327 
1328  if ( textRect.width() > 2000 )
1329  textRect.setWidth( 2000 - 2 * padding );
1330 
1331  const double bottom = textRect.height() / 2 + textHeight / 2;
1332  textRect.setTop( bottom - textHeight );
1333  textRect.setBottom( bottom );
1334 
1335  const double iconWidth = QFontMetricsF( QFont() ).width( 'X' ) * Qgis::UI_SCALE_FACTOR;
1336 
1337  if ( settings.callout() && settings.callout()->enabled() )
1338  {
1339  // draw callout preview
1340  const double textWidth = QgsTextRenderer::textWidth( context, tempFormat, text );
1341  QgsCallout *callout = settings.callout();
1342  callout->startRender( context );
1343  QgsCallout::QgsCalloutContext calloutContext;
1344  QRectF labelRect( textRect.left() + ( textRect.width() - textWidth ) / 2.0, textRect.top(), textWidth, textRect.height() );
1345  callout->render( context, labelRect, 0, QgsGeometry::fromPointXY( QgsPointXY( labelRect.left() - iconWidth * 1.5, labelRect.bottom() + iconWidth ) ), calloutContext );
1346  callout->stopRender( context );
1347  }
1348 
1349  QgsTextRenderer::drawText( textRect, 0, QgsTextRenderer::AlignCenter, text, context, tempFormat );
1350 
1351  if ( size.width() > 30 )
1352  {
1353  // draw a label icon
1354 
1355  QgsApplication::getThemeIcon( QStringLiteral( "labelingSingle.svg" ) ).paint( &painter, QRect(
1356  rect.width() - iconWidth * 3, rect.height() - iconWidth * 3,
1357  iconWidth * 2, iconWidth * 2 ), Qt::AlignRight | Qt::AlignBottom );
1358  }
1359 
1360  // draw border on top of text
1361  painter.setBrush( Qt::NoBrush );
1362  painter.setPen( QPen( tempFormat.previewBackgroundColor().darker( 150 ), 0 ) );
1363  if ( size.width() > 30 )
1364  {
1365  painter.drawRoundedRect( rect, 6, 6 );
1366  }
1367  else
1368  {
1369  // don't use rounded rect for small previews
1370  painter.drawRect( rect );
1371  }
1372 
1373  painter.end();
1374  return pixmap;
1375 }
1376 
1377 bool QgsPalLayerSettings::checkMinimumSizeMM( const QgsRenderContext &ct, const QgsGeometry &geom, double minSize ) const
1378 {
1379  return QgsPalLabeling::checkMinimumSizeMM( ct, geom, minSize );
1380 }
1381 
1382 void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF *fm, const QString &text, double &labelX, double &labelY, const QgsFeature *f, QgsRenderContext *context, double *rotatedLabelX, double *rotatedLabelY, QgsTextDocument *document )
1383 {
1384  if ( !fm || !f )
1385  {
1386  return;
1387  }
1388 
1389  QString textCopy( text );
1390 
1391  //try to keep < 2.12 API - handle no passed render context
1392  std::unique_ptr< QgsRenderContext > scopedRc;
1393  if ( !context )
1394  {
1395  scopedRc.reset( new QgsRenderContext() );
1396  if ( f )
1397  scopedRc->expressionContext().setFeature( *f );
1398  }
1399  QgsRenderContext *rc = context ? context : scopedRc.get();
1400 
1401  QString wrapchr = wrapChar;
1402  int evalAutoWrapLength = autoWrapLength;
1403  double multilineH = mFormat.lineHeight();
1404  QgsTextFormat::TextOrientation orientation = mFormat.orientation();
1405 
1406  bool addDirSymb = addDirectionSymbol;
1407  QString leftDirSymb = leftDirectionSymbol;
1408  QString rightDirSymb = rightDirectionSymbol;
1410 
1411  if ( f == mCurFeat ) // called internally, use any stored data defined values
1412  {
1413  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
1414  {
1415  wrapchr = dataDefinedValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
1416  }
1417 
1418  if ( dataDefinedValues.contains( QgsPalLayerSettings::AutoWrapLength ) )
1419  {
1420  evalAutoWrapLength = dataDefinedValues.value( QgsPalLayerSettings::AutoWrapLength, evalAutoWrapLength ).toInt();
1421  }
1422 
1423  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
1424  {
1425  multilineH = dataDefinedValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
1426  }
1427 
1428  if ( dataDefinedValues.contains( QgsPalLayerSettings::TextOrientation ) )
1429  {
1430  orientation = QgsTextRendererUtils::decodeTextOrientation( dataDefinedValues.value( QgsPalLayerSettings::TextOrientation ).toString() );
1431  }
1432 
1433  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
1434  {
1435  addDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
1436  }
1437 
1438  if ( addDirSymb )
1439  {
1440 
1441  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
1442  {
1443  leftDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
1444  }
1445  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbRight ) )
1446  {
1447  rightDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
1448  }
1449 
1450  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
1451  {
1452  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( dataDefinedValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt() );
1453  }
1454 
1455  }
1456 
1457  }
1458  else // called externally with passed-in feature, evaluate data defined
1459  {
1460  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::MultiLineWrapChar ) )
1461  {
1463  wrapchr = mDataDefinedProperties.value( QgsPalLayerSettings::MultiLineWrapChar, rc->expressionContext(), wrapchr ).toString();
1464  }
1465 
1466  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::AutoWrapLength ) )
1467  {
1468  rc->expressionContext().setOriginalValueVariable( evalAutoWrapLength );
1469  evalAutoWrapLength = mDataDefinedProperties.value( QgsPalLayerSettings::AutoWrapLength, rc->expressionContext(), evalAutoWrapLength ).toInt();
1470  }
1471 
1472  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::MultiLineHeight ) )
1473  {
1474  rc->expressionContext().setOriginalValueVariable( multilineH );
1475  multilineH = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::MultiLineHeight, rc->expressionContext(), multilineH );
1476  }
1477 
1478  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::TextOrientation ) )
1479  {
1480  QString encoded = QgsTextRendererUtils::encodeTextOrientation( orientation );
1483  }
1484 
1485  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::DirSymbDraw ) )
1486  {
1487  rc->expressionContext().setOriginalValueVariable( addDirSymb );
1488  addDirSymb = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::DirSymbDraw, rc->expressionContext(), addDirSymb );
1489  }
1490 
1491  if ( addDirSymb ) // don't do extra evaluations if not adding a direction symbol
1492  {
1493  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::DirSymbLeft ) )
1494  {
1495  rc->expressionContext().setOriginalValueVariable( leftDirSymb );
1496  leftDirSymb = mDataDefinedProperties.value( QgsPalLayerSettings::DirSymbLeft, rc->expressionContext(), leftDirSymb ).toString();
1497  }
1498 
1499  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::DirSymbRight ) )
1500  {
1501  rc->expressionContext().setOriginalValueVariable( rightDirSymb );
1502  rightDirSymb = mDataDefinedProperties.value( QgsPalLayerSettings::DirSymbRight, rc->expressionContext(), rightDirSymb ).toString();
1503  }
1504 
1505  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::DirSymbPlacement ) )
1506  {
1507  rc->expressionContext().setOriginalValueVariable( static_cast< int >( placeDirSymb ) );
1508  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( mDataDefinedProperties.valueAsInt( QgsPalLayerSettings::DirSymbPlacement, rc->expressionContext(), placeDirSymb ) );
1509  }
1510  }
1511  }
1512 
1513  if ( wrapchr.isEmpty() )
1514  {
1515  wrapchr = QStringLiteral( "\n" ); // default to new line delimiter
1516  }
1517 
1518  //consider the space needed for the direction symbol
1519  if ( addDirSymb && placement == QgsPalLayerSettings::Line
1520  && ( !leftDirSymb.isEmpty() || !rightDirSymb.isEmpty() ) )
1521  {
1522  QString dirSym = leftDirSymb;
1523 
1524  if ( fm->width( rightDirSymb ) > fm->width( dirSym ) )
1525  dirSym = rightDirSymb;
1526 
1527  if ( placeDirSymb == QgsPalLayerSettings::SymbolLeftRight )
1528  {
1529  textCopy.append( dirSym );
1530  }
1531  else
1532  {
1533  textCopy.prepend( dirSym + QStringLiteral( "\n" ) ); // SymbolAbove or SymbolBelow
1534  }
1535  }
1536 
1537  double w = 0.0, h = 0.0, rw = 0.0, rh = 0.0;
1538  double labelHeight = fm->ascent() + fm->descent(); // ignore +1 for baseline
1539 
1540  QStringList multiLineSplit;
1541 
1542  if ( document )
1543  {
1544  document->splitLines( wrapchr, evalAutoWrapLength, useMaxLineLengthForAutoWrap );
1545  multiLineSplit = document->toPlainText();
1546  }
1547  else
1548  {
1549  multiLineSplit = QgsPalLabeling::splitToLines( textCopy, wrapchr, evalAutoWrapLength, useMaxLineLengthForAutoWrap );
1550  }
1551 
1552  int lines = multiLineSplit.size();
1553 
1554  switch ( orientation )
1555  {
1557  {
1558  h += fm->height() + static_cast< double >( ( lines - 1 ) * labelHeight * multilineH );
1559 
1560  for ( const auto &line : multiLineSplit )
1561  {
1562  w = std::max( w, fm->width( line ) );
1563  }
1564  break;
1565  }
1566 
1568  {
1569  double letterSpacing = mFormat.scaledFont( *context ).letterSpacing();
1570  double labelWidth = fm->maxWidth();
1571  w = labelWidth + ( lines - 1 ) * labelWidth * multilineH;
1572 
1573  int maxLineLength = 0;
1574  for ( const auto &line : multiLineSplit )
1575  {
1576  maxLineLength = std::max( maxLineLength, line.length() );
1577  }
1578  h = fm->ascent() * maxLineLength + ( maxLineLength - 1 ) * letterSpacing;
1579  break;
1580  }
1581 
1583  {
1584  double widthHorizontal = 0.0;
1585  for ( const auto &line : multiLineSplit )
1586  {
1587  widthHorizontal = std::max( w, fm->width( line ) );
1588  }
1589 
1590  double widthVertical = 0.0;
1591  double letterSpacing = mFormat.scaledFont( *context ).letterSpacing();
1592  double labelWidth = fm->maxWidth();
1593  widthVertical = labelWidth + ( lines - 1 ) * labelWidth * multilineH;
1594 
1595  double heightHorizontal = 0.0;
1596  heightHorizontal += fm->height() + static_cast< double >( ( lines - 1 ) * labelHeight * multilineH );
1597 
1598  double heightVertical = 0.0;
1599  int maxLineLength = 0;
1600  for ( const auto &line : multiLineSplit )
1601  {
1602  maxLineLength = std::max( maxLineLength, line.length() );
1603  }
1604  heightVertical = fm->ascent() * maxLineLength + ( maxLineLength - 1 ) * letterSpacing;
1605 
1606  w = widthHorizontal;
1607  rw = heightVertical;
1608  h = heightHorizontal;
1609  rh = widthVertical;
1610  break;
1611  }
1612  }
1613 
1614 #if 0 // XXX strk
1615  QgsPointXY ptSize = xform->toMapCoordinatesF( w, h );
1616  labelX = std::fabs( ptSize.x() - ptZero.x() );
1617  labelY = std::fabs( ptSize.y() - ptZero.y() );
1618 #else
1619  double uPP = xform->mapUnitsPerPixel();
1620  labelX = w * uPP;
1621  labelY = h * uPP;
1622  if ( rotatedLabelX && rotatedLabelY )
1623  {
1624  *rotatedLabelX = rw * uPP;
1625  *rotatedLabelY = rh * uPP;
1626  }
1627 #endif
1628 }
1629 
1630 void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext &context, QgsLabelFeature **labelFeature, QgsGeometry obstacleGeometry, const QgsSymbol *symbol )
1631 {
1632  // either used in QgsPalLabeling (palLayer is set) or in QgsLabelingEngine (labelFeature is set)
1633  Q_ASSERT( labelFeature );
1634 
1635  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
1636  mCurFeat = &f;
1637 
1638  // data defined is obstacle? calculate this first, to avoid wasting time working with obstacles we don't require
1639  bool isObstacle = mObstacleSettings.isObstacle();
1640  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::IsObstacle ) )
1641  isObstacle = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::IsObstacle, context.expressionContext(), isObstacle ); // default to layer default
1642 
1643  if ( !drawLabels )
1644  {
1645  if ( isObstacle )
1646  {
1647  registerObstacleFeature( f, context, labelFeature, obstacleGeometry );
1648  }
1649  return;
1650  }
1651 
1652  QgsFeature feature = f;
1654  {
1655  const QgsGeometry geometry = mGeometryGeneratorExpression.evaluate( &context.expressionContext() ).value<QgsGeometry>();
1656  if ( mGeometryGeneratorExpression.hasEvalError() )
1657  QgsMessageLog::logMessage( mGeometryGeneratorExpression.evalErrorString(), QObject::tr( "Labeling" ) );
1658 
1659  if ( obstacleGeometry.isNull() )
1660  {
1661  // if an explicit obstacle geometry hasn't been set, we must always use the original feature geometry
1662  // as the obstacle -- because we want to use the geometry which was used to render the symbology
1663  // for the feature as the obstacle for other layers' labels, NOT the generated geometry which is used
1664  // only to place labels for this layer.
1665  obstacleGeometry = f.geometry();
1666  }
1667 
1668  feature.setGeometry( geometry );
1669  }
1670 
1671  // store data defined-derived values for later adding to label feature for use during rendering
1672  dataDefinedValues.clear();
1673 
1674  // data defined show label? defaults to show label if not set
1675  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Show ) )
1676  {
1677  context.expressionContext().setOriginalValueVariable( true );
1678  if ( !mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Show, context.expressionContext(), true ) )
1679  {
1680  return;
1681  }
1682  }
1683 
1684  // data defined scale visibility?
1685  bool useScaleVisibility = scaleVisibility;
1686  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::ScaleVisibility ) )
1687  useScaleVisibility = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::ScaleVisibility, context.expressionContext(), scaleVisibility );
1688 
1689  if ( useScaleVisibility )
1690  {
1691  // data defined min scale?
1692  double maxScale = maximumScale;
1693  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::MaximumScale ) )
1694  {
1696  maxScale = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::MaximumScale, context.expressionContext(), maxScale );
1697  }
1698 
1699  // scales closer than 1:1
1700  if ( maxScale < 0 )
1701  {
1702  maxScale = 1 / std::fabs( maxScale );
1703  }
1704 
1705  if ( !qgsDoubleNear( maxScale, 0.0 ) && context.rendererScale() < maxScale )
1706  {
1707  return;
1708  }
1709 
1710  // data defined min scale?
1711  double minScale = minimumScale;
1712  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::MinimumScale ) )
1713  {
1715  minScale = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::MinimumScale, context.expressionContext(), minScale );
1716  }
1717 
1718  // scales closer than 1:1
1719  if ( minScale < 0 )
1720  {
1721  minScale = 1 / std::fabs( minScale );
1722  }
1723 
1724  if ( !qgsDoubleNear( minScale, 0.0 ) && context.rendererScale() > minScale )
1725  {
1726  return;
1727  }
1728  }
1729 
1730  QFont labelFont = mFormat.font();
1731  // labelFont will be added to label feature for use during label painting
1732 
1733  // data defined font units?
1734  QgsUnitTypes::RenderUnit fontunits = mFormat.sizeUnit();
1735  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::FontSizeUnit, context.expressionContext() );
1736  if ( exprVal.isValid() )
1737  {
1738  QString units = exprVal.toString();
1739  if ( !units.isEmpty() )
1740  {
1741  bool ok;
1743  if ( ok )
1744  fontunits = res;
1745  }
1746  }
1747 
1748  //data defined label size?
1749  double fontSize = mFormat.size();
1750  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Size ) )
1751  {
1752  context.expressionContext().setOriginalValueVariable( fontSize );
1753  fontSize = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::Size, context.expressionContext(), fontSize );
1754  }
1755  if ( fontSize <= 0.0 )
1756  {
1757  return;
1758  }
1759 
1760  int fontPixelSize = QgsTextRenderer::sizeToPixel( fontSize, context, fontunits, mFormat.sizeMapUnitScale() );
1761  // don't try to show font sizes less than 1 pixel (Qt complains)
1762  if ( fontPixelSize < 1 )
1763  {
1764  return;
1765  }
1766  labelFont.setPixelSize( fontPixelSize );
1767 
1768  // NOTE: labelFont now always has pixelSize set, so pointSize or pointSizeF might return -1
1769 
1770  // defined 'minimum/maximum pixel font size'?
1771  if ( fontunits == QgsUnitTypes::RenderMapUnits )
1772  {
1773  if ( mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::FontLimitPixel, context.expressionContext(), fontLimitPixelSize ) )
1774  {
1775  int fontMinPixel = mDataDefinedProperties.valueAsInt( QgsPalLayerSettings::FontMinPixel, context.expressionContext(), fontMinPixelSize );
1776  int fontMaxPixel = mDataDefinedProperties.valueAsInt( QgsPalLayerSettings::FontMaxPixel, context.expressionContext(), fontMaxPixelSize );
1777 
1778  if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
1779  {
1780  return;
1781  }
1782  }
1783  }
1784 
1785  // NOTE: the following parsing functions calculate and store any data defined values for later use in QgsPalLabeling::drawLabeling
1786  // this is done to provide clarity, and because such parsing is not directly related to PAL feature registration calculations
1787 
1788  // calculate rest of font attributes and store any data defined values
1789  // this is done here for later use in making label backgrounds part of collision management (when implemented)
1790  labelFont.setCapitalization( QFont::MixedCase ); // reset this - we don't use QFont's handling as it breaks with curved labels
1791 
1792  parseTextStyle( labelFont, fontunits, context );
1793  if ( mDataDefinedProperties.hasActiveProperties() )
1794  {
1795  parseTextFormatting( context );
1796  parseTextBuffer( context );
1797  parseTextMask( context );
1798  parseShapeBackground( context );
1799  parseDropShadow( context );
1800  }
1801 
1802  QString labelText;
1803 
1804  // Check to see if we are a expression string.
1805  if ( isExpression )
1806  {
1808  if ( exp->hasParserError() )
1809  {
1810  QgsDebugMsgLevel( QStringLiteral( "Expression parser error:%1" ).arg( exp->parserErrorString() ), 4 );
1811  return;
1812  }
1813 
1814  QVariant result = exp->evaluate( &context.expressionContext() ); // expression prepared in QgsPalLabeling::prepareLayer()
1815  if ( exp->hasEvalError() )
1816  {
1817  QgsDebugMsgLevel( QStringLiteral( "Expression parser eval error:%1" ).arg( exp->evalErrorString() ), 4 );
1818  return;
1819  }
1820  labelText = result.isNull() ? QString() : result.toString();
1821  }
1822  else
1823  {
1824  const QVariant &v = feature.attribute( fieldIndex );
1825  labelText = v.isNull() ? QString() : v.toString();
1826  }
1827 
1828  // apply text replacements
1829  if ( useSubstitutions )
1830  {
1831  labelText = substitutions.process( labelText );
1832  }
1833 
1834  // apply capitalization
1836  // maintain API - capitalization may have been set in textFont
1837  if ( mFormat.font().capitalization() != QFont::MixedCase )
1838  {
1839  capitalization = static_cast< QgsStringUtils::Capitalization >( mFormat.font().capitalization() );
1840  }
1841  // data defined font capitalization?
1842  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::FontCase ) )
1843  {
1844  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::FontCase, context.expressionContext() );
1845  if ( exprVal.isValid() )
1846  {
1847  QString fcase = exprVal.toString().trimmed();
1848  QgsDebugMsgLevel( QStringLiteral( "exprVal FontCase:%1" ).arg( fcase ), 4 );
1849 
1850  if ( !fcase.isEmpty() )
1851  {
1852  if ( fcase.compare( QLatin1String( "NoChange" ), Qt::CaseInsensitive ) == 0 )
1853  {
1854  capitalization = QgsStringUtils::MixedCase;
1855  }
1856  else if ( fcase.compare( QLatin1String( "Upper" ), Qt::CaseInsensitive ) == 0 )
1857  {
1858  capitalization = QgsStringUtils::AllUppercase;
1859  }
1860  else if ( fcase.compare( QLatin1String( "Lower" ), Qt::CaseInsensitive ) == 0 )
1861  {
1862  capitalization = QgsStringUtils::AllLowercase;
1863  }
1864  else if ( fcase.compare( QLatin1String( "Capitalize" ), Qt::CaseInsensitive ) == 0 )
1865  {
1867  }
1868  }
1869  }
1870  }
1871  labelText = QgsStringUtils::capitalize( labelText, capitalization );
1872 
1873  // format number if label text is coercible to a number
1874  bool evalFormatNumbers = formatNumbers;
1875  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::NumFormat ) )
1876  {
1877  evalFormatNumbers = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::NumFormat, context.expressionContext(), evalFormatNumbers );
1878  }
1879  if ( evalFormatNumbers )
1880  {
1881  // data defined decimal places?
1882  int decimalPlaces = mDataDefinedProperties.valueAsInt( QgsPalLayerSettings::NumDecimals, context.expressionContext(), decimals );
1883  if ( decimalPlaces <= 0 ) // needs to be positive
1884  decimalPlaces = decimals;
1885 
1886  // data defined plus sign?
1887  bool signPlus = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::NumPlusSign, context.expressionContext(), plusSign );
1888 
1889  QVariant textV( labelText );
1890  bool ok;
1891  double d = textV.toDouble( &ok );
1892  if ( ok )
1893  {
1894  QString numberFormat;
1895  if ( d > 0 && signPlus )
1896  {
1897  numberFormat.append( '+' );
1898  }
1899  numberFormat.append( "%1" );
1900  labelText = numberFormat.arg( d, 0, 'f', decimalPlaces );
1901  }
1902  }
1903 
1904  // NOTE: this should come AFTER any option that affects font metrics
1905  std::unique_ptr<QFontMetricsF> labelFontMetrics( new QFontMetricsF( labelFont ) );
1906  double labelX, labelY, rotatedLabelX, rotatedLabelY; // will receive label size
1907 
1908  QgsTextDocument doc;
1909  if ( format().allowHtmlFormatting() )
1910  doc = QgsTextDocument::fromHtml( QStringList() << labelText );
1911 
1912  // also applies the line split to doc!
1913  calculateLabelSize( labelFontMetrics.get(), labelText, labelX, labelY, mCurFeat, &context, &rotatedLabelX, &rotatedLabelY, format().allowHtmlFormatting() ? &doc : nullptr );
1914 
1915  // maximum angle between curved label characters (hardcoded defaults used in QGIS <2.0)
1916  //
1917  double maxcharanglein = 20.0; // range 20.0-60.0
1918  double maxcharangleout = -20.0; // range 20.0-95.0
1919 
1921  {
1922  maxcharanglein = maxCurvedCharAngleIn;
1923  maxcharangleout = maxCurvedCharAngleOut;
1924 
1925  //data defined maximum angle between curved label characters?
1926  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::CurvedCharAngleInOut ) )
1927  {
1928  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::CurvedCharAngleInOut, context.expressionContext() );
1929  bool ok = false;
1930  const QPointF maxcharanglePt = QgsSymbolLayerUtils::toPoint( exprVal, &ok );
1931  if ( ok )
1932  {
1933  maxcharanglein = qBound( 20.0, static_cast< double >( maxcharanglePt.x() ), 60.0 );
1934  maxcharangleout = qBound( 20.0, static_cast< double >( maxcharanglePt.y() ), 95.0 );
1935  }
1936  }
1937  // make sure maxcharangleout is always negative
1938  maxcharangleout = -( std::fabs( maxcharangleout ) );
1939  }
1940 
1941  // data defined centroid whole or clipped?
1942  bool wholeCentroid = centroidWhole;
1943  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::CentroidWhole ) )
1944  {
1945  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::CentroidWhole, context.expressionContext() );
1946  if ( exprVal.isValid() )
1947  {
1948  QString str = exprVal.toString().trimmed();
1949  QgsDebugMsgLevel( QStringLiteral( "exprVal CentroidWhole:%1" ).arg( str ), 4 );
1950 
1951  if ( !str.isEmpty() )
1952  {
1953  if ( str.compare( QLatin1String( "Visible" ), Qt::CaseInsensitive ) == 0 )
1954  {
1955  wholeCentroid = false;
1956  }
1957  else if ( str.compare( QLatin1String( "Whole" ), Qt::CaseInsensitive ) == 0 )
1958  {
1959  wholeCentroid = true;
1960  }
1961  }
1962  }
1963  }
1964 
1965  QgsGeometry geom = feature.geometry();
1966  if ( geom.isNull() )
1967  {
1968  return;
1969  }
1970 
1971  // simplify?
1972  const QgsVectorSimplifyMethod &simplifyMethod = context.vectorSimplifyMethod();
1973  std::unique_ptr<QgsGeometry> scopedClonedGeom;
1974  if ( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && simplifyMethod.forceLocalOptimization() )
1975  {
1976  unsigned int simplifyHints = simplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
1978  QgsGeometry g = geom;
1979  QgsMapToPixelSimplifier simplifier( simplifyHints, simplifyMethod.tolerance(), simplifyAlgorithm );
1980  geom = simplifier.simplify( geom );
1981  }
1982 
1983  // whether we're going to create a centroid for polygon
1984  bool centroidPoly = ( ( placement == QgsPalLayerSettings::AroundPoint
1986  && geom.type() == QgsWkbTypes::PolygonGeometry );
1987 
1988  // CLIP the geometry if it is bigger than the extent
1989  // don't clip if centroid is requested for whole feature
1990  bool doClip = false;
1991  if ( !centroidPoly || !wholeCentroid )
1992  {
1993  doClip = true;
1994  }
1995 
1996 
1997  QgsLabeling::PolygonPlacementFlags polygonPlacement = mPolygonPlacementFlags;
1998  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::PolygonLabelOutside ) )
1999  {
2000  const QVariant dataDefinedOutside = mDataDefinedProperties.value( QgsPalLayerSettings::PolygonLabelOutside, context.expressionContext() );
2001  if ( dataDefinedOutside.isValid() )
2002  {
2003  if ( dataDefinedOutside.type() == QVariant::String )
2004  {
2005  const QString value = dataDefinedOutside.toString().trimmed();
2006  if ( value.compare( QLatin1String( "force" ), Qt::CaseInsensitive ) == 0 )
2007  {
2008  // forced outside placement -- remove inside flag, add outside flag
2009  polygonPlacement &= ~static_cast< int >( QgsLabeling::PolygonPlacementFlag::AllowPlacementInsideOfPolygon );
2010  polygonPlacement |= QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2011  }
2012  else if ( value.compare( QLatin1String( "yes" ), Qt::CaseInsensitive ) == 0 )
2013  {
2014  // permit outside placement
2015  polygonPlacement |= QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2016  }
2017  else if ( value.compare( QLatin1String( "no" ), Qt::CaseInsensitive ) == 0 )
2018  {
2019  // block outside placement
2020  polygonPlacement &= ~static_cast< int >( QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon );
2021  }
2022  }
2023  else
2024  {
2025  if ( dataDefinedOutside.toBool() )
2026  {
2027  // permit outside placement
2028  polygonPlacement |= QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon;
2029  }
2030  else
2031  {
2032  // block outside placement
2033  polygonPlacement &= ~static_cast< int >( QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon );
2034  }
2035  }
2036  }
2037  }
2038 
2039  // if using fitInPolygonOnly option, generate the permissible zone (must happen before geometry is modified - e.g.,
2040  // as a result of using perimeter based labeling and the geometry is converted to a boundary)
2041  // note that we also force this if we are permitting labels to be placed outside of polygons too!
2042  QgsGeometry permissibleZone;
2043  if ( geom.type() == QgsWkbTypes::PolygonGeometry && ( fitInPolygonOnly || polygonPlacement & QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon ) )
2044  {
2045  permissibleZone = geom;
2046  if ( QgsPalLabeling::geometryRequiresPreparation( permissibleZone, context, ct, doClip ? extentGeom : QgsGeometry(), mergeLines ) )
2047  {
2048  permissibleZone = QgsPalLabeling::prepareGeometry( permissibleZone, context, ct, doClip ? extentGeom : QgsGeometry(), mergeLines );
2049  }
2050  }
2051 
2052  // if using perimeter based labeling for polygons, get the polygon's
2053  // linear boundary and use that for the label geometry
2054  if ( ( geom.type() == QgsWkbTypes::PolygonGeometry )
2055  && ( placement == Line || placement == PerimeterCurved ) )
2056  {
2057  geom = QgsGeometry( geom.constGet()->boundary() );
2058  }
2059 
2060  geos::unique_ptr geos_geom_clone;
2061  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, doClip ? extentGeom : QgsGeometry(), mergeLines ) )
2062  {
2063  geom = QgsPalLabeling::prepareGeometry( geom, context, ct, doClip ? extentGeom : QgsGeometry(), mergeLines );
2064 
2065  if ( geom.isEmpty() )
2066  return;
2067  }
2068  geos_geom_clone = QgsGeos::asGeos( geom );
2069 
2070  if ( isObstacle || ( geom.type() == QgsWkbTypes::PointGeometry && offsetType == FromSymbolBounds ) )
2071  {
2072  if ( !obstacleGeometry.isNull() && QgsPalLabeling::geometryRequiresPreparation( obstacleGeometry, context, ct, doClip ? extentGeom : QgsGeometry(), mergeLines ) )
2073  {
2074  obstacleGeometry = QgsGeometry( QgsPalLabeling::prepareGeometry( obstacleGeometry, context, ct, doClip ? extentGeom : QgsGeometry(), mergeLines ) );
2075  }
2076  }
2077 
2078  QgsLabelThinningSettings featureThinningSettings = mThinningSettings;
2079  featureThinningSettings.updateDataDefinedProperties( mDataDefinedProperties, context.expressionContext() );
2080 
2081  if ( featureThinningSettings.minimumFeatureSize() > 0 && !checkMinimumSizeMM( context, geom, featureThinningSettings.minimumFeatureSize() ) )
2082  return;
2083 
2084  if ( !geos_geom_clone )
2085  return; // invalid geometry
2086 
2087  // likelihood exists label will be registered with PAL and may be drawn
2088  // check if max number of features to label (already registered with PAL) has been reached
2089  // Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
2090  if ( featureThinningSettings.limitNumberOfLabelsEnabled() )
2091  {
2092  if ( !featureThinningSettings.maximumNumberLabels() )
2093  {
2094  return;
2095  }
2096  if ( mFeatsRegPal >= featureThinningSettings.maximumNumberLabels() )
2097  {
2098  return;
2099  }
2100 
2101  int divNum = static_cast< int >( ( static_cast< double >( mFeaturesToLabel ) / featureThinningSettings.maximumNumberLabels() ) + 0.5 ); // NOLINT
2102  if ( divNum && ( mFeatsRegPal == static_cast< int >( mFeatsSendingToPal / divNum ) ) )
2103  {
2104  mFeatsSendingToPal += 1;
2105  if ( divNum && mFeatsSendingToPal % divNum )
2106  {
2107  return;
2108  }
2109  }
2110  }
2111 
2112  //data defined position / alignment / rotation?
2113  bool dataDefinedPosition = false;
2114  bool layerDefinedRotation = false;
2115  bool dataDefinedRotation = false;
2116  double xPos = 0.0, yPos = 0.0, angle = 0.0;
2117  bool ddXPos = false, ddYPos = false;
2118  double quadOffsetX = 0.0, quadOffsetY = 0.0;
2119  double offsetX = 0.0, offsetY = 0.0;
2120  QgsPointXY anchorPosition;
2121 
2123  {
2124  anchorPosition = geom.centroid().asPoint();
2125  }
2126  //x/y shift in case of alignment
2127  double xdiff = 0.0;
2128  double ydiff = 0.0;
2129 
2130  //data defined quadrant offset?
2131  bool ddFixedQuad = false;
2132  QuadrantPosition quadOff = quadOffset;
2133  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::OffsetQuad ) )
2134  {
2135  context.expressionContext().setOriginalValueVariable( static_cast< int >( quadOff ) );
2136  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::OffsetQuad, context.expressionContext() );
2137  if ( exprVal.isValid() )
2138  {
2139  bool ok;
2140  int quadInt = exprVal.toInt( &ok );
2141  if ( ok && 0 <= quadInt && quadInt <= 8 )
2142  {
2143  quadOff = static_cast< QuadrantPosition >( quadInt );
2144  ddFixedQuad = true;
2145  }
2146  }
2147  }
2148 
2149  // adjust quadrant offset of labels
2150  switch ( quadOff )
2151  {
2152  case QuadrantAboveLeft:
2153  quadOffsetX = -1.0;
2154  quadOffsetY = 1.0;
2155  break;
2156  case QuadrantAbove:
2157  quadOffsetX = 0.0;
2158  quadOffsetY = 1.0;
2159  break;
2160  case QuadrantAboveRight:
2161  quadOffsetX = 1.0;
2162  quadOffsetY = 1.0;
2163  break;
2164  case QuadrantLeft:
2165  quadOffsetX = -1.0;
2166  quadOffsetY = 0.0;
2167  break;
2168  case QuadrantRight:
2169  quadOffsetX = 1.0;
2170  quadOffsetY = 0.0;
2171  break;
2172  case QuadrantBelowLeft:
2173  quadOffsetX = -1.0;
2174  quadOffsetY = -1.0;
2175  break;
2176  case QuadrantBelow:
2177  quadOffsetX = 0.0;
2178  quadOffsetY = -1.0;
2179  break;
2180  case QuadrantBelowRight:
2181  quadOffsetX = 1.0;
2182  quadOffsetY = -1.0;
2183  break;
2184  case QuadrantOver:
2185  break;
2186  }
2187 
2188  //data defined label offset?
2189  double xOff = xOffset;
2190  double yOff = yOffset;
2191  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::OffsetXY ) )
2192  {
2194  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::OffsetXY, context.expressionContext() );
2195  bool ok = false;
2196  const QPointF ddOffPt = QgsSymbolLayerUtils::toPoint( exprVal, &ok );
2197  if ( ok )
2198  {
2199  xOff = ddOffPt.x();
2200  yOff = ddOffPt.y();
2201  }
2202  }
2203 
2204  // data defined label offset units?
2206  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::OffsetUnits ) )
2207  {
2208  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::OffsetUnits, context.expressionContext() );
2209  if ( exprVal.isValid() )
2210  {
2211  QString units = exprVal.toString().trimmed();
2212  if ( !units.isEmpty() )
2213  {
2214  bool ok = false;
2215  QgsUnitTypes::RenderUnit decodedUnits = QgsUnitTypes::decodeRenderUnit( units, &ok );
2216  if ( ok )
2217  {
2218  offUnit = decodedUnits;
2219  }
2220  }
2221  }
2222  }
2223 
2224  // adjust offset of labels to match chosen unit and map scale
2225  // offsets match those of symbology: -x = left, -y = up
2226  offsetX = context.convertToMapUnits( xOff, offUnit, labelOffsetMapUnitScale );
2227  // must be negative to match symbology offset direction
2228  offsetY = context.convertToMapUnits( -yOff, offUnit, labelOffsetMapUnitScale );
2229 
2230  // layer defined rotation?
2231  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2233  {
2234  layerDefinedRotation = true;
2235  angle = ( 360 - angleOffset ) * M_PI / 180; // convert to radians counterclockwise
2236  }
2237 
2238  const QgsMapToPixel &m2p = context.mapToPixel();
2239  //data defined rotation?
2240  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::LabelRotation ) )
2241  {
2243  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::LabelRotation, context.expressionContext() );
2244  if ( exprVal.isValid() )
2245  {
2246  bool ok;
2247  double rotD = exprVal.toDouble( &ok );
2248  if ( ok )
2249  {
2250  dataDefinedRotation = true;
2251  // TODO: add setting to disable having data defined rotation follow
2252  // map rotation ?
2253  rotD += m2p.mapRotation();
2254  angle = ( 360 - rotD ) * M_PI / 180.0;
2255  }
2256  }
2257  }
2258 
2259  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::PositionX ) )
2260  {
2261  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::PositionX, context.expressionContext() );
2262  if ( exprVal.isValid() )
2263  {
2264  if ( !exprVal.isNull() )
2265  xPos = exprVal.toDouble( &ddXPos );
2266 
2267  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::PositionY ) )
2268  {
2269  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::PositionY, context.expressionContext() );
2270  if ( exprVal.isValid() )
2271  {
2272  //data defined position. But field values could be NULL -> positions will be generated by PAL
2273  if ( !exprVal.isNull() )
2274  yPos = exprVal.toDouble( &ddYPos );
2275 
2276  if ( ddXPos && ddYPos )
2277  {
2278  dataDefinedPosition = true;
2279  // layer rotation set, but don't rotate pinned labels unless data defined
2280  if ( layerDefinedRotation && !dataDefinedRotation )
2281  {
2282  angle = 0.0;
2283  }
2284 
2285  //horizontal alignment
2286  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Hali ) )
2287  {
2288  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Hali, context.expressionContext() );
2289  if ( exprVal.isValid() )
2290  {
2291  QString haliString = exprVal.toString();
2292  if ( haliString.compare( QLatin1String( "Center" ), Qt::CaseInsensitive ) == 0 )
2293  {
2294  xdiff -= labelX / 2.0;
2295  }
2296  else if ( haliString.compare( QLatin1String( "Right" ), Qt::CaseInsensitive ) == 0 )
2297  {
2298  xdiff -= labelX;
2299  }
2300  }
2301  }
2302 
2303  //vertical alignment
2304  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Vali ) )
2305  {
2306  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Vali, context.expressionContext() );
2307  if ( exprVal.isValid() )
2308  {
2309  QString valiString = exprVal.toString();
2310  if ( valiString.compare( QLatin1String( "Bottom" ), Qt::CaseInsensitive ) != 0 )
2311  {
2312  if ( valiString.compare( QLatin1String( "Top" ), Qt::CaseInsensitive ) == 0 )
2313  {
2314  ydiff -= labelY;
2315  }
2316  else
2317  {
2318  double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
2319  if ( valiString.compare( QLatin1String( "Base" ), Qt::CaseInsensitive ) == 0 )
2320  {
2321  ydiff -= labelY * descentRatio;
2322  }
2323  else //'Cap' or 'Half'
2324  {
2325  double capHeightRatio = ( labelFontMetrics->boundingRect( 'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
2326  ydiff -= labelY * capHeightRatio;
2327  if ( valiString.compare( QLatin1String( "Half" ), Qt::CaseInsensitive ) == 0 )
2328  {
2329  ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
2330  }
2331  }
2332  }
2333  }
2334  }
2335  }
2336 
2337  if ( dataDefinedRotation )
2338  {
2339  //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
2340  double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
2341  double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
2342  xdiff = xd;
2343  ydiff = yd;
2344  }
2345 
2346  //project xPos and yPos from layer to map CRS, handle rotation
2347  QgsGeometry ddPoint( new QgsPoint( xPos, yPos ) );
2348  if ( QgsPalLabeling::geometryRequiresPreparation( ddPoint, context, ct ) )
2349  {
2350  ddPoint = QgsPalLabeling::prepareGeometry( ddPoint, context, ct );
2351  xPos = static_cast< const QgsPoint * >( ddPoint.constGet() )->x();
2352  yPos = static_cast< const QgsPoint * >( ddPoint.constGet() )->y();
2353  }
2354 
2355  anchorPosition = QgsPointXY( xPos, yPos );
2356 
2357  xPos += xdiff;
2358  yPos += ydiff;
2359  }
2360  else
2361  {
2362  anchorPosition = QgsPointXY( xPos, yPos );
2363 
2364  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2365  if ( dataDefinedRotation && placement != QgsPalLayerSettings::OverPoint )
2366  {
2367  angle = 0.0;
2368  }
2369  }
2370  }
2371  }
2372  }
2373  }
2374 
2375  // data defined always show?
2376  bool alwaysShow = false;
2377  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::AlwaysShow ) )
2378  {
2379  alwaysShow = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::AlwaysShow, context.expressionContext(), false );
2380  }
2381 
2382  // set repeat distance
2383  // data defined repeat distance?
2384  double repeatDist = repeatDistance;
2385  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::RepeatDistance ) )
2386  {
2387  context.expressionContext().setOriginalValueVariable( repeatDist );
2388  repeatDist = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::RepeatDistance, context.expressionContext(), repeatDist );
2389  }
2390 
2391  // data defined label-repeat distance units?
2393  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::RepeatDistanceUnit ) )
2394  {
2395  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::RepeatDistanceUnit, context.expressionContext() );
2396  if ( exprVal.isValid() )
2397  {
2398  QString units = exprVal.toString().trimmed();
2399  if ( !units.isEmpty() )
2400  {
2401  bool ok = false;
2402  QgsUnitTypes::RenderUnit decodedUnits = QgsUnitTypes::decodeRenderUnit( units, &ok );
2403  if ( ok )
2404  {
2405  repeatUnits = decodedUnits;
2406  }
2407  }
2408  }
2409  }
2410 
2411  if ( !qgsDoubleNear( repeatDist, 0.0 ) )
2412  {
2413  if ( repeatUnits != QgsUnitTypes::RenderMapUnits )
2414  {
2415  repeatDist = context.convertToMapUnits( repeatDist, repeatUnits, repeatDistanceMapUnitScale );
2416  }
2417  }
2418 
2419  // data defined overrun distance
2420  double overrunDistanceEval = overrunDistance;
2421  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::OverrunDistance ) )
2422  {
2423  context.expressionContext().setOriginalValueVariable( overrunDistanceEval );
2424  overrunDistanceEval = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::OverrunDistance, context.expressionContext(), overrunDistanceEval );
2425  }
2426  if ( !qgsDoubleNear( overrunDistanceEval, 0.0 ) )
2427  {
2428  overrunDistanceEval = context.convertToMapUnits( overrunDistanceEval, overrunDistanceUnit, overrunDistanceMapUnitScale );
2429  }
2430 
2431  // we smooth out the overrun label extensions by 1 mm, to avoid little jaggies right at the start or end of the lines
2432  // causing the overrun extension to extend out in an undesirable direction. This is hard coded, we don't want to overload
2433  // users with options they likely don't need to see...
2434  const double overrunSmoothDist = context.convertToMapUnits( 1, QgsUnitTypes::RenderMillimeters );
2435 
2436  bool labelAll = labelPerPart && !dataDefinedPosition;
2437  if ( !dataDefinedPosition )
2438  {
2439  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::LabelAllParts ) )
2440  {
2442  labelAll = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::LabelAllParts, context.expressionContext(), labelPerPart );
2443  }
2444  }
2445 
2446  // feature to the layer
2447  QgsTextLabelFeature *lf = new QgsTextLabelFeature( feature.id(), std::move( geos_geom_clone ), QSizeF( labelX, labelY ) );
2448  lf->setAnchorPosition( anchorPosition );
2449  lf->setFeature( feature );
2450  lf->setSymbol( symbol );
2451  lf->setDocument( doc );
2452  if ( !qgsDoubleNear( rotatedLabelX, 0.0 ) && !qgsDoubleNear( rotatedLabelY, 0.0 ) )
2453  lf->setRotatedSize( QSizeF( rotatedLabelX, rotatedLabelY ) );
2454  mFeatsRegPal++;
2455 
2456  *labelFeature = lf;
2457  ( *labelFeature )->setHasFixedPosition( dataDefinedPosition );
2458  ( *labelFeature )->setFixedPosition( QgsPointXY( xPos, yPos ) );
2459  // use layer-level defined rotation, but not if position fixed
2460  ( *labelFeature )->setHasFixedAngle( dataDefinedRotation || ( !dataDefinedPosition && !qgsDoubleNear( angle, 0.0 ) ) );
2461  ( *labelFeature )->setFixedAngle( angle );
2462  ( *labelFeature )->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
2463  ( *labelFeature )->setPositionOffset( QgsPointXY( offsetX, offsetY ) );
2464  ( *labelFeature )->setOffsetType( offsetType );
2465  ( *labelFeature )->setAlwaysShow( alwaysShow );
2466  ( *labelFeature )->setRepeatDistance( repeatDist );
2467  ( *labelFeature )->setLabelText( labelText );
2468  ( *labelFeature )->setPermissibleZone( permissibleZone );
2469  ( *labelFeature )->setOverrunDistance( overrunDistanceEval );
2470  ( *labelFeature )->setOverrunSmoothDistance( overrunSmoothDist );
2471  ( *labelFeature )->setLabelAllParts( labelAll );
2472  if ( geom.type() == QgsWkbTypes::PointGeometry && !obstacleGeometry.isNull() )
2473  {
2474  //register symbol size
2475  ( *labelFeature )->setSymbolSize( QSizeF( obstacleGeometry.boundingBox().width(),
2476  obstacleGeometry.boundingBox().height() ) );
2477  }
2478 
2479  //set label's visual margin so that top visual margin is the leading, and bottom margin is the font's descent
2480  //this makes labels align to the font's baseline or highest character
2481  double topMargin = std::max( 0.25 * labelFontMetrics->ascent(), 0.0 );
2482  double bottomMargin = 1.0 + labelFontMetrics->descent();
2483  QgsMargins vm( 0.0, topMargin, 0.0, bottomMargin );
2484  vm *= xform->mapUnitsPerPixel();
2485  ( *labelFeature )->setVisualMargin( vm );
2486 
2487  // store the label's calculated font for later use during painting
2488  QgsDebugMsgLevel( QStringLiteral( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString(), labelFont.styleName() ), 4 );
2489  lf->setDefinedFont( labelFont );
2490 
2491  // TODO: only for placement which needs character info
2492  // account for any data defined font metrics adjustments
2494  labelFontMetrics.get(), xform, maxcharanglein, maxcharangleout, format().allowHtmlFormatting() ? &doc : nullptr );
2495  // for labelFeature the LabelInfo is passed to feat when it is registered
2496 
2497  // TODO: allow layer-wide feature dist in PAL...?
2498 
2499  // data defined label-feature distance?
2500  double distance = dist;
2501  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::LabelDistance ) )
2502  {
2503  context.expressionContext().setOriginalValueVariable( distance );
2504  distance = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::LabelDistance, context.expressionContext(), distance );
2505  }
2506 
2507  // data defined label-feature distance units?
2509  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::DistanceUnits ) )
2510  {
2511  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::DistanceUnits, context.expressionContext() );
2512  if ( exprVal.isValid() )
2513  {
2514  QString units = exprVal.toString().trimmed();
2515  QgsDebugMsgLevel( QStringLiteral( "exprVal DistanceUnits:%1" ).arg( units ), 4 );
2516  if ( !units.isEmpty() )
2517  {
2518  bool ok = false;
2519  QgsUnitTypes::RenderUnit decodedUnits = QgsUnitTypes::decodeRenderUnit( units, &ok );
2520  if ( ok )
2521  {
2522  distUnit = decodedUnits;
2523  }
2524  }
2525  }
2526  }
2527  distance = context.convertToPainterUnits( distance, distUnit, distMapUnitScale );
2528 
2529  // when using certain placement modes, we force a tiny minimum distance. This ensures that
2530  // candidates are created just offset from a border and avoids candidates being incorrectly flagged as colliding with neighbours
2534  {
2535  distance = ( distance < 0 ? -1 : 1 ) * std::max( std::fabs( distance ), 1.0 );
2536  }
2541  placement == QgsPalLayerSettings::Free ) && polygonPlacement & QgsLabeling::PolygonPlacementFlag::AllowPlacementOutsideOfPolygon ) )
2542  {
2543  distance = std::max( distance, 2.0 );
2544  }
2545 
2546  if ( !qgsDoubleNear( distance, 0.0 ) )
2547  {
2548  double d = ptOne.distance( ptZero ) * distance;
2549  ( *labelFeature )->setDistLabel( d );
2550  }
2551 
2552  if ( ddFixedQuad )
2553  {
2554  ( *labelFeature )->setHasFixedQuadrant( true );
2555  }
2556 
2557  QgsLabeling::LinePlacementFlags featureArrangementFlags = static_cast< QgsLabeling::LinePlacementFlags >( placementFlags );
2558  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::LinePlacementOptions ) )
2559  {
2561  const QString dataDefinedLineArrangement = mDataDefinedProperties.valueAsString( QgsPalLayerSettings::LinePlacementOptions, context.expressionContext() );
2562  if ( !dataDefinedLineArrangement.isEmpty() )
2563  {
2564  featureArrangementFlags = QgsLabelingUtils::decodeLinePlacementFlags( dataDefinedLineArrangement );
2565  }
2566  }
2567  ( *labelFeature )->setArrangementFlags( featureArrangementFlags );
2568 
2569  ( *labelFeature )->setPolygonPlacementFlags( polygonPlacement );
2570 
2571  // data defined z-index?
2572  double z = zIndex;
2573  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::ZIndex ) )
2574  {
2576  z = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::ZIndex, context.expressionContext(), z );
2577  }
2578  ( *labelFeature )->setZIndex( z );
2579 
2580  // data defined priority?
2581  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Priority ) )
2582  {
2584  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Priority, context.expressionContext() );
2585  if ( exprVal.isValid() )
2586  {
2587  bool ok;
2588  double priorityD = exprVal.toDouble( &ok );
2589  if ( ok )
2590  {
2591  priorityD = qBound( 0.0, priorityD, 10.0 );
2592  priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
2593  ( *labelFeature )->setPriority( priorityD );
2594  }
2595  }
2596  }
2597 
2598  QgsLabelObstacleSettings os = mObstacleSettings;
2599  os.setIsObstacle( isObstacle );
2600  os.updateDataDefinedProperties( mDataDefinedProperties, context.expressionContext() );
2601  os.setObstacleGeometry( obstacleGeometry );
2602  lf->setObstacleSettings( os );
2603 
2604  QVector< QgsPalLayerSettings::PredefinedPointPosition > positionOrder = predefinedPositionOrder;
2605  if ( positionOrder.isEmpty() )
2606  positionOrder = *DEFAULT_PLACEMENT_ORDER();
2607 
2608  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::PredefinedPositionOrder ) )
2609  {
2611  QString dataDefinedOrder = mDataDefinedProperties.valueAsString( QgsPalLayerSettings::PredefinedPositionOrder, context.expressionContext() );
2612  if ( !dataDefinedOrder.isEmpty() )
2613  {
2614  positionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( dataDefinedOrder );
2615  }
2616  }
2617  ( *labelFeature )->setPredefinedPositionOrder( positionOrder );
2618 
2619  // add parameters for data defined labeling to label feature
2620  lf->setDataDefinedValues( dataDefinedValues );
2621 }
2622 
2623 void QgsPalLayerSettings::registerObstacleFeature( const QgsFeature &f, QgsRenderContext &context, QgsLabelFeature **obstacleFeature, const QgsGeometry &obstacleGeometry )
2624 {
2625  mCurFeat = &f;
2626 
2627  QgsGeometry geom;
2628  if ( !obstacleGeometry.isNull() )
2629  {
2630  geom = obstacleGeometry;
2631  }
2632  else
2633  {
2634  geom = f.geometry();
2635  }
2636 
2637  if ( geom.isNull() )
2638  {
2639  return;
2640  }
2641 
2642  // simplify?
2643  const QgsVectorSimplifyMethod &simplifyMethod = context.vectorSimplifyMethod();
2644  std::unique_ptr<QgsGeometry> scopedClonedGeom;
2645  if ( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && simplifyMethod.forceLocalOptimization() )
2646  {
2647  int simplifyHints = simplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
2649  QgsMapToPixelSimplifier simplifier( simplifyHints, simplifyMethod.tolerance(), simplifyAlgorithm );
2650  geom = simplifier.simplify( geom );
2651  }
2652 
2653  geos::unique_ptr geos_geom_clone;
2654  std::unique_ptr<QgsGeometry> scopedPreparedGeom;
2655 
2657  {
2658  geom = QgsPalLabeling::prepareGeometry( geom, context, ct, extentGeom, mergeLines );
2659  }
2660  geos_geom_clone = QgsGeos::asGeos( geom );
2661 
2662  if ( !geos_geom_clone )
2663  return; // invalid geometry
2664 
2665  // feature to the layer
2666  *obstacleFeature = new QgsLabelFeature( f.id(), std::move( geos_geom_clone ), QSizeF( 0, 0 ) );
2667  ( *obstacleFeature )->setFeature( f );
2668 
2669  QgsLabelObstacleSettings os = mObstacleSettings;
2670  os.setIsObstacle( true );
2671  os.updateDataDefinedProperties( mDataDefinedProperties, context.expressionContext() );
2672  ( *obstacleFeature )->setObstacleSettings( os );
2673 
2674  mFeatsRegPal++;
2675 }
2676 
2677 bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
2679  QVariant &exprVal, QgsExpressionContext &context, const QVariant &originalValue )
2680 {
2681  if ( !mDataDefinedProperties.isActive( p ) )
2682  return false;
2683 
2684  context.setOriginalValueVariable( originalValue );
2685  exprVal = mDataDefinedProperties.value( p, context );
2686  if ( exprVal.isValid() )
2687  {
2688  switch ( valType )
2689  {
2690  case DDBool:
2691  {
2692  bool bol = exprVal.toBool();
2693  dataDefinedValues.insert( p, QVariant( bol ) );
2694  return true;
2695  }
2696  case DDInt:
2697  {
2698  bool ok;
2699  int size = exprVal.toInt( &ok );
2700 
2701  if ( ok )
2702  {
2703  dataDefinedValues.insert( p, QVariant( size ) );
2704  return true;
2705  }
2706  return false;
2707  }
2708  case DDIntPos:
2709  {
2710  bool ok;
2711  int size = exprVal.toInt( &ok );
2712 
2713  if ( ok && size > 0 )
2714  {
2715  dataDefinedValues.insert( p, QVariant( size ) );
2716  return true;
2717  }
2718  return false;
2719  }
2720  case DDDouble:
2721  {
2722  bool ok;
2723  double size = exprVal.toDouble( &ok );
2724 
2725  if ( ok )
2726  {
2727  dataDefinedValues.insert( p, QVariant( size ) );
2728  return true;
2729  }
2730  return false;
2731  }
2732  case DDDoublePos:
2733  {
2734  bool ok;
2735  double size = exprVal.toDouble( &ok );
2736 
2737  if ( ok && size > 0.0 )
2738  {
2739  dataDefinedValues.insert( p, QVariant( size ) );
2740  return true;
2741  }
2742  return false;
2743  }
2744  case DDRotation180:
2745  {
2746  bool ok;
2747  double rot = exprVal.toDouble( &ok );
2748  if ( ok )
2749  {
2750  if ( rot < -180.0 && rot >= -360 )
2751  {
2752  rot += 360;
2753  }
2754  if ( rot > 180.0 && rot <= 360 )
2755  {
2756  rot -= 360;
2757  }
2758  if ( rot >= -180 && rot <= 180 )
2759  {
2760  dataDefinedValues.insert( p, QVariant( rot ) );
2761  return true;
2762  }
2763  }
2764  return false;
2765  }
2766  case DDOpacity:
2767  {
2768  bool ok;
2769  int size = exprVal.toInt( &ok );
2770  if ( ok && size >= 0 && size <= 100 )
2771  {
2772  dataDefinedValues.insert( p, QVariant( size ) );
2773  return true;
2774  }
2775  return false;
2776  }
2777  case DDString:
2778  {
2779  QString str = exprVal.toString(); // don't trim whitespace
2780 
2781  dataDefinedValues.insert( p, QVariant( str ) ); // let it stay empty if it is
2782  return true;
2783  }
2784  case DDUnits:
2785  {
2786  QString unitstr = exprVal.toString().trimmed();
2787 
2788  if ( !unitstr.isEmpty() )
2789  {
2790  dataDefinedValues.insert( p, QVariant( static_cast< int >( QgsUnitTypes::decodeRenderUnit( unitstr ) ) ) );
2791  return true;
2792  }
2793  return false;
2794  }
2795  case DDColor:
2796  {
2797  QString colorstr = exprVal.toString().trimmed();
2798  QColor color = QgsSymbolLayerUtils::decodeColor( colorstr );
2799 
2800  if ( color.isValid() )
2801  {
2802  dataDefinedValues.insert( p, QVariant( color ) );
2803  return true;
2804  }
2805  return false;
2806  }
2807  case DDJoinStyle:
2808  {
2809  QString joinstr = exprVal.toString().trimmed();
2810 
2811  if ( !joinstr.isEmpty() )
2812  {
2813  dataDefinedValues.insert( p, QVariant( static_cast< int >( QgsSymbolLayerUtils::decodePenJoinStyle( joinstr ) ) ) );
2814  return true;
2815  }
2816  return false;
2817  }
2818  case DDBlendMode:
2819  {
2820  QString blendstr = exprVal.toString().trimmed();
2821 
2822  if ( !blendstr.isEmpty() )
2823  {
2824  dataDefinedValues.insert( p, QVariant( static_cast< int >( QgsSymbolLayerUtils::decodeBlendMode( blendstr ) ) ) );
2825  return true;
2826  }
2827  return false;
2828  }
2829  case DDPointF:
2830  {
2831  bool ok = false;
2832  const QPointF res = QgsSymbolLayerUtils::toPoint( exprVal, &ok );
2833  if ( ok )
2834  {
2835  dataDefinedValues.insert( p, res );
2836  return true;
2837  }
2838  return false;
2839  }
2840  case DDSizeF:
2841  {
2842  bool ok = false;
2843  const QSizeF res = QgsSymbolLayerUtils::toSize( exprVal, &ok );
2844  if ( ok )
2845  {
2846  dataDefinedValues.insert( p, res );
2847  return true;
2848  }
2849  return false;
2850  }
2851  }
2852  }
2853  return false;
2854 }
2855 
2856 void QgsPalLayerSettings::parseTextStyle( QFont &labelFont,
2857  QgsUnitTypes::RenderUnit fontunits,
2858  QgsRenderContext &context )
2859 {
2860  // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
2861 
2862  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2863 
2864  // Two ways to generate new data defined font:
2865  // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
2866  // 2) Family + named style (bold or italic is ignored)
2867 
2868  // data defined font family?
2869  QString ddFontFamily;
2870  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Family ) )
2871  {
2872  context.expressionContext().setOriginalValueVariable( labelFont.family() );
2873  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Family, context.expressionContext() );
2874  if ( exprVal.isValid() )
2875  {
2876  QString family = exprVal.toString().trimmed();
2877  QgsDebugMsgLevel( QStringLiteral( "exprVal Font family:%1" ).arg( family ), 4 );
2878 
2879  if ( labelFont.family() != family )
2880  {
2881  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
2882  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
2883  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
2884  {
2885  ddFontFamily = family;
2886  }
2887  }
2888  }
2889  }
2890 
2891  // data defined named font style?
2892  QString ddFontStyle;
2893  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::FontStyle ) )
2894  {
2895  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::FontStyle, context.expressionContext() );
2896  if ( exprVal.isValid() )
2897  {
2898  QString fontstyle = exprVal.toString().trimmed();
2899  QgsDebugMsgLevel( QStringLiteral( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
2900  ddFontStyle = fontstyle;
2901  }
2902  }
2903 
2904  // data defined bold font style?
2905  bool ddBold = false;
2906  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Bold ) )
2907  {
2908  context.expressionContext().setOriginalValueVariable( labelFont.bold() );
2909  ddBold = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Bold, context.expressionContext(), false );
2910  }
2911 
2912  // data defined italic font style?
2913  bool ddItalic = false;
2914  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Italic ) )
2915  {
2916  context.expressionContext().setOriginalValueVariable( labelFont.italic() );
2917  ddItalic = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Italic, context.expressionContext(), false );
2918  }
2919 
2920  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
2921  // (currently defaults to what has been read in from layer settings)
2922  QFont newFont;
2923  QFont appFont = QApplication::font();
2924  bool newFontBuilt = false;
2925  if ( ddBold || ddItalic )
2926  {
2927  // new font needs built, since existing style needs removed
2928  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
2929  newFontBuilt = true;
2930  newFont.setBold( ddBold );
2931  newFont.setItalic( ddItalic );
2932  }
2933  else if ( !ddFontStyle.isEmpty()
2934  && ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
2935  {
2936  if ( !ddFontFamily.isEmpty() )
2937  {
2938  // both family and style are different, build font from database
2939  QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
2940  if ( appFont != styledfont )
2941  {
2942  newFont = styledfont;
2943  newFontBuilt = true;
2944  }
2945  }
2946 
2947  // update the font face style
2948  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
2949  }
2950  else if ( !ddFontFamily.isEmpty() )
2951  {
2952  if ( ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
2953  {
2954  // just family is different, build font from database
2955  QFont styledfont = mFontDB.font( ddFontFamily, mFormat.namedStyle(), appFont.pointSize() );
2956  if ( appFont != styledfont )
2957  {
2958  newFont = styledfont;
2959  newFontBuilt = true;
2960  }
2961  }
2962  else
2963  {
2964  newFont = QFont( ddFontFamily );
2965  newFontBuilt = true;
2966  }
2967  }
2968 
2969  if ( newFontBuilt )
2970  {
2971  // copy over existing font settings
2972  //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
2973  newFont.setPixelSize( labelFont.pixelSize() );
2974  newFont.setUnderline( labelFont.underline() );
2975  newFont.setStrikeOut( labelFont.strikeOut() );
2976  newFont.setWordSpacing( labelFont.wordSpacing() );
2977  newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
2978 
2979  labelFont = newFont;
2980  }
2981 
2982  // data defined word spacing?
2983  double wordspace = labelFont.wordSpacing();
2984  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::FontWordSpacing ) )
2985  {
2986  context.expressionContext().setOriginalValueVariable( wordspace );
2987  wordspace = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::FontWordSpacing, context.expressionContext(), wordspace );
2988  }
2989  labelFont.setWordSpacing( context.convertToPainterUnits( wordspace, fontunits, mFormat.sizeMapUnitScale() ) );
2990 
2991  // data defined letter spacing?
2992  double letterspace = labelFont.letterSpacing();
2993  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::FontLetterSpacing ) )
2994  {
2995  context.expressionContext().setOriginalValueVariable( letterspace );
2996  letterspace = mDataDefinedProperties.valueAsDouble( QgsPalLayerSettings::FontLetterSpacing, context.expressionContext(), letterspace );
2997  }
2998  labelFont.setLetterSpacing( QFont::AbsoluteSpacing, context.convertToPainterUnits( letterspace, fontunits, mFormat.sizeMapUnitScale() ) );
2999 
3000  // data defined strikeout font style?
3001  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Strikeout ) )
3002  {
3003  context.expressionContext().setOriginalValueVariable( labelFont.strikeOut() );
3004  bool strikeout = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Strikeout, context.expressionContext(), false );
3005  labelFont.setStrikeOut( strikeout );
3006  }
3007 
3008  // data defined underline font style?
3009  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Underline ) )
3010  {
3011  context.expressionContext().setOriginalValueVariable( labelFont.underline() );
3012  bool underline = mDataDefinedProperties.valueAsBool( QgsPalLayerSettings::Underline, context.expressionContext(), false );
3013  labelFont.setUnderline( underline );
3014  }
3015 
3016  // pass the rest on to QgsPalLabeling::drawLabeling
3017 
3018  // data defined font color?
3019  dataDefinedValEval( DDColor, QgsPalLayerSettings::Color, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( mFormat.color() ) );
3020 
3021  // data defined font opacity?
3022  dataDefinedValEval( DDOpacity, QgsPalLayerSettings::FontOpacity, exprVal, context.expressionContext(), mFormat.opacity() * 100 );
3023 
3024  // data defined font blend mode?
3025  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::FontBlendMode, exprVal, context.expressionContext() );
3026 
3027 }
3028 
3029 void QgsPalLayerSettings::parseTextBuffer( QgsRenderContext &context )
3030 {
3031  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3032 
3033  QgsTextBufferSettings buffer = mFormat.buffer();
3034 
3035  // data defined draw buffer?
3036  bool drawBuffer = mFormat.buffer().enabled();
3037  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::BufferDraw, exprVal, context.expressionContext(), buffer.enabled() ) )
3038  {
3039  drawBuffer = exprVal.toBool();
3040  }
3041  else if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::BufferDraw ) && exprVal.isNull() )
3042  {
3043  drawBuffer = false;
3044  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( drawBuffer ) );
3045  }
3046 
3047  if ( !drawBuffer )
3048  {
3049  return;
3050  }
3051 
3052  // data defined buffer size?
3053  double bufrSize = buffer.size();
3054  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::BufferSize, exprVal, context.expressionContext(), buffer.size() ) )
3055  {
3056  bufrSize = exprVal.toDouble();
3057  }
3058 
3059  // data defined buffer transparency?
3060  double bufferOpacity = buffer.opacity() * 100;
3061  if ( dataDefinedValEval( DDOpacity, QgsPalLayerSettings::BufferOpacity, exprVal, context.expressionContext(), bufferOpacity ) )
3062  {
3063  bufferOpacity = exprVal.toDouble();
3064  }
3065 
3066  drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufferOpacity > 0 );
3067 
3068  if ( !drawBuffer )
3069  {
3070  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
3071  dataDefinedValues.remove( QgsPalLayerSettings::BufferSize );
3072  dataDefinedValues.remove( QgsPalLayerSettings::BufferOpacity );
3073  return; // don't bother evaluating values that won't be used
3074  }
3075 
3076  // data defined buffer units?
3077  dataDefinedValEval( DDUnits, QgsPalLayerSettings::BufferUnit, exprVal, context.expressionContext() );
3078 
3079  // data defined buffer color?
3080  dataDefinedValEval( DDColor, QgsPalLayerSettings::BufferColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( buffer.color() ) );
3081 
3082  // data defined buffer pen join style?
3083  dataDefinedValEval( DDJoinStyle, QgsPalLayerSettings::BufferJoinStyle, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodePenJoinStyle( buffer.joinStyle() ) );
3084 
3085  // data defined buffer blend mode?
3086  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::BufferBlendMode, exprVal, context.expressionContext() );
3087 }
3088 
3089 void QgsPalLayerSettings::parseTextMask( QgsRenderContext &context )
3090 {
3091  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3092 
3093  QgsTextMaskSettings mask = mFormat.mask();
3094 
3095  // data defined enabled mask?
3096  bool maskEnabled = mask.enabled();
3097  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::MaskEnabled, exprVal, context.expressionContext(), mask.enabled() ) )
3098  {
3099  maskEnabled = exprVal.toBool();
3100  }
3101 
3102  if ( !maskEnabled )
3103  {
3104  return;
3105  }
3106 
3107  // data defined buffer size?
3108  double bufrSize = mask.size();
3109  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::MaskBufferSize, exprVal, context.expressionContext(), mask.size() ) )
3110  {
3111  bufrSize = exprVal.toDouble();
3112  }
3113 
3114  // data defined opacity?
3115  double opacity = mask.opacity() * 100;
3116  if ( dataDefinedValEval( DDOpacity, QgsPalLayerSettings::MaskOpacity, exprVal, context.expressionContext(), opacity ) )
3117  {
3118  opacity = exprVal.toDouble();
3119  }
3120 
3121  maskEnabled = ( maskEnabled && bufrSize > 0.0 && opacity > 0 );
3122 
3123  if ( !maskEnabled )
3124  {
3125  dataDefinedValues.insert( QgsPalLayerSettings::MaskEnabled, QVariant( false ) ); // trigger value
3126  dataDefinedValues.remove( QgsPalLayerSettings::MaskBufferSize );
3127  dataDefinedValues.remove( QgsPalLayerSettings::MaskOpacity );
3128  return; // don't bother evaluating values that won't be used
3129  }
3130 
3131  // data defined buffer units?
3132  dataDefinedValEval( DDUnits, QgsPalLayerSettings::MaskBufferUnit, exprVal, context.expressionContext() );
3133 
3134  // data defined buffer pen join style?
3135  dataDefinedValEval( DDJoinStyle, QgsPalLayerSettings::MaskJoinStyle, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodePenJoinStyle( mask.joinStyle() ) );
3136 }
3137 
3138 void QgsPalLayerSettings::parseTextFormatting( QgsRenderContext &context )
3139 {
3140  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3141 
3142  // data defined multiline wrap character?
3143  QString wrapchr = wrapChar;
3144  if ( dataDefinedValEval( DDString, QgsPalLayerSettings::MultiLineWrapChar, exprVal, context.expressionContext(), wrapChar ) )
3145  {
3146  wrapchr = exprVal.toString();
3147  }
3148 
3149  int evalAutoWrapLength = autoWrapLength;
3150  if ( dataDefinedValEval( DDInt, QgsPalLayerSettings::AutoWrapLength, exprVal, context.expressionContext(), evalAutoWrapLength ) )
3151  {
3152  evalAutoWrapLength = exprVal.toInt();
3153  }
3154 
3155  // data defined multiline height?
3156  dataDefinedValEval( DDDouble, QgsPalLayerSettings::MultiLineHeight, exprVal, context.expressionContext() );
3157 
3158  // data defined multiline text align?
3159  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::MultiLineAlignment ) )
3160  {
3161  context.expressionContext().setOriginalValueVariable( mFormat.lineHeight() );
3162  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::MultiLineAlignment, context.expressionContext() );
3163  if ( exprVal.isValid() )
3164  {
3165  QString str = exprVal.toString().trimmed();
3166  QgsDebugMsgLevel( QStringLiteral( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
3167 
3168  if ( !str.isEmpty() )
3169  {
3170  // "Left"
3172 
3173  if ( str.compare( QLatin1String( "Center" ), Qt::CaseInsensitive ) == 0 )
3174  {
3176  }
3177  else if ( str.compare( QLatin1String( "Right" ), Qt::CaseInsensitive ) == 0 )
3178  {
3179  aligntype = QgsPalLayerSettings::MultiRight;
3180  }
3181  else if ( str.compare( QLatin1String( "Follow" ), Qt::CaseInsensitive ) == 0 )
3182  {
3184  }
3185  dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant( static_cast< int >( aligntype ) ) );
3186  }
3187  }
3188  }
3189 
3190  // text orientation
3191  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::TextOrientation ) )
3192  {
3193  const QString encoded = QgsTextRendererUtils::encodeTextOrientation( mFormat.orientation() );
3194  context.expressionContext().setOriginalValueVariable( encoded );
3195  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::TextOrientation, context.expressionContext() );
3196  if ( exprVal.isValid() )
3197  {
3198  QString str = exprVal.toString().trimmed();
3199  if ( !str.isEmpty() )
3200  dataDefinedValues.insert( QgsPalLayerSettings::TextOrientation, str );
3201  }
3202  }
3203 
3204  // data defined direction symbol?
3205  bool drawDirSymb = addDirectionSymbol;
3206  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbDraw, exprVal, context.expressionContext(), addDirectionSymbol ) )
3207  {
3208  drawDirSymb = exprVal.toBool();
3209  }
3210 
3211  if ( drawDirSymb )
3212  {
3213  // data defined direction left symbol?
3214  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbLeft, exprVal, context.expressionContext(), leftDirectionSymbol );
3215 
3216  // data defined direction right symbol?
3217  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbRight, exprVal, context.expressionContext(), rightDirectionSymbol );
3218 
3219  // data defined direction symbol placement?
3220  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::DirSymbPlacement, context.expressionContext() );
3221  if ( exprVal.isValid() )
3222  {
3223  QString str = exprVal.toString().trimmed();
3224  QgsDebugMsgLevel( QStringLiteral( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
3225 
3226  if ( !str.isEmpty() )
3227  {
3228  // "LeftRight"
3230 
3231  if ( str.compare( QLatin1String( "Above" ), Qt::CaseInsensitive ) == 0 )
3232  {
3234  }
3235  else if ( str.compare( QLatin1String( "Below" ), Qt::CaseInsensitive ) == 0 )
3236  {
3238  }
3239  dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant( static_cast< int >( placetype ) ) );
3240  }
3241  }
3242 
3243  // data defined direction symbol reversed?
3244  dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbReverse, exprVal, context.expressionContext(), reverseDirectionSymbol );
3245  }
3246 
3247  // formatting for numbers is inline with generation of base label text and not passed to label painting
3248 }
3249 
3250 void QgsPalLayerSettings::parseShapeBackground( QgsRenderContext &context )
3251 {
3252  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3253 
3254  QgsTextBackgroundSettings background = mFormat.background();
3255 
3256  // data defined draw shape?
3257  bool drawShape = background.enabled();
3258  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShapeDraw, exprVal, context.expressionContext(), drawShape ) )
3259  {
3260  drawShape = exprVal.toBool();
3261  }
3262 
3263  if ( !drawShape )
3264  {
3265  return;
3266  }
3267 
3268  // data defined shape transparency?
3269  double shapeOpacity = background.opacity() * 100;
3270  if ( dataDefinedValEval( DDOpacity, QgsPalLayerSettings::ShapeOpacity, exprVal, context.expressionContext(), shapeOpacity ) )
3271  {
3272  shapeOpacity = 100.0 * exprVal.toDouble();
3273  }
3274 
3275  drawShape = ( drawShape && shapeOpacity > 0 ); // size is not taken into account (could be)
3276 
3277  if ( !drawShape )
3278  {
3279  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
3280  dataDefinedValues.remove( QgsPalLayerSettings::ShapeOpacity );
3281  return; // don't bother evaluating values that won't be used
3282  }
3283 
3284  // data defined shape kind?
3285  QgsTextBackgroundSettings::ShapeType shapeKind = background.type();
3286  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::ShapeKind ) )
3287  {
3288  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShapeKind, context.expressionContext() );
3289  if ( exprVal.isValid() )
3290  {
3291  QString skind = exprVal.toString().trimmed();
3292  QgsDebugMsgLevel( QStringLiteral( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
3293 
3294  if ( !skind.isEmpty() )
3295  {
3296  shapeKind = QgsTextRendererUtils::decodeShapeType( skind );
3297  dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant( static_cast< int >( shapeKind ) ) );
3298  }
3299  }
3300  }
3301 
3302  // data defined shape SVG path?
3303  QString svgPath = background.svgFile();
3304  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::ShapeSVGFile ) )
3305  {
3306  context.expressionContext().setOriginalValueVariable( svgPath );
3307  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShapeSVGFile, context.expressionContext() );
3308  if ( exprVal.isValid() )
3309  {
3310  QString svgfile = exprVal.toString().trimmed();
3311  QgsDebugMsgLevel( QStringLiteral( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
3312 
3313  // '' empty paths are allowed
3314  svgPath = QgsSymbolLayerUtils::svgSymbolNameToPath( svgfile, context.pathResolver() );
3315  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgPath ) );
3316  }
3317  }
3318 
3319  // data defined shape size type?
3320  QgsTextBackgroundSettings::SizeType shpSizeType = background.sizeType();
3321  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::ShapeSizeType ) )
3322  {
3323  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShapeSizeType, context.expressionContext() );
3324  if ( exprVal.isValid() )
3325  {
3326  QString stype = exprVal.toString().trimmed();
3327  QgsDebugMsgLevel( QStringLiteral( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
3328 
3329  if ( !stype.isEmpty() )
3330  {
3331  shpSizeType = QgsTextRendererUtils::decodeBackgroundSizeType( stype );
3332  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant( static_cast< int >( shpSizeType ) ) );
3333  }
3334  }
3335  }
3336 
3337  // data defined shape size X? (SVGs only use X for sizing)
3338  double ddShpSizeX = background.size().width();
3339  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeX, exprVal, context.expressionContext(), ddShpSizeX ) )
3340  {
3341  ddShpSizeX = exprVal.toDouble();
3342  }
3343 
3344  // data defined shape size Y?
3345  double ddShpSizeY = background.size().height();
3346  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeY, exprVal, context.expressionContext(), ddShpSizeY ) )
3347  {
3348  ddShpSizeY = exprVal.toDouble();
3349  }
3350 
3351  // don't continue under certain circumstances (e.g. size is fixed)
3352  bool skip = false;
3353  if ( shapeKind == QgsTextBackgroundSettings::ShapeSVG
3354  && ( svgPath.isEmpty()
3355  || ( !svgPath.isEmpty()
3356  && shpSizeType == QgsTextBackgroundSettings::SizeFixed
3357  && ddShpSizeX == 0.0 ) ) )
3358  {
3359  skip = true;
3360  }
3362  && ( !background.markerSymbol()
3363  || ( background.markerSymbol()
3364  && shpSizeType == QgsTextBackgroundSettings::SizeFixed
3365  && ddShpSizeX == 0.0 ) ) )
3366  {
3367  skip = true;
3368  }
3369  if ( shapeKind != QgsTextBackgroundSettings::ShapeSVG
3371  && shpSizeType == QgsTextBackgroundSettings::SizeFixed
3372  && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
3373  {
3374  skip = true;
3375  }
3376 
3377  if ( skip )
3378  {
3379  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
3380  dataDefinedValues.remove( QgsPalLayerSettings::ShapeOpacity );
3381  dataDefinedValues.remove( QgsPalLayerSettings::ShapeKind );
3382  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSVGFile );
3383  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeX );
3384  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeY );
3385  return; // don't bother evaluating values that won't be used
3386  }
3387 
3388  // data defined shape size units?
3389  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeSizeUnits, exprVal, context.expressionContext() );
3390 
3391  // data defined shape rotation type?
3392  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::ShapeRotationType ) )
3393  {
3394  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShapeRotationType, context.expressionContext() );
3395  if ( exprVal.isValid() )
3396  {
3397  QString rotstr = exprVal.toString().trimmed();
3398  QgsDebugMsgLevel( QStringLiteral( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
3399 
3400  if ( !rotstr.isEmpty() )
3401  {
3402  // "Sync"
3404  dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant( static_cast< int >( rottype ) ) );
3405  }
3406  }
3407  }
3408 
3409  // data defined shape rotation?
3410  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShapeRotation, exprVal, context.expressionContext(), background.rotation() );
3411 
3412  // data defined shape offset?
3413  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeOffset, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodePoint( background.offset() ) );
3414 
3415  // data defined shape offset units?
3416  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeOffsetUnits, exprVal, context.expressionContext() );
3417 
3418  // data defined shape radii?
3419  dataDefinedValEval( DDSizeF, QgsPalLayerSettings::ShapeRadii, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeSize( background.radii() ) );
3420 
3421  // data defined shape radii units?
3422  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeRadiiUnits, exprVal, context.expressionContext() );
3423 
3424  // data defined shape blend mode?
3425  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShapeBlendMode, exprVal, context.expressionContext() );
3426 
3427  // data defined shape fill color?
3428  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShapeFillColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( background.fillColor() ) );
3429 
3430  // data defined shape stroke color?
3431  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShapeStrokeColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( background.strokeColor() ) );
3432 
3433  // data defined shape stroke width?
3434  dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShapeStrokeWidth, exprVal, context.expressionContext(), background.strokeWidth() );
3435 
3436  // data defined shape stroke width units?
3437  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeStrokeWidthUnits, exprVal, context.expressionContext() );
3438 
3439  // data defined shape join style?
3440  dataDefinedValEval( DDJoinStyle, QgsPalLayerSettings::ShapeJoinStyle, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodePenJoinStyle( background.joinStyle() ) );
3441 
3442 }
3443 
3444 void QgsPalLayerSettings::parseDropShadow( QgsRenderContext &context )
3445 {
3446  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3447 
3448  QgsTextShadowSettings shadow = mFormat.shadow();
3449 
3450  // data defined draw shadow?
3451  bool drawShadow = shadow.enabled();
3452  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShadowDraw, exprVal, context.expressionContext(), drawShadow ) )
3453  {
3454  drawShadow = exprVal.toBool();
3455  }
3456 
3457  if ( !drawShadow )
3458  {
3459  return;
3460  }
3461 
3462  // data defined shadow transparency?
3463  double shadowOpacity = shadow.opacity() * 100;
3464  if ( dataDefinedValEval( DDOpacity, QgsPalLayerSettings::ShadowOpacity, exprVal, context.expressionContext(), shadowOpacity ) )
3465  {
3466  shadowOpacity = exprVal.toDouble();
3467  }
3468 
3469  // data defined shadow offset distance?
3470  double shadowOffDist = shadow.offsetDistance();
3471  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowOffsetDist, exprVal, context.expressionContext(), shadowOffDist ) )
3472  {
3473  shadowOffDist = exprVal.toDouble();
3474  }
3475 
3476  // data defined shadow offset distance?
3477  double shadowRad = shadow.blurRadius();
3478  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRad ) )
3479  {
3480  shadowRad = exprVal.toDouble();
3481  }
3482 
3483  drawShadow = ( drawShadow && shadowOpacity > 0 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
3484 
3485  if ( !drawShadow )
3486  {
3487  dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
3488  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOpacity );
3489  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOffsetDist );
3490  dataDefinedValues.remove( QgsPalLayerSettings::ShadowRadius );
3491  return; // don't bother evaluating values that won't be used
3492  }
3493 
3494  // data defined shadow under type?
3495  if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::ShadowUnder ) )
3496  {
3497  exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ShadowUnder, context.expressionContext() );
3498  if ( exprVal.isValid() )
3499  {
3500  QString str = exprVal.toString().trimmed();
3501  QgsDebugMsgLevel( QStringLiteral( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
3502 
3503  if ( !str.isEmpty() )
3504  {
3506  dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant( static_cast< int >( shdwtype ) ) );
3507  }
3508  }
3509  }
3510 
3511  // data defined shadow offset angle?
3512  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShadowOffsetAngle, exprVal, context.expressionContext(), shadow.offsetAngle() );
3513 
3514  // data defined shadow offset units?
3515  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowOffsetUnits, exprVal, context.expressionContext() );
3516 
3517  // data defined shadow radius?
3518  dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadow.blurRadius() );
3519 
3520  // data defined shadow radius units?
3521  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowRadiusUnits, exprVal, context.expressionContext() );
3522 
3523  // data defined shadow scale? ( gui bounds to 0-2000, no upper bound here )
3524  dataDefinedValEval( DDIntPos, QgsPalLayerSettings::ShadowScale, exprVal, context.expressionContext(), shadow.scale() );
3525 
3526  // data defined shadow color?
3527  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShadowColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( shadow.color() ) );
3528 
3529  // data defined shadow blend mode?
3530  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShadowBlendMode, exprVal, context.expressionContext() );
3531 }
3532 
3533 // -------------
3534 
3535 
3537 {
3538  return layer->labelsEnabled() || layer->diagramsEnabled();
3539 }
3540 
3541 
3542 bool QgsPalLabeling::geometryRequiresPreparation( const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, const QgsGeometry &clipGeometry, bool mergeLines )
3543 {
3544  if ( geometry.isNull() )
3545  {
3546  return false;
3547  }
3548 
3549  if ( geometry.type() == QgsWkbTypes::LineGeometry && geometry.isMultipart() && mergeLines )
3550  {
3551  return true;
3552  }
3553 
3554  //requires reprojection
3555  if ( ct.isValid() && !ct.isShortCircuited() )
3556  return true;
3557 
3558  //requires rotation
3559  const QgsMapToPixel &m2p = context.mapToPixel();
3560  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
3561  return true;
3562 
3563  //requires clip
3564  if ( !clipGeometry.isNull() && !clipGeometry.boundingBox().contains( geometry.boundingBox() ) )
3565  return true;
3566 
3567  //requires fixing
3568  if ( geometry.type() == QgsWkbTypes::PolygonGeometry && !geometry.isGeosValid() )
3569  return true;
3570 
3571  return false;
3572 }
3573 
3574 QStringList QgsPalLabeling::splitToLines( const QString &text, const QString &wrapCharacter, const int autoWrapLength, const bool useMaxLineLengthWhenAutoWrapping )
3575 {
3576  QStringList multiLineSplit;
3577  if ( !wrapCharacter.isEmpty() && wrapCharacter != QLatin1String( "\n" ) )
3578  {
3579  //wrap on both the wrapchr and new line characters
3580  const QStringList lines = text.split( wrapCharacter );
3581  for ( const QString &line : lines )
3582  {
3583  multiLineSplit.append( line.split( '\n' ) );
3584  }
3585  }
3586  else
3587  {
3588  multiLineSplit = text.split( '\n' );
3589  }
3590 
3591  // apply auto wrapping to each manually created line
3592  if ( autoWrapLength != 0 )
3593  {
3594  QStringList autoWrappedLines;
3595  autoWrappedLines.reserve( multiLineSplit.count() );
3596  for ( const QString &line : qgis::as_const( multiLineSplit ) )
3597  {
3598  autoWrappedLines.append( QgsStringUtils::wordWrap( line, autoWrapLength, useMaxLineLengthWhenAutoWrapping ).split( '\n' ) );
3599  }
3600  multiLineSplit = autoWrappedLines;
3601  }
3602  return multiLineSplit;
3603 }
3604 
3605 QStringList QgsPalLabeling::splitToGraphemes( const QString &text )
3606 {
3607  QStringList graphemes;
3608  QTextBoundaryFinder boundaryFinder( QTextBoundaryFinder::Grapheme, text );
3609  int currentBoundary = -1;
3610  int previousBoundary = 0;
3611  while ( ( currentBoundary = boundaryFinder.toNextBoundary() ) > 0 )
3612  {
3613  graphemes << text.mid( previousBoundary, currentBoundary - previousBoundary );
3614  previousBoundary = currentBoundary;
3615  }
3616  return graphemes;
3617 }
3618 
3619 QgsGeometry QgsPalLabeling::prepareGeometry( const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, const QgsGeometry &clipGeometry, bool mergeLines )
3620 {
3621  if ( geometry.isNull() )
3622  {
3623  return QgsGeometry();
3624  }
3625 
3626  //don't modify the feature's geometry so that geometry based expressions keep working
3627  QgsGeometry geom = geometry;
3628 
3629  if ( geom.type() == QgsWkbTypes::LineGeometry && geom.isMultipart() && mergeLines )
3630  {
3631  geom = geom.mergeLines();
3632  }
3633 
3634  //reproject the geometry if necessary
3635  if ( ct.isValid() && !ct.isShortCircuited() )
3636  {
3637  try
3638  {
3639  geom.transform( ct );
3640  }
3641  catch ( QgsCsException &cse )
3642  {
3643  Q_UNUSED( cse )
3644  QgsDebugMsgLevel( QStringLiteral( "Ignoring feature due to transformation exception" ), 4 );
3645  return QgsGeometry();
3646  }
3647  // geometry transforms may result in nan points, remove these
3648  geom.filterVertices( []( const QgsPoint & point )->bool
3649  {
3650  return std::isfinite( point.x() ) && std::isfinite( point.y() );
3651  } );
3652  if ( QgsCurvePolygon *cp = qgsgeometry_cast< QgsCurvePolygon * >( geom.get() ) )
3653  cp->removeInvalidRings();
3654  }
3655 
3656  // Rotate the geometry if needed, before clipping
3657  const QgsMapToPixel &m2p = context.mapToPixel();
3658  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
3659  {
3660  QgsPointXY center = context.mapExtent().center();
3661  if ( geom.rotate( m2p.mapRotation(), center ) )
3662  {
3663  QgsDebugMsg( QStringLiteral( "Error rotating geometry" ).arg( geom.asWkt() ) );
3664  return QgsGeometry();
3665  }
3666  }
3667 
3668  // fix invalid polygons
3669  if ( geom.type() == QgsWkbTypes::PolygonGeometry )
3670  {
3671  if ( geom.isMultipart() )
3672  {
3673  // important -- we need to treat ever part in isolation here. We can't test the validity of the whole geometry
3674  // at once, because touching parts would result in an invalid geometry, and buffering this "dissolves" the parts.
3675  // because the actual label engine treats parts as separate entities, we aren't bound by the usual "touching parts are invalid" rule
3676  // see https://github.com/qgis/QGIS/issues/26763
3677  QVector< QgsGeometry> parts;
3678  parts.reserve( qgsgeometry_cast< const QgsGeometryCollection * >( geom.constGet() )->numGeometries() );
3679  for ( auto it = geom.const_parts_begin(); it != geom.const_parts_end(); ++it )
3680  {
3681  QgsGeometry partGeom( ( *it )->clone() );
3682  if ( !partGeom.isGeosValid() )
3683  {
3684  partGeom = partGeom.buffer( 0, 0 );
3685  }
3686  parts.append( partGeom );
3687  }
3688  geom = QgsGeometry::collectGeometry( parts );
3689  }
3690  else if ( !geom.isGeosValid() )
3691  {
3692  QgsGeometry bufferGeom = geom.buffer( 0, 0 );
3693  if ( bufferGeom.isNull() )
3694  {
3695  QgsDebugMsg( QStringLiteral( "Could not repair geometry: %1" ).arg( bufferGeom.lastError() ) );
3696  return QgsGeometry();
3697  }
3698  geom = bufferGeom;
3699  }
3700  }
3701 
3702  if ( !clipGeometry.isNull() &&
3703  ( ( qgsDoubleNear( m2p.mapRotation(), 0 ) && !clipGeometry.boundingBox().contains( geom.boundingBox() ) )
3704  || ( !qgsDoubleNear( m2p.mapRotation(), 0 ) && !clipGeometry.contains( geom ) ) ) )
3705  {
3706  QgsGeometry clipGeom = geom.intersection( clipGeometry ); // creates new geometry
3707  if ( clipGeom.isEmpty() )
3708  {
3709  return QgsGeometry();
3710  }
3711  geom = clipGeom;
3712  }
3713 
3714  return geom;
3715 }
3716 
3717 bool QgsPalLabeling::checkMinimumSizeMM( const QgsRenderContext &context, const QgsGeometry &geom, double minSize )
3718 {
3719  if ( minSize <= 0 )
3720  {
3721  return true;
3722  }
3723 
3724  if ( geom.isNull() )
3725  {
3726  return false;
3727  }
3728 
3729  QgsWkbTypes::GeometryType featureType = geom.type();
3730  if ( featureType == QgsWkbTypes::PointGeometry ) //minimum size does not apply to point features
3731  {
3732  return true;
3733  }
3734 
3735  double mapUnitsPerMM = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
3736  if ( featureType == QgsWkbTypes::LineGeometry )
3737  {
3738  double length = geom.length();
3739  if ( length >= 0.0 )
3740  {
3741  return ( length >= ( minSize * mapUnitsPerMM ) );
3742  }
3743  }
3744  else if ( featureType == QgsWkbTypes::PolygonGeometry )
3745  {
3746  double area = geom.area();
3747  if ( area >= 0.0 )
3748  {
3749  return ( std::sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
3750  }
3751  }
3752  return true; //should never be reached. Return true in this case to label such geometries anyway.
3753 }
3754 
3755 
3756 void QgsPalLabeling::dataDefinedTextStyle( QgsPalLayerSettings &tmpLyr,
3757  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3758 {
3759  QgsTextFormat format = tmpLyr.format();
3760  bool changed = false;
3761 
3762  //font color
3763  if ( ddValues.contains( QgsPalLayerSettings::Color ) )
3764  {
3765  QVariant ddColor = ddValues.value( QgsPalLayerSettings::Color );
3766  format.setColor( ddColor.value<QColor>() );
3767  changed = true;
3768  }
3769 
3770  //font transparency
3771  if ( ddValues.contains( QgsPalLayerSettings::FontOpacity ) )
3772  {
3773  format.setOpacity( ddValues.value( QgsPalLayerSettings::FontOpacity ).toDouble() / 100.0 );
3774  changed = true;
3775  }
3776 
3777  //font blend mode
3778  if ( ddValues.contains( QgsPalLayerSettings::FontBlendMode ) )
3779  {
3780  format.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::FontBlendMode ).toInt() ) );
3781  changed = true;
3782  }
3783 
3784  if ( changed )
3785  {
3786  tmpLyr.setFormat( format );
3787  }
3788 }
3789 
3790 void QgsPalLabeling::dataDefinedTextFormatting( QgsPalLayerSettings &tmpLyr,
3791  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3792 {
3793  if ( ddValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
3794  {
3795  tmpLyr.wrapChar = ddValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
3796  }
3797 
3798  if ( ddValues.contains( QgsPalLayerSettings::AutoWrapLength ) )
3799  {
3800  tmpLyr.autoWrapLength = ddValues.value( QgsPalLayerSettings::AutoWrapLength ).toInt();
3801  }
3802 
3803  if ( ddValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
3804  {
3805  QgsTextFormat format = tmpLyr.format();
3806  format.setLineHeight( ddValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble() );
3807  tmpLyr.setFormat( format );
3808  }
3809 
3810  if ( ddValues.contains( QgsPalLayerSettings::MultiLineAlignment ) )
3811  {
3812  tmpLyr.multilineAlign = static_cast< QgsPalLayerSettings::MultiLineAlign >( ddValues.value( QgsPalLayerSettings::MultiLineAlignment ).toInt() );
3813  }
3814 
3815  if ( ddValues.contains( QgsPalLayerSettings::TextOrientation ) )
3816  {
3817  QgsTextFormat format = tmpLyr.format();
3819  tmpLyr.setFormat( format );
3820  }
3821 
3822  if ( ddValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
3823  {
3824  tmpLyr.addDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
3825  }
3826 
3827  if ( tmpLyr.addDirectionSymbol )
3828  {
3829 
3830  if ( ddValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
3831  {
3832  tmpLyr.leftDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
3833  }
3834  if ( ddValues.contains( QgsPalLayerSettings::DirSymbRight ) )
3835  {
3836  tmpLyr.rightDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
3837  }
3838 
3839  if ( ddValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
3840  {
3841  tmpLyr.placeDirectionSymbol = static_cast< QgsPalLayerSettings::DirectionSymbols >( ddValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt() );
3842  }
3843 
3844  if ( ddValues.contains( QgsPalLayerSettings::DirSymbReverse ) )
3845  {
3846  tmpLyr.reverseDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbReverse ).toBool();
3847  }
3848 
3849  }
3850 }
3851 
3852 void QgsPalLabeling::dataDefinedTextBuffer( QgsPalLayerSettings &tmpLyr,
3853  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3854 {
3855  QgsTextBufferSettings buffer = tmpLyr.format().buffer();
3856  bool changed = false;
3857 
3858  //buffer draw
3859  if ( ddValues.contains( QgsPalLayerSettings::BufferDraw ) )
3860  {
3861  buffer.setEnabled( ddValues.value( QgsPalLayerSettings::BufferDraw ).toBool() );
3862  changed = true;
3863  }
3864 
3865  if ( !buffer.enabled() )
3866  {
3867  if ( changed )
3868  {
3869  QgsTextFormat format = tmpLyr.format();
3870  format.setBuffer( buffer );
3871  tmpLyr.setFormat( format );
3872  }
3873 
3874  // tmpLyr.bufferSize > 0.0 && tmpLyr.bufferTransp < 100 figured in during evaluation
3875  return; // don't continue looking for unused values
3876  }
3877 
3878  //buffer size
3879  if ( ddValues.contains( QgsPalLayerSettings::BufferSize ) )
3880  {
3881  buffer.setSize( ddValues.value( QgsPalLayerSettings::BufferSize ).toDouble() );
3882  changed = true;
3883  }
3884 
3885  //buffer opacity
3886  if ( ddValues.contains( QgsPalLayerSettings::BufferOpacity ) )
3887  {
3888  buffer.setOpacity( ddValues.value( QgsPalLayerSettings::BufferOpacity ).toDouble() / 100.0 );
3889  changed = true;
3890  }
3891 
3892  //buffer size units
3893  if ( ddValues.contains( QgsPalLayerSettings::BufferUnit ) )
3894  {
3895  QgsUnitTypes::RenderUnit bufunit = static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::BufferUnit ).toInt() );
3896  buffer.setSizeUnit( bufunit );
3897  changed = true;
3898  }
3899 
3900  //buffer color
3901  if ( ddValues.contains( QgsPalLayerSettings::BufferColor ) )
3902  {
3903  QVariant ddColor = ddValues.value( QgsPalLayerSettings::BufferColor );
3904  buffer.setColor( ddColor.value<QColor>() );
3905  changed = true;
3906  }
3907 
3908  //buffer pen join style
3909  if ( ddValues.contains( QgsPalLayerSettings::BufferJoinStyle ) )
3910  {
3911  buffer.setJoinStyle( static_cast< Qt::PenJoinStyle >( ddValues.value( QgsPalLayerSettings::BufferJoinStyle ).toInt() ) );
3912  changed = true;
3913  }
3914 
3915  //buffer blend mode
3916  if ( ddValues.contains( QgsPalLayerSettings::BufferBlendMode ) )
3917  {
3918  buffer.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::BufferBlendMode ).toInt() ) );
3919  changed = true;
3920  }
3921 
3922  if ( changed )
3923  {
3924  QgsTextFormat format = tmpLyr.format();
3925  format.setBuffer( buffer );
3926  tmpLyr.setFormat( format );
3927  }
3928 }
3929 
3930 void QgsPalLabeling::dataDefinedTextMask( QgsPalLayerSettings &tmpLyr,
3931  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3932 {
3933  if ( ddValues.isEmpty() )
3934  return;
3935 
3936  QgsTextMaskSettings mask = tmpLyr.format().mask();
3937  bool changed = false;
3938 
3939  // enabled ?
3940  if ( ddValues.contains( QgsPalLayerSettings::MaskEnabled ) )
3941  {
3942  mask.setEnabled( ddValues.value( QgsPalLayerSettings::MaskEnabled ).toBool() );
3943  changed = true;
3944  }
3945 
3946  if ( !mask.enabled() )
3947  {
3948  if ( changed )
3949  {
3950  QgsTextFormat format = tmpLyr.format();
3951  format.setMask( mask );
3952  tmpLyr.setFormat( format );
3953  }
3954 
3955  // tmpLyr.bufferSize > 0.0 && tmpLyr.bufferTransp < 100 figured in during evaluation
3956  return; // don't continue looking for unused values
3957  }
3958 
3959  // buffer size
3960  if ( ddValues.contains( QgsPalLayerSettings::MaskBufferSize ) )
3961  {
3962  mask.setSize( ddValues.value( QgsPalLayerSettings::MaskBufferSize ).toDouble() );
3963  changed = true;
3964  }
3965 
3966  // opacity
3967  if ( ddValues.contains( QgsPalLayerSettings::MaskOpacity ) )
3968  {
3969  mask.setOpacity( ddValues.value( QgsPalLayerSettings::MaskOpacity ).toDouble() / 100.0 );
3970  changed = true;
3971  }
3972 
3973  // buffer size units
3974  if ( ddValues.contains( QgsPalLayerSettings::MaskBufferUnit ) )
3975  {
3976  QgsUnitTypes::RenderUnit bufunit = static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::MaskBufferUnit ).toInt() );
3977  mask.setSizeUnit( bufunit );
3978  changed = true;
3979  }
3980 
3981  // pen join style
3982  if ( ddValues.contains( QgsPalLayerSettings::MaskJoinStyle ) )
3983  {
3984  mask.setJoinStyle( static_cast< Qt::PenJoinStyle >( ddValues.value( QgsPalLayerSettings::MaskJoinStyle ).toInt() ) );
3985  changed = true;
3986  }
3987 
3988  if ( changed )
3989  {
3990  QgsTextFormat format = tmpLyr.format();
3991  format.setMask( mask );
3992  tmpLyr.setFormat( format );
3993  }
3994 }
3995 
3996 void QgsPalLabeling::dataDefinedShapeBackground( QgsPalLayerSettings &tmpLyr,
3997  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
3998 {
3999  QgsTextBackgroundSettings background = tmpLyr.format().background();
4000  bool changed = false;
4001 
4002  //shape draw
4003  if ( ddValues.contains( QgsPalLayerSettings::ShapeDraw ) )
4004  {
4005  background.setEnabled( ddValues.value( QgsPalLayerSettings::ShapeDraw ).toBool() );
4006  changed = true;
4007  }
4008 
4009  if ( !background.enabled() )
4010  {
4011  if ( changed )
4012  {
4013  QgsTextFormat format = tmpLyr.format();
4014  format.setBackground( background );
4015  tmpLyr.setFormat( format );
4016  }
4017  return; // don't continue looking for unused values
4018  }
4019 
4020  if ( ddValues.contains( QgsPalLayerSettings::ShapeKind ) )
4021  {
4022  background.setType( static_cast< QgsTextBackgroundSettings::ShapeType >( ddValues.value( QgsPalLayerSettings::ShapeKind ).toInt() ) );
4023  changed = true;
4024  }
4025 
4026  if ( ddValues.contains( QgsPalLayerSettings::ShapeSVGFile ) )
4027  {
4028  background.setSvgFile( ddValues.value( QgsPalLayerSettings::ShapeSVGFile ).toString() );
4029  changed = true;
4030  }
4031 
4032  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeType ) )
4033  {
4034  background.setSizeType( static_cast< QgsTextBackgroundSettings::SizeType >( ddValues.value( QgsPalLayerSettings::ShapeSizeType ).toInt() ) );
4035  changed = true;
4036  }
4037 
4038  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeX ) )
4039  {
4040  QSizeF size = background.size();
4041  size.setWidth( ddValues.value( QgsPalLayerSettings::ShapeSizeX ).toDouble() );
4042  background.setSize( size );
4043  changed = true;
4044  }
4045  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeY ) )
4046  {
4047  QSizeF size = background.size();
4048  size.setHeight( ddValues.value( QgsPalLayerSettings::ShapeSizeY ).toDouble() );
4049  background.setSize( size );
4050  changed = true;
4051  }
4052 
4053  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeUnits ) )
4054  {
4055  background.setSizeUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeSizeUnits ).toInt() ) );
4056  changed = true;
4057  }
4058 
4059  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotationType ) )
4060  {
4061  background.setRotationType( static_cast< QgsTextBackgroundSettings::RotationType >( ddValues.value( QgsPalLayerSettings::ShapeRotationType ).toInt() ) );
4062  changed = true;
4063  }
4064 
4065  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotation ) )
4066  {
4067  background.setRotation( ddValues.value( QgsPalLayerSettings::ShapeRotation ).toDouble() );
4068  changed = true;
4069  }
4070 
4071  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffset ) )
4072  {
4073  background.setOffset( ddValues.value( QgsPalLayerSettings::ShapeOffset ).toPointF() );
4074  changed = true;
4075  }
4076 
4077  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffsetUnits ) )
4078  {
4079  background.setOffsetUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeOffsetUnits ).toInt() ) );
4080  changed = true;
4081  }
4082 
4083  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadii ) )
4084  {
4085  background.setRadii( ddValues.value( QgsPalLayerSettings::ShapeRadii ).toSizeF() );
4086  changed = true;
4087  }
4088 
4089  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadiiUnits ) )
4090  {
4091  background.setRadiiUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeRadiiUnits ).toInt() ) );
4092  changed = true;
4093  }
4094 
4095  if ( ddValues.contains( QgsPalLayerSettings::ShapeBlendMode ) )
4096  {
4097  background.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::ShapeBlendMode ).toInt() ) );
4098  changed = true;
4099  }
4100 
4101  if ( ddValues.contains( QgsPalLayerSettings::ShapeFillColor ) )
4102  {
4103  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeFillColor );
4104  background.setFillColor( ddColor.value<QColor>() );
4105  changed = true;
4106  }
4107 
4108  if ( ddValues.contains( QgsPalLayerSettings::ShapeStrokeColor ) )
4109  {
4110  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeStrokeColor );
4111  background.setStrokeColor( ddColor.value<QColor>() );
4112  changed = true;
4113  }
4114 
4115  if ( ddValues.contains( QgsPalLayerSettings::ShapeOpacity ) )
4116  {
4117  background.setOpacity( ddValues.value( QgsPalLayerSettings::ShapeOpacity ).toDouble() / 100.0 );
4118  changed = true;
4119  }
4120 
4121  if ( ddValues.contains( QgsPalLayerSettings::ShapeStrokeWidth ) )
4122  {
4123  background.setStrokeWidth( ddValues.value( QgsPalLayerSettings::ShapeStrokeWidth ).toDouble() );
4124  changed = true;
4125  }
4126 
4127  if ( ddValues.contains( QgsPalLayerSettings::ShapeStrokeWidthUnits ) )
4128  {
4129  background.setStrokeWidthUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeStrokeWidthUnits ).toInt() ) );
4130  changed = true;
4131  }
4132 
4133  if ( ddValues.contains( QgsPalLayerSettings::ShapeJoinStyle ) )
4134  {
4135  background.setJoinStyle( static_cast< Qt::PenJoinStyle >( ddValues.value( QgsPalLayerSettings::ShapeJoinStyle ).toInt() ) );
4136  changed = true;
4137  }
4138 
4139  if ( changed )
4140  {
4141  QgsTextFormat format = tmpLyr.format();
4142  format.setBackground( background );
4143  tmpLyr.setFormat( format );
4144  }
4145 }
4146 
4147 void QgsPalLabeling::dataDefinedDropShadow( QgsPalLayerSettings &tmpLyr,
4148  const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues )
4149 {
4150  QgsTextShadowSettings shadow = tmpLyr.format().shadow();
4151  bool changed = false;
4152 
4153  //shadow draw
4154  if ( ddValues.contains( QgsPalLayerSettings::ShadowDraw ) )
4155  {
4156  shadow.setEnabled( ddValues.value( QgsPalLayerSettings::ShadowDraw ).toBool() );
4157  changed = true;
4158  }
4159 
4160  if ( !shadow.enabled() )
4161  {
4162  if ( changed )
4163  {
4164  QgsTextFormat format = tmpLyr.format();
4165  format.setShadow( shadow );
4166  tmpLyr.setFormat( format );
4167  }
4168  return; // don't continue looking for unused values
4169  }
4170 
4171  if ( ddValues.contains( QgsPalLayerSettings::ShadowUnder ) )
4172  {
4173  shadow.setShadowPlacement( static_cast< QgsTextShadowSettings::ShadowPlacement >( ddValues.value( QgsPalLayerSettings::ShadowUnder ).toInt() ) );
4174  changed = true;
4175  }
4176 
4177  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetAngle ) )
4178  {
4179  shadow.setOffsetAngle( ddValues.value( QgsPalLayerSettings::ShadowOffsetAngle ).toInt() );
4180  changed = true;
4181  }
4182 
4183  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetDist ) )
4184  {
4185  shadow.setOffsetDistance( ddValues.value( QgsPalLayerSettings::ShadowOffsetDist ).toDouble() );
4186  changed = true;
4187  }
4188 
4189  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetUnits ) )
4190  {
4191  shadow.setOffsetUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShadowOffsetUnits ).toInt() ) );
4192  changed = true;
4193  }
4194 
4195  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadius ) )
4196  {
4197  shadow.setBlurRadius( ddValues.value( QgsPalLayerSettings::ShadowRadius ).toDouble() );
4198  changed = true;
4199  }
4200 
4201  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadiusUnits ) )
4202  {
4203  shadow.setBlurRadiusUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShadowRadiusUnits ).toInt() ) );
4204  changed = true;
4205  }
4206 
4207  if ( ddValues.contains( QgsPalLayerSettings::ShadowColor ) )
4208  {
4209  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShadowColor );
4210  shadow.setColor( ddColor.value<QColor>() );
4211  changed = true;
4212  }
4213 
4214  if ( ddValues.contains( QgsPalLayerSettings::ShadowOpacity ) )
4215  {
4216  shadow.setOpacity( ddValues.value( QgsPalLayerSettings::ShadowOpacity ).toDouble() / 100.0 );
4217  changed = true;
4218  }
4219 
4220  if ( ddValues.contains( QgsPalLayerSettings::ShadowScale ) )
4221  {
4222  shadow.setScale( ddValues.value( QgsPalLayerSettings::ShadowScale ).toInt() );
4223  changed = true;
4224  }
4225 
4226 
4227  if ( ddValues.contains( QgsPalLayerSettings::ShadowBlendMode ) )
4228  {
4229  shadow.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::ShadowBlendMode ).toInt() ) );
4230  changed = true;
4231  }
4232 
4233  if ( changed )
4234  {
4235  QgsTextFormat format = tmpLyr.format();
4236  format.setShadow( shadow );
4237  tmpLyr.setFormat( format );
4238  }
4239 }
4240 
4241 
4242 void QgsPalLabeling::drawLabelCandidateRect( pal::LabelPosition *lp, QPainter *painter, const QgsMapToPixel *xform, QList<QgsLabelCandidate> *candidates )
4243 {
4244  QgsPointXY outPt = xform->transform( lp->getX(), lp->getY() );
4245 
4246  painter->save();
4247 
4248 #if 0 // TODO: generalize some of this
4249  double w = lp->getWidth();
4250  double h = lp->getHeight();
4251  double cx = lp->getX() + w / 2.0;
4252  double cy = lp->getY() + h / 2.0;
4253  double scale = 1.0 / xform->mapUnitsPerPixel();
4254  double rotation = xform->mapRotation();
4255  double sw = w * scale;
4256  double sh = h * scale;
4257  QRectF rect( -sw / 2, -sh / 2, sw, sh );
4258 
4259  painter->translate( xform->transform( QPointF( cx, cy ) ).toQPointF() );
4260  if ( rotation )
4261  {
4262  // Only if not horizontal
4263  if ( lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT &&
4264  lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT_OVER &&
4265  lp->getFeaturePart()->getLayer()->getArrangement() != P_HORIZ )
4266  {
4267  painter->rotate( rotation );
4268  }
4269  }
4270  painter->translate( rect.bottomLeft() );
4271  painter->rotate( -lp->getAlpha() * 180 / M_PI );
4272  painter->translate( -rect.bottomLeft() );
4273 #else
4274  QgsPointXY outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
4275  QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
4276  painter->translate( QPointF( outPt.x(), outPt.y() ) );
4277  painter->rotate( -lp->getAlpha() * 180 / M_PI );
4278 #endif
4279 
4280  if ( lp->conflictsWithObstacle() )
4281  {
4282  painter->setPen( QColor( 255, 0, 0, 64 ) );
4283  }
4284  else
4285  {
4286  painter->setPen( QColor( 0, 0, 0, 64 ) );
4287  }
4288  painter->drawRect( rect );
4289  painter->restore();
4290 
4291  // save the rect
4292  rect.moveTo( outPt.x(), outPt.y() );
4293  if ( candidates )
4294  candidates->append( QgsLabelCandidate( rect, lp->cost() * 1000 ) );
4295 
4296  // show all parts of the multipart label
4297  if ( lp->nextPart() )
4298  drawLabelCandidateRect( lp->nextPart(), painter, xform, candidates );
4299 }
4300 
4302  : mLabelSearchTree( qgis::make_unique< QgsLabelSearchTree >() )
4303 {
4304 }
4305 
4307 
4308 QList<QgsLabelPosition> QgsLabelingResults::labelsAtPosition( const QgsPointXY &p ) const
4309 {
4310  QList<QgsLabelPosition> positions;
4311 
4312  QList<QgsLabelPosition *> positionPointers;
4313  if ( mLabelSearchTree )
4314  {
4315  mLabelSearchTree->label( p, positionPointers );
4316  QList<QgsLabelPosition *>::const_iterator pointerIt = positionPointers.constBegin();
4317  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
4318  {
4319  positions.push_back( QgsLabelPosition( **pointerIt ) );
4320  }
4321  }
4322 
4323  return positions;
4324 }
4325 
4326 QList<QgsLabelPosition> QgsLabelingResults::labelsWithinRect( const QgsRectangle &r ) const
4327 {
4328  QList<QgsLabelPosition> positions;
4329 
4330  QList<QgsLabelPosition *> positionPointers;
4331  if ( mLabelSearchTree )
4332  {
4333  mLabelSearchTree->labelsInRect( r, positionPointers );
4334  QList<QgsLabelPosition *>::const_iterator pointerIt = positionPointers.constBegin();
4335  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
4336  {
4337  positions.push_back( QgsLabelPosition( **pointerIt ) );
4338  }
4339  }
4340 
4341  return positions;
4342 }
4343 
4345 {
4346  mLabelSearchTree->setMapSettings( settings );
4347 }
QgsTextMaskSettings::setSize
void setSize(double size)
Sets the size of the buffer.
Definition: qgstextmasksettings.cpp:66
QgsPropertyDefinition::RenderUnits
@ RenderUnits
Render units (eg mm/pixels/map units)
Definition: qgsproperty.h:64
QgsPalLayerSettings::previewBkgrdColor
Q_DECL_DEPRECATED QColor previewBkgrdColor
Definition: qgspallabeling.h:547
QgsGeometry::fromPointXY
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
Definition: qgsgeometry.cpp:164
QgsPalLayerSettings::ShadowBlendMode
@ ShadowBlendMode
Definition: qgspallabeling.h:423
QgsPalLayerSettings::BufferBlendMode
@ BufferBlendMode
Definition: qgspallabeling.h:379
qgsEnumKeyToValue
T qgsEnumKeyToValue(const QString &key, const T &defaultValue)
Returns the value corresponding to the given key of an enum.
Definition: qgis.h:518
QgsTextBufferSettings::setSizeUnit
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the buffer size.
Definition: qgstextbuffersettings.cpp:72
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:369
QgsPalLayerSettings::ShadowRadius
@ ShadowRadius
Definition: qgspallabeling.h:417
QgsPalLayerSettings::useSubstitutions
bool useSubstitutions
True if substitutions should be applied.
Definition: qgspallabeling.h:552
QgsPalLayerSettings::preserveRotation
bool preserveRotation
True if label rotation should be preserved during label pin/unpin operations.
Definition: qgspallabeling.h:805
QgsPalLayerSettings::OffsetUnits
@ OffsetUnits
Definition: qgspallabeling.h:429
Q_GLOBAL_STATIC_WITH_ARGS
Q_GLOBAL_STATIC_WITH_ARGS(PredefinedPointPositionVector, DEFAULT_PLACEMENT_ORDER,({ QgsPalLayerSettings::TopRight, QgsPalLayerSettings::TopLeft, QgsPalLayerSettings::BottomRight, QgsPalLayerSettings::BottomLeft, QgsPalLayerSettings::MiddleRight, QgsPalLayerSettings::MiddleLeft, QgsPalLayerSettings::TopSlightlyRight, QgsPalLayerSettings::BottomSlightlyRight })) void QgsPalLayerSettings
Definition: qgspallabeling.cpp:85
QgsLabelObstacleSettings::updateDataDefinedProperties
void updateDataDefinedProperties(const QgsPropertyCollection &properties, QgsExpressionContext &context)
Updates the obstacle settings to respect any data defined properties set within the specified propert...
Definition: qgslabelobstaclesettings.cpp:31
QgsTextMaskSettings::enabled
bool enabled() const
Returns whether the mask is enabled.
Definition: qgstextmasksettings.cpp:40
QgsTextLabelFeature::setDocument
void setDocument(const QgsTextDocument &document)
Sets the document for the label.
Definition: qgstextlabelfeature.cpp:144
QgsPalLayerSettings::DistanceUnits
@ DistanceUnits
Definition: qgspallabeling.h:431
QgsPalLayerSettings::ct
QgsCoordinateTransform ct
Definition: qgspallabeling.h:1104
QgsVectorLayer::diagramsEnabled
bool diagramsEnabled() const
Returns whether the layer contains diagrams which are enabled and should be drawn.
Definition: qgsvectorlayer.cpp:738
QgsLabelObstacleSettings::setObstacleGeometry
void setObstacleGeometry(const QgsGeometry &obstacleGeom)
Sets the label's obstacle geometry, if different to the feature geometry.
Definition: qgslabelobstaclesettings.cpp:21
QgsPalLayerSettings::scaleVisibility
bool scaleVisibility
Set to true to limit label visibility to a range of scales.
Definition: qgspallabeling.h:832
QgsTextBackgroundSettings::setStrokeColor
void setStrokeColor(const QColor &color)
Sets the color used for outlining the background shape.
Definition: qgstextbackgroundsettings.cpp:242
QgsTextRendererUtils::decodeBackgroundSizeType
static QgsTextBackgroundSettings::SizeType decodeBackgroundSizeType(const QString &string)
Decodes a string representation of a background size type to a type.
Definition: qgstextrendererutils.cpp:47
QgsVectorSimplifyMethod
Definition: qgsvectorsimplifymethod.h:29
QgsPalLayerSettings::MaskEnabled
@ MaskEnabled
Whether the mask is enabled.
Definition: qgspallabeling.h:382
QgsVectorSimplifyMethod::NoSimplification
@ NoSimplification
No simplification can be applied.
Definition: qgsvectorsimplifymethod.h:52
QgsPalLayerSettings::readXml
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
Definition: qgspallabeling.cpp:910
QgsPropertyCollection::prepare
bool prepare(const QgsExpressionContext &context=QgsExpressionContext()) const override
Prepares the collection against a specified expression context.
Definition: qgspropertycollection.cpp:230
QgsStringReplacementCollection::readXml
void readXml(const QDomElement &elem)
Reads the collection state from an XML element.
Definition: qgsstringutils.cpp:741
QgsLabelFeature::setRotatedSize
void setRotatedSize(QSizeF size)
Sets an alternate label size to be used when a label rotation angle is between 45 to 135 and 235 to 3...
Definition: qgslabelfeature.h:452
QgsPalLayerSettings::distUnits
QgsUnitTypes::RenderUnit distUnits
Units the distance from feature to the label.
Definition: qgspallabeling.h:707
QgsTextFormat::buffer
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
Definition: qgstextformat.h:66
pal::LabelPosition::getY
double getY(int i=0) const
Returns the down-left y coordinate.
Definition: labelposition.cpp:353
QgsPalLayerSettings::mFeaturesToLabel
int mFeaturesToLabel
Definition: qgspallabeling.h:1109
QgsProperty::fromField
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
Definition: qgsproperty.cpp:220
QgsAbstractPropertyCollection::valueAsDouble
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.
Definition: qgspropertycollection.cpp:66
QgsPalLayerSettings::MaskBufferSize
@ MaskBufferSize
Mask buffer size.
Definition: qgspallabeling.h:383
QgsSymbolLayerUtils::encodeColor
static QString encodeColor(const QColor &color)
Definition: qgssymbollayerutils.cpp:52
QgsPalLayerSettings::Hali
@ Hali
Horizontal alignment for data defined label position (Left, Center, Right)
Definition: qgspallabeling.h:437
QgsPalLayerSettings::NumFormat
@ NumFormat
Definition: qgspallabeling.h:367
QgsTextShadowSettings::setOpacity
void setOpacity(double opacity)
Sets the shadow's opacity.
Definition: qgstextshadowsettings.cpp:161
QgsPointXY::y
double y
Definition: qgspointxy.h:48
QgsPropertyDefinition::Offset
@ Offset
2D offset
Definition: qgsproperty.h:79
QgsPalLayerSettings::DirectionSymbols
DirectionSymbols
Definition: qgspallabeling.h:302
qgspallabeling.h
QgsSymbolLayerUtils::decodeBlendMode
static QPainter::CompositionMode decodeBlendMode(const QString &s)
Definition: qgssymbollayerutils.cpp:745
QgsTextBufferSettings::setOpacity
void setOpacity(double opacity)
Sets the buffer opacity.
Definition: qgstextbuffersettings.cpp:112
QgsTextFormat::namedStyle
QString namedStyle() const
Returns the named style for the font used for rendering text (e.g., "bold").
Definition: qgstextformat.cpp:81
QgsPalLayerSettings::PerimeterCurved
@ PerimeterCurved
Arranges candidates following the curvature of a polygon's boundary. Applies to polygon layers only.
Definition: qgspallabeling.h:229
QgsGeometry::transform
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.
Definition: qgsgeometry.cpp:2836
QgsPalLayerSettings::leftDirectionSymbol
QString leftDirectionSymbol
String to use for left direction arrows.
Definition: qgspallabeling.h:603
QgsTextBackgroundSettings::setRotationType
void setRotationType(RotationType type)
Sets the method used for rotating the background shape.
Definition: qgstextbackgroundsettings.cpp:132
QgsRenderContext::convertToPainterUnits
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
Definition: qgsrendercontext.cpp:287
QgsTextShadowSettings::offsetDistance
double offsetDistance() const
Returns the distance for offsetting the position of the shadow from the text.
Definition: qgstextshadowsettings.cpp:76
QgsRenderContext::mapToPixel
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
Definition: qgsrendercontext.h:309
qgslabelsearchtree.h
QgsPalLayerSettings::registerFeature
void registerFeature(const QgsFeature &f, QgsRenderContext &context, QgsLabelFeature **labelFeature=nullptr, QgsGeometry obstacleGeometry=QgsGeometry(), const QgsSymbol *symbol=nullptr)
Register a feature for labeling.
Definition: qgspallabeling.cpp:1630
QgsPalLayerSettings::angleOffset
double angleOffset
Label rotation, in degrees clockwise.
Definition: qgspallabeling.h:802
QgsProperty
A store for object properties.
Definition: qgsproperty.h:231
QgsTextShadowSettings::setEnabled
void setEnabled(bool enabled)
Sets whether the text shadow will be drawn.
Definition: qgstextshadowsettings.cpp:51
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:605
QgsTextBufferSettings::sizeMapUnitScale
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the buffer size.
Definition: qgstextbuffersettings.cpp:77
QgsPalLayerSettings::decimals
int decimals
Number of decimal places to show for numeric labels.
Definition: qgspallabeling.h:634
QgsPalLayerSettings::OffsetXY
@ OffsetXY
Definition: qgspallabeling.h:428
QgsUnitTypes::RenderUnit
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:166
QgsPalLayerSettings::AroundPoint
@ AroundPoint
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
Definition: qgspallabeling.h:222
QgsPalLayerSettings::reverseDirectionSymbol
bool reverseDirectionSymbol
True if direction symbols should be reversed.
Definition: qgspallabeling.h:619
QgsPalLayerSettings::overrunDistance
double overrunDistance
Distance which labels are allowed to overrun past the start or end of line features.
Definition: qgspallabeling.h:746
QgsAbstractPropertyCollection::valueAsInt
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.
Definition: qgspropertycollection.cpp:77
QgsRenderContext::expressionContext
QgsExpressionContext & expressionContext()
Gets the expression context.
Definition: qgsrendercontext.h:580
QgsMapToPixel::mapUnitsPerPixel
double mapUnitsPerPixel() const
Returns current map units per pixel.
Definition: qgsmaptopixel.cpp:128
QgsGeometry::filterVertices
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...
Definition: qgsgeometry.cpp:3023
QgsReadWriteContext
Definition: qgsreadwritecontext.h:34
QgsTextBackgroundSettings::enabled
bool enabled() const
Returns whether the background is enabled.
Definition: qgstextbackgroundsettings.cpp:47
QgsPalLayerSettings::maxCurvedCharAngleOut
double maxCurvedCharAngleOut
Maximum angle between outside curved label characters (valid range -20.0 to -95.0)
Definition: qgspallabeling.h:817
QgsExpression::evalErrorString
QString evalErrorString() const
Returns evaluation error.
Definition: qgsexpression.cpp:379
QgsPalLayerSettings::FromSymbolBounds
@ FromSymbolBounds
Offset distance applies from rendered symbol bounds.
Definition: qgspallabeling.h:260
QgsPalLayerSettings::RepeatDistanceUnit
@ RepeatDistanceUnit
Definition: qgspallabeling.h:442
QgsCallout
Abstract base class for callout renderers.
Definition: qgscallout.h:46
QgsTextFormat::previewBackgroundColor
QColor previewBackgroundColor() const
Returns the background color for text previews.
Definition: qgstextformat.cpp:186
QgsPropertyDefinition::BlendMode
@ BlendMode
Blend mode.
Definition: qgsproperty.h:68
QgsPalLayerSettings::MaximumScale
@ MaximumScale
Maximum map scale (ie most "zoomed in")
Definition: qgspallabeling.h:455
QgsExpression::referencedColumns
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
Definition: qgsexpression.cpp:217
QgsPalLayerSettings::ShapeSizeType
@ ShapeSizeType
Definition: qgspallabeling.h:392
QgsPalLayerSettings::BufferSize
@ BufferSize
Definition: qgspallabeling.h:373
QgsSymbolLayerUtils::encodeMapUnitScale
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
Definition: qgssymbollayerutils.cpp:558
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
qgstextfragment.h
QgsTextBackgroundSettings::setSizeType
void setSizeType(SizeType type)
Sets the method used to determine the size of the background shape (e.g., fixed size or buffer around...
Definition: qgstextbackgroundsettings.cpp:92
QgsPalLayerSettings::Color
@ Color
Text color.
Definition: qgspallabeling.h:344
QgsTextBufferSettings::joinStyle
Qt::PenJoinStyle joinStyle() const
Returns the buffer join style.
Definition: qgstextbuffersettings.cpp:117
QgsGeometry::const_parts_end
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
Definition: qgsgeometry.cpp:1895
QgsLabelingUtils::encodePredefinedPositionOrder
static QString encodePredefinedPositionOrder(const QVector< QgsPalLayerSettings::PredefinedPointPosition > &positions)
Encodes an ordered list of predefined point label positions to a string.
Definition: qgslabelingengine.cpp:656
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsTextBackgroundSettings::setStrokeWidth
void setStrokeWidth(double width)
Sets the width of the shape's stroke (stroke).
Definition: qgstextbackgroundsettings.cpp:252
labelposition.h
QgsPalLayerSettings
Definition: qgspallabeling.h:205
QgsMapToPixel::setParameters
void setParameters(double mapUnitsPerPixel, double centerX, double centerY, int widthPixels, int heightPixels, double rotation)
Set parameters for use in transforming coordinates.
Definition: qgsmaptopixel.cpp:163
QgsPalLayerSettings::mFeatsSendingToPal
int mFeatsSendingToPal
Definition: qgspallabeling.h:1110
pal::LabelPosition::cost
double cost() const
Returns the candidate label position's geographical cost.
Definition: labelposition.h:205
qgsexpression.h
QgsPalLayerSettings::zIndex
double zIndex
Z-Index of label, where labels with a higher z-index are rendered on top of labels with a lower z-ind...
Definition: qgspallabeling.h:925
QgsPalLayerSettings::labelPerPart
bool labelPerPart
true if every part of a multi-part feature should be labeled.
Definition: qgspallabeling.h:889
QgsTextBackgroundSettings::setJoinStyle
void setJoinStyle(Qt::PenJoinStyle style)
Sets the join style used for drawing the background shape.
Definition: qgstextbackgroundsettings.cpp:282
QgsPropertyDefinition::DataTypeString
@ DataTypeString
Property requires a string value.
Definition: qgsproperty.h:93
QgsPalLayerSettings::PositionY
@ PositionY
Y-coordinate data defined label position.
Definition: qgspallabeling.h:436
QgsTextBufferSettings::setEnabled
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
Definition: qgstextbuffersettings.cpp:52
QgsTextFormat::orientation
TextOrientation orientation() const
Returns the orientation of the text.
Definition: qgstextformat.cpp:166
QgsGeometry::isMultipart
bool isMultipart() const
Returns true if WKB of the geometry is of WKBMulti* type.
Definition: qgsgeometry.cpp:377
QgsPalLayerSettings::FontOpacity
@ FontOpacity
Text opacity.
Definition: qgspallabeling.h:350
QgsTextRendererUtils::decodeBackgroundRotationType
static QgsTextBackgroundSettings::RotationType decodeBackgroundRotationType(const QString &string)
Decodes a string representation of a background rotation type to a type.
Definition: qgstextrendererutils.cpp:60
QgsPalLayerSettings::MultiLineHeight
@ MultiLineHeight
Definition: qgspallabeling.h:359
QgsPalLayerSettings::ShapeOffsetUnits
@ ShapeOffsetUnits
Definition: qgspallabeling.h:399
QgsTextRenderer::AlignCenter
@ AlignCenter
Center align.
Definition: qgstextrenderer.h:61
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:105
QgsLabelThinningSettings::setMinimumFeatureSize
void setMinimumFeatureSize(double size)
Sets the minimum feature size (in millimeters) for a feature to be labelled.
Definition: qgslabelthinningsettings.h:78
pal::LabelPosition
LabelPosition is a candidate feature label position.
Definition: labelposition.h:55
QgsPalLayerSettings::BufferUnit
@ BufferUnit
Definition: qgspallabeling.h:374
QgsCurvePolygon
Curve polygon geometry type.
Definition: qgscurvepolygon.h:34
QgsPalLayerSettings::ShapeFillColor
@ ShapeFillColor
Definition: qgspallabeling.h:405
QgsPalLayerSettings::getLabelExpression
QgsExpression * getLabelExpression()
Returns the QgsExpression for this label settings.
Definition: qgspallabeling.cpp:585
QgsRenderContext::setPainter
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
Definition: qgsrendercontext.h:475
QgsUnitTypes::RenderPoints
@ RenderPoints
Points (e.g., for font sizes)
Definition: qgsunittypes.h:172
QgsPalLayerSettings::upsidedownLabels
UpsideDownLabels upsidedownLabels
Controls whether upside down labels are displayed and how they are handled.
Definition: qgspallabeling.h:883
QgsPalLayerSettings::FromPoint
@ FromPoint
Offset distance applies from point geometry.
Definition: qgspallabeling.h:259
QgsLabelThinningSettings::setMaximumNumberLabels
void setMaximumNumberLabels(int number)
Sets the maximum number of labels which should be drawn for this layer.
Definition: qgslabelthinningsettings.h:66
QgsExpressionContext::setOriginalValueVariable
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
Definition: qgsexpressioncontext.cpp:566
qgssymbollayerutils.h
QgsTextDocument::splitLines
void splitLines(const QString &wrapCharacter, int autoWrapLength=0, bool useMaxLineLengthWhenAutoWrapping=true)
Splits lines of text in the document to separate lines, using a specified wrap character (wrapCharact...
Definition: qgstextdocument.cpp:131
QgsAbstractPropertyCollection::readXml
virtual bool readXml(const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions)
Reads property collection state from an XML element.
Definition: qgspropertycollection.cpp:108
QgsPalLayerSettings::BufferOpacity
@ BufferOpacity
Buffer opacity.
Definition: qgspallabeling.h:377
QgsPalLayerSettings::FontMaxPixel
@ FontMaxPixel
Definition: qgspallabeling.h:458
QgsFields
Definition: qgsfields.h:44
QgsPalLayerSettings::fontMaxPixelSize
int fontMaxPixelSize
Maximum pixel size for showing rendered map unit labels (1 - 10000).
Definition: qgspallabeling.h:877
QgsPalLayerSettings::CurvedCharAngleInOut
@ CurvedCharAngleInOut
Definition: qgspallabeling.h:433
QgsPalLayerSettings::Strikeout
@ Strikeout
Use strikeout.
Definition: qgspallabeling.h:345
QgsPalLayerSettings::BufferJoinStyle
@ BufferJoinStyle
Definition: qgspallabeling.h:378
QgsPalLayerSettings::xform
const QgsMapToPixel * xform
Definition: qgspallabeling.h:1103
QgsGeometry::centroid
QgsGeometry centroid() const
Returns the center of mass of a geometry.
Definition: qgsgeometry.cpp:2153
QgsProperty::asExpression
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
Definition: qgsproperty.cpp:332
QgsPalLayerSettings::yOffset
double yOffset
Vertical offset of label.
Definition: qgspallabeling.h:783
QgsPalLayerSettings::FontLimitPixel
@ FontLimitPixel
Definition: qgspallabeling.h:456
QgsTextFormat::sizeUnit
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the size of rendered text.
Definition: qgstextformat.cpp:96
qgsmarkersymbollayer.h
QgsTextBackgroundSettings::fillColor
QColor fillColor() const
Returns the color used for filing the background shape.
Definition: qgstextbackgroundsettings.cpp:227
QgsTextBackgroundSettings
Definition: qgstextbackgroundsettings.h:45
QgsPalLayerSettings::MiddleRight
@ MiddleRight
Label on right of point.
Definition: qgspallabeling.h:243
QgsFeature::geometry
QgsGeometry geometry
Definition: qgsfeature.h:71
QgsVectorSimplifyMethod::simplifyAlgorithm
SimplifyAlgorithm simplifyAlgorithm() const
Gets the local simplification algorithm of the vector layer managed.
Definition: qgsvectorsimplifymethod.h:79
QgsPalLabeling::prepareGeometry
static QgsGeometry prepareGeometry(const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, const QgsGeometry &clipGeometry=QgsGeometry(), bool mergeLines=false)
Prepares a geometry for registration with PAL.
Definition: qgspallabeling.cpp:3619
QgsUnitTypes::RenderPercentage
@ RenderPercentage
Percentage of another measurement (e.g., canvas size, feature size)
Definition: qgsunittypes.h:171
QgsRenderContext::convertToMapUnits
double convertToMapUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to map units.
Definition: qgsrendercontext.cpp:349
QgsPalLayerSettings::TopRight
@ TopRight
Label on top-right of point.
Definition: qgspallabeling.h:241
QgsTextShadowSettings::scale
int scale() const
Returns the scaling used for the drop shadow (in percentage of original size).
Definition: qgstextshadowsettings.cpp:166
QgsPalLayerSettings::OffsetType
OffsetType
Behavior modifier for label offset and distance, only applies in some label placement modes.
Definition: qgspallabeling.h:257
QgsTextBufferSettings::opacity
double opacity() const
Returns the buffer opacity.
Definition: qgstextbuffersettings.cpp:107
QgsPalLayerSettings::addDirectionSymbol
bool addDirectionSymbol
If true, '<' or '>' (or custom strings set via leftDirectionSymbol and rightDirectionSymbol) will be ...
Definition: qgspallabeling.h:596
QgsLabelThinningSettings::minimumFeatureSize
double minimumFeatureSize() const
Returns the minimum feature size (in millimeters) for a feature to be labelled.
Definition: qgslabelthinningsettings.h:72
QgsPalLayerSettings::QuadrantPosition
QuadrantPosition
Definition: qgspallabeling.h:282
QgsRenderContext
Definition: qgsrendercontext.h:57
QgsPalLayerSettings::mergeLines
bool mergeLines
true if connected line features with identical label text should be merged prior to generating label ...
Definition: qgspallabeling.h:895
QgsPalLayerSettings::PredefinedPositionOrder
@ PredefinedPositionOrder
Definition: qgspallabeling.h:444
QgsTextBackgroundSettings::SizeType
SizeType
Methods for determining the background shape size.
Definition: qgstextbackgroundsettings.h:65
QgsPropertyDefinition::DataTypeNumeric
@ DataTypeNumeric
Property requires a numeric value.
Definition: qgsproperty.h:100
QgsTextFormat::shadow
QgsTextShadowSettings & shadow()
Returns a reference to the text drop shadow settings.
Definition: qgstextformat.h:104
QgsPropertyDefinition::Double
@ Double
Double value (including negative values)
Definition: qgsproperty.h:58
QgsPalLayerSettings::geometryGeneratorType
QgsWkbTypes::GeometryType geometryGeneratorType
The type of the result geometry of the geometry generator.
Definition: qgspallabeling.h:931
qgsunittypes.h
QgsTextMaskSettings::setSizeUnit
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the buffer size.
Definition: qgstextmasksettings.cpp:76
QgsGeometry::rotate
OperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Definition: qgsgeometry.cpp:803
QgsTextFormat::mask
QgsTextMaskSettings & mask()
Returns a reference to the masking settings.
Definition: qgstextformat.h:123
QgsUnitTypes::RenderMillimeters
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:168
QgsPropertyDefinition::PenJoinStyle
@ PenJoinStyle
Pen join style.
Definition: qgsproperty.h:67
QgsTextBackgroundSettings::offset
QPointF offset() const
Returns the offset used for drawing the background shape.
Definition: qgstextbackgroundsettings.cpp:147
QgsTextRendererUtils::decodeTextOrientation
static QgsTextFormat::TextOrientation decodeTextOrientation(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a text orientation.
Definition: qgstextrendererutils.cpp:112
qgsdiagram.h
QgsGeometry::const_parts_begin
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
Definition: qgsgeometry.cpp:1888
QgsPalLayerSettings::Vali
@ Vali
Vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top)
Definition: qgspallabeling.h:438
QgsPalLayerSettings::overrunDistanceMapUnitScale
QgsMapUnitScale overrunDistanceMapUnitScale
Map unit scale for label overrun distance.
Definition: qgspallabeling.h:762
QgsTextBackgroundSettings::opacity
double opacity() const
Returns the background shape's opacity.
Definition: qgstextbackgroundsettings.cpp:207
QgsLabelObstacleSettings::setIsObstacle
void setIsObstacle(bool isObstacle)
Sets whether features are obstacles to labels of other layers.
Definition: qgslabelobstaclesettings.h:71
QgsSymbolLayerUtils::toSize
static QSizeF toSize(const QVariant &value, bool *ok=nullptr)
Converts a value to a size.
Definition: qgssymbollayerutils.cpp:507
QgsPalLayerSettings::MultiLineAlign
MultiLineAlign
Definition: qgspallabeling.h:309
layer.h
QgsPalLayerSettings::ShadowOffsetUnits
@ ShadowOffsetUnits
Definition: qgspallabeling.h:416
QgsPalLayerSettings::ShapeOpacity
@ ShapeOpacity
Shape opacity.
Definition: qgspallabeling.h:403
QgsTextRendererUtils::encodeTextOrientation
static QString encodeTextOrientation(QgsTextFormat::TextOrientation orientation)
Encodes a text orientation.
Definition: qgstextrendererutils.cpp:98
QgsPalLayerSettings::ShapeBlendMode
@ ShapeBlendMode
Definition: qgspallabeling.h:404
QgsPalLayerSettings::minimumScale
double minimumScale
The minimum map scale (i.e.
Definition: qgspallabeling.h:856
QgsPalLayerSettings::substitutions
QgsStringReplacementCollection substitutions
Substitution collection for automatic text substitution with labels.
Definition: qgspallabeling.h:550
QgsLabelObstacleSettings
Definition: qgslabelobstaclesettings.h:34
QgsSymbol
Definition: qgssymbol.h:63
QgsProperty::fromExpression
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
Definition: qgsproperty.cpp:212
QgsStringUtils::Capitalization
Capitalization
Capitalization options.
Definition: qgsstringutils.h:188
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
qgsfontutils.h
QgsTextBackgroundSettings::setBlendMode
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the background shape.
Definition: qgstextbackgroundsettings.cpp:222
QgsPalLayerSettings::referencedFields
QSet< QString > referencedFields(const QgsRenderContext &context) const
Returns all field names referenced by the configuration (e.g.
Definition: qgspallabeling.cpp:491
QgsRenderContext::scaleFactor
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Definition: qgsrendercontext.h:317
QgsMapToPixel::toMapCoordinates
QgsPointXY toMapCoordinates(int x, int y) const
Transform device coordinates to map (world) coordinates.
Definition: qgsmaptopixel.cpp:108
QgsPalLayerSettings::DirSymbDraw
@ DirSymbDraw
Definition: qgspallabeling.h:362
QgsPalLayerSettings::DirSymbReverse
@ DirSymbReverse
Definition: qgspallabeling.h:366
QgsPalLayerSettings::Line
@ Line
Arranges candidates parallel to a generalised line representing the feature or parallel to a polygon'...
Definition: qgspallabeling.h:224
pal::LabelPosition::nextPart
LabelPosition * nextPart() const
Returns the next part of this label position (i.e.
Definition: labelposition.h:276
QgsCoordinateTransform::isValid
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Definition: qgscoordinatetransform.cpp:876
QgsTextLabelFeature
Class that adds extra information to QgsLabelFeature for text labels.
Definition: qgspalgeometry.h:30
QgsPropertyCollection::clear
void clear() override
Removes all properties from the collection.
Definition: qgspropertycollection.cpp:168
QgsTextShadowSettings::blurRadius
double blurRadius() const
Returns the blur radius for the shadow.
Definition: qgstextshadowsettings.cpp:116
QgsPalLayerSettings::maximumScale
double maximumScale
The maximum map scale (i.e.
Definition: qgspallabeling.h:844
QgsPalLayerSettings::MultiLineWrapChar
@ MultiLineWrapChar
Definition: qgspallabeling.h:357
QgsPalLayerSettings::TopSlightlyRight
@ TopSlightlyRight
Label on top of point, slightly right of center.
Definition: qgspallabeling.h:240
QgsPalLayerSettings::LabelDistance
@ LabelDistance
Definition: qgspallabeling.h:430
QgsSymbolLayerUtils::decodeColor
static QColor decodeColor(const QString &str)
Definition: qgssymbollayerutils.cpp:57
QgsTextShadowSettings::setOffsetDistance
void setOffsetDistance(double distance)
Sets the distance for offsetting the position of the shadow from the text.
Definition: qgstextshadowsettings.cpp:81
QgsTextBackgroundSettings::setOpacity
void setOpacity(double opacity)
Sets the background shape's opacity.
Definition: qgstextbackgroundsettings.cpp:212
QgsGeometry::buffer
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...
Definition: qgsgeometry.cpp:1919
QgsTextBackgroundSettings::setStrokeWidthUnit
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape's stroke width.
Definition: qgstextbackgroundsettings.cpp:262
QgsTextBackgroundSettings::sizeType
SizeType sizeType() const
Returns the method used to determine the size of the background shape (e.g., fixed size or buffer aro...
Definition: qgstextbackgroundsettings.cpp:87
QgsPalLayerSettings::SymbolLeftRight
@ SymbolLeftRight
Place direction symbols on left/right of label.
Definition: qgspallabeling.h:304
QgsPalLabeling::staticWillUseLayer
static bool staticWillUseLayer(QgsVectorLayer *layer)
called to find out whether the layer is used for labeling
Definition: qgspallabeling.cpp:3536
Q_GLOBAL_STATIC
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QgsRectangle
Definition: qgsrectangle.h:41
QgsPalLayerSettings::dist
double dist
Distance from feature to the label.
Definition: qgspallabeling.h:700
QgsPalLayerSettings::OverPoint
@ OverPoint
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point....
Definition: qgspallabeling.h:223
QgsWkbTypes::PolygonGeometry
@ PolygonGeometry
Definition: qgswkbtypes.h:143
QgsTextShadowSettings::setOffsetAngle
void setOffsetAngle(int angle)
Sets the angle for offsetting the position of the shadow from the text.
Definition: qgstextshadowsettings.cpp:71
pal::LabelPosition::getFeaturePart
FeaturePart * getFeaturePart() const
Returns the feature corresponding to this labelposition.
Definition: labelposition.cpp:371
QgsPalLayerSettings::Show
@ Show
Definition: qgspallabeling.h:465
QgsTextShadowSettings::enabled
bool enabled() const
Returns whether the shadow is enabled.
Definition: qgstextshadowsettings.cpp:46
QgsLabelObstacleSettings::setType
void setType(ObstacleType type)
Controls how features act as obstacles for labels.
Definition: qgslabelobstaclesettings.h:119
QgsTextMaskSettings::size
double size() const
Returns the size of the buffer.
Definition: qgstextmasksettings.cpp:61
QgsGeometry::mergeLines
QgsGeometry mergeLines() const
Merges any connected lines in a LineString/MultiLineString geometry and converts them to single line ...
Definition: qgsgeometry.cpp:2436
QgsProperty::setActive
void setActive(bool active)
Sets whether the property is currently active.
Definition: qgsproperty.cpp:271
QgsLabelCandidate
Definition: qgspallabeling.h:1207
QgsPalLayerSettings::ShapeKind
@ ShapeKind
Definition: qgspallabeling.h:390
pal
Definition: qgsdiagramrenderer.h:49
QgsLabelPosition
Definition: qgspallabeling.h:85
QgsGeometry::intersection
QgsGeometry intersection(const QgsGeometry &geometry) const
Returns a geometry representing the points shared by this geometry and other.
Definition: qgsgeometry.cpp:2395
QgsGeometry::fromRect
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
Definition: qgsgeometry.cpp:229
QgsLabelingUtils::decodePredefinedPositionOrder
static QVector< QgsPalLayerSettings::PredefinedPointPosition > decodePredefinedPositionOrder(const QString &positionString)
Decodes a string to an ordered list of predefined point label positions.
Definition: qgslabelingengine.cpp:705
QgsPropertyCollection::property
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
Definition: qgspropertycollection.cpp:204
QgsPalLayerSettings::QuadrantAboveRight
@ QuadrantAboveRight
Definition: qgspallabeling.h:286
QgsPropertyDefinition::String
@ String
Any string value.
Definition: qgsproperty.h:62
QgsTextFormat::VerticalOrientation
@ VerticalOrientation
Horizontally oriented text.
Definition: qgstextformat.h:46
QgsTextFormat::color
QColor color() const
Returns the color that text will be rendered in.
Definition: qgstextformat.cpp:126
QgsTextRenderer::textWidth
static double textWidth(const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, QFontMetricsF *fontMetrics=nullptr)
Returns the width of a text based on a given format.
Definition: qgstextrenderer.cpp:448
QgsTextRenderer::Rect
@ Rect
Text within rectangle draw mode.
Definition: qgstextrenderer.h:43
qgsapplication.h
QgsUnitTypes::decodeRenderUnit
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
Definition: qgsunittypes.cpp:2900
problem.h
QgsPalLayerSettings::ObstacleFactor
@ ObstacleFactor
Definition: qgspallabeling.h:460
geos::unique_ptr
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:79
QgsPropertyDefinition::Rotation
@ Rotation
Rotation (value between 0-360 degrees)
Definition: qgsproperty.h:61
QgsPalLayerSettings::FontWordSpacing
@ FontWordSpacing
Word spacing.
Definition: qgspallabeling.h:353
QgsTextBackgroundSettings::setRadiiUnit
void setRadiiUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape's radii.
Definition: qgstextbackgroundsettings.cpp:192
QgsPalLayerSettings::repeatDistance
double repeatDistance
Distance for repeating labels for a single feature.
Definition: qgspallabeling.h:724
QgsPalLayerSettings::formatNumbers
bool formatNumbers
Set to true to format numeric label text as numbers (e.g.
Definition: qgspallabeling.h:627
QgsPalLayerSettings::PolygonLabelOutside
@ PolygonLabelOutside
Whether labels outside a polygon feature are permitted, or should be forced (since QGIS 3....
Definition: qgspallabeling.h:448
QgsPalLayerSettings::QuadrantBelowLeft
@ QuadrantBelowLeft
Definition: qgspallabeling.h:290
QgsSymbolLayerUtils::toPoint
static QPointF toPoint(const QVariant &value, bool *ok=nullptr)
Converts a value to a point.
Definition: qgssymbollayerutils.cpp:443
QgsPalLayerSettings::labelSettingsPreviewPixmap
static QPixmap labelSettingsPreviewPixmap(const QgsPalLayerSettings &settings, QSize size, const QString &previewText=QString(), int padding=0)
Returns a pixmap preview for label settings.
Definition: qgspallabeling.cpp:1258
QgsTextFormat::setBackground
void setBackground(const QgsTextBackgroundSettings &backgroundSettings)
Sets the text's background settings.q.
Definition: qgstextformat.h:98
QgsPalLayerSettings::repeatDistanceUnit
QgsUnitTypes::RenderUnit repeatDistanceUnit
Units for repeating labels for a single feature.
Definition: qgspallabeling.h:731
QgsPalLayerSettings::xOffset
double xOffset
Horizontal offset of label.
Definition: qgspallabeling.h:775
qgsvectorlayerdiagramprovider.h
QgsPalLayerSettings::displayAll
bool displayAll
If true, all features will be labelled even when overlaps occur.
Definition: qgspallabeling.h:880
QgsPalLayerSettings::MaxScale
@ MaxScale
Max scale (deprecated, for old project compatibility only)
Definition: qgspallabeling.h:454
QgsTextRenderer::drawText
static void drawText(const QRectF &rect, double rotation, HAlignment alignment, const QStringList &textLines, QgsRenderContext &context, const QgsTextFormat &format, bool drawAsOutlines=true)
Draws text within a rectangle using the specified settings.
Definition: qgstextrenderer.cpp:45
Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:752
QgsTextBackgroundSettings::sizeMapUnitScale
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the shape size.
Definition: qgstextbackgroundsettings.cpp:117
QgsPalLayerSettings::OrderedPositionsAroundPoint
@ OrderedPositionsAroundPoint
Candidates are placed in predefined positions around a point. Preference is given to positions with g...
Definition: qgspallabeling.h:228
QgsFeature::id
QgsFeatureId id
Definition: qgsfeature.h:68
QgsRenderContext::coordinateTransform
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Definition: qgsrendercontext.h:229
QgsPoint::y
double y
Definition: qgspoint.h:59
QgsTextFormat
Definition: qgstextformat.h:38
QgsTextBackgroundSettings::RotationType
RotationType
Methods for determining the rotation of the background shape.
Definition: qgstextbackgroundsettings.h:75
QgsTextFormat::setColor
void setColor(const QColor &color)
Sets the color that text will be rendered in.
Definition: qgstextformat.cpp:131
QgsPalLayerSettings::predefinedPositionOrder
QVector< PredefinedPointPosition > predefinedPositionOrder
Ordered list of predefined label positions for points.
Definition: qgspallabeling.h:688
QgsLabelThinningSettings::updateDataDefinedProperties
void updateDataDefinedProperties(const QgsPropertyCollection &properties, QgsExpressionContext &context)
Updates the thinning settings to respect any data defined properties set within the specified propert...
Definition: qgslabelthinningsettings.cpp:22
QgsLabelObstacleSettings::setFactor
void setFactor(double factor)
Sets the obstacle factor, where 1.0 = default, < 1.0 more likely to be covered by labels,...
Definition: qgslabelobstaclesettings.h:97
QgsPalLayerSettings::MultiLineAlignment
@ MultiLineAlignment
Definition: qgspallabeling.h:360
QgsPropertiesDefinition
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
Definition: qgspropertycollection.h:29
feature.h
QgsTextBackgroundSettings::size
QSizeF size() const
Returns the size of the background shape.
Definition: qgstextbackgroundsettings.cpp:97
QgsPalLayerSettings::Placement
Placement
Placement modes which determine how label candidates are generated for a feature.
Definition: qgspallabeling.h:220
QgsTextFormat::sizeMapUnitScale
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the size.
Definition: qgstextformat.cpp:106
QgsPalLayerSettings::fontLimitPixelSize
bool fontLimitPixelSize
true if label sizes should be limited by pixel size.
Definition: qgspallabeling.h:863
QgsGeos::asGeos
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
QgsExpression::hasEvalError
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
Definition: qgsexpression.cpp:374
QgsPalLayerSettings::Curved
@ Curved
Arranges candidates following the curvature of a line feature. Applies to line layers only.
Definition: qgspallabeling.h:225
QgsPalLayerSettings::DirSymbRight
@ DirSymbRight
Definition: qgspallabeling.h:364
qgstextrendererutils.h
QgsTextShadowSettings::ShadowPlacement
ShadowPlacement
Placement positions for text shadow.
Definition: qgstextshadowsettings.h:44
QgsTextBackgroundSettings::setOffset
void setOffset(QPointF offset)
Sets the offset used for drawing the background shape.
Definition: qgstextbackgroundsettings.cpp:152
QgsCsException
Definition: qgsexception.h:65
QgsTextBackgroundSettings::joinStyle
Qt::PenJoinStyle joinStyle() const
Returns the join style used for drawing the background shape.
Definition: qgstextbackgroundsettings.cpp:277
QgsPalLayerSettings::FontLetterSpacing
@ FontLetterSpacing
Letter spacing.
Definition: qgspallabeling.h:352
QgsCoordinateTransform::isShortCircuited
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent.
Definition: qgscoordinatetransform.cpp:881
QgsPalLabeling::geometryRequiresPreparation
static bool geometryRequiresPreparation(const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, const QgsGeometry &clipGeometry=QgsGeometry(), bool mergeLines=false)
Checks whether a geometry requires preparation before registration with PAL.
Definition: qgspallabeling.cpp:3542
QgsUnitTypes::encodeUnit
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
Definition: qgsunittypes.cpp:122
QgsStringUtils::MixedCase
@ MixedCase
Mixed case, ie no change.
Definition: qgsstringutils.h:190
QgsMapSettings::transformContext
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context, which stores various information regarding which datum tran...
Definition: qgsmapsettings.cpp:400
QgsMapSettings::rotation
double rotation() const
Returns the rotation of the resulting map image, in degrees clockwise.
Definition: qgsmapsettings.cpp:101
QgsPalLayerSettings::Bold
@ Bold
Use bold style.
Definition: qgspallabeling.h:341
QgsTextRendererUtils::decodeShadowPlacementType
static QgsTextShadowSettings::ShadowPlacement decodeShadowPlacementType(const QString &string)
Decodes a string representation of a shadow placement type to a type.
Definition: qgstextrendererutils.cpp:77
QgsFeature::setGeometry
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:137
QgsPalLayerSettings::QuadrantLeft
@ QuadrantLeft
Definition: qgspallabeling.h:287
QgsPalLayerSettings::QgsPalLayerSettings
QgsPalLayerSettings()
Definition: qgspallabeling.cpp:261
QgsPalLayerSettings::stopRender
void stopRender(QgsRenderContext &context)
Finalises the label settings after use.
Definition: qgspallabeling.cpp:550
QgsLabelingUtils::encodeLinePlacementFlags
static QString encodeLinePlacementFlags(QgsLabeling::LinePlacem