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