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