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