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