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