QGIS API Documentation  2.17.0-Master (6f7b933)
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 
22 #include <list>
23 
24 #include <pal/pal.h>
25 #include <pal/feature.h>
26 #include <pal/layer.h>
27 #include <pal/palexception.h>
28 #include <pal/problem.h>
29 #include <pal/labelposition.h>
30 
31 #include <cmath>
32 
33 #include <QApplication>
34 #include <QByteArray>
35 #include <QString>
36 #include <QFontMetrics>
37 #include <QTime>
38 #include <QPainter>
39 
40 #include "diagram/qgsdiagram.h"
41 #include "qgsdiagramrendererv2.h"
42 #include "qgsfontutils.h"
43 #include "qgslabelsearchtree.h"
44 #include "qgsexpression.h"
45 #include "qgsdatadefined.h"
46 #include "qgslabelingenginev2.h"
47 #include "qgsvectorlayerlabeling.h"
48 
49 #include <qgslogger.h>
50 #include <qgsvectorlayer.h>
51 #include <qgsmaplayerregistry.h>
52 #include <qgsvectordataprovider.h>
55 #include <qgsgeometry.h>
56 #include <qgsmaprenderer.h>
57 #include <qgsmarkersymbollayerv2.h>
58 #include <qgsproject.h>
59 #include "qgssymbolv2.h"
60 #include "qgssymbollayerv2utils.h"
62 #include <QMessageBox>
63 
64 
65 Q_GUI_EXPORT extern int qt_defaultDpiX();
66 Q_GUI_EXPORT extern int qt_defaultDpiY();
67 
68 static void _fixQPictureDPI( QPainter* p )
69 {
70  // QPicture makes an assumption that we drawing to it with system DPI.
71  // Then when being drawn, it scales the painter. The following call
72  // negates the effect. There is no way of setting QPicture's DPI.
73  // See QTBUG-20361
74  p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
75  static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
76 }
77 
78 
79 using namespace pal;
80 
81 // -------------
82 
83 /* ND: Default point label position priority. These are set to match variants of the ideal placement priority described
84  in "Making Maps", Krygier & Wood (2011) (p216),
85  "Elements of Cartography", Robinson et al (1995)
86  and "Designing Better Maps", Brewer (2005) (p76)
87  Note that while they agree on positions 1-4, 5-8 are more contentious so I've selected these placements
88  based on my preferences, and to follow Krygier and Wood's placements more closer. (I'm not going to disagree
89  with Denis Wood on anything cartography related...!)
90 */
100 //debugging only - don't use these placements by default
101 /* << QgsPalLayerSettings::TopSlightlyLeft
102 << QgsPalLayerSettings::BottomSlightlyLeft;
103 << QgsPalLayerSettings::TopMiddle
104 << QgsPalLayerSettings::BottomMiddle;*/
105 
107  : upsidedownLabels( Upright )
108  , mCurFeat( nullptr )
109  , xform( nullptr )
110  , ct( nullptr )
111  , extentGeom( nullptr )
112  , mFeaturesToLabel( 0 )
113  , mFeatsSendingToPal( 0 )
114  , mFeatsRegPal( 0 )
115  , expression( nullptr )
116 {
117  enabled = false;
118  drawLabels = true;
119  isExpression = false;
120  fieldIndex = 0;
121 
122  // text style
124  fontSizeInMapUnits = false;
125  textColor = Qt::black;
126  textTransp = 0;
127  blendMode = QPainter::CompositionMode_SourceOver;
128  previewBkgrdColor = Qt::white;
129  // font processing info
130  mTextFontFound = true;
132 
133  // text formatting
134  wrapChar = "";
135  multilineHeight = 1.0;
137  addDirectionSymbol = false;
138  leftDirectionSymbol = QString( "<" );
139  rightDirectionSymbol = QString( ">" );
140  reverseDirectionSymbol = false;
142  formatNumbers = false;
143  decimals = 3;
144  plusSign = false;
145 
146  // text buffer
147  bufferDraw = false;
148  bufferSize = 1.0;
149  bufferSizeInMapUnits = false;
150  bufferColor = Qt::white;
151  bufferTransp = 0;
152  bufferNoFill = false;
153  bufferJoinStyle = Qt::BevelJoin;
154  bufferBlendMode = QPainter::CompositionMode_SourceOver;
155 
156  // shape background
157  shapeDraw = false;
159  shapeSVGFile = QString();
161  shapeSize = QPointF( 0.0, 0.0 );
162  shapeSizeUnits = MM;
164  shapeRotation = 0.0;
165  shapeOffset = QPointF( 0.0, 0.0 );
167  shapeRadii = QPointF( 0.0, 0.0 );
169  shapeFillColor = Qt::white;
170  shapeBorderColor = Qt::darkGray;
171  shapeBorderWidth = 0.0;
173  shapeJoinStyle = Qt::BevelJoin;
174  shapeTransparency = 0;
175  shapeBlendMode = QPainter::CompositionMode_SourceOver;
176 
177  // drop shadow
178  shadowDraw = false;
180  shadowOffsetAngle = 135;
181  shadowOffsetDist = 1.0;
183  shadowOffsetGlobal = true;
184  shadowRadius = 1.5;
186  shadowRadiusAlphaOnly = false;
187  shadowTransparency = 30;
188  shadowScale = 100;
189  shadowColor = Qt::black;
190  shadowBlendMode = QPainter::CompositionMode_Multiply;
191 
192  // placement
195  centroidWhole = false;
196  centroidInside = false;
197  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
198  fitInPolygonOnly = false;
200  xOffset = 0;
201  yOffset = 0;
202  labelOffsetInMapUnits = true;
203  dist = 0;
204  distInMapUnits = false;
206  angleOffset = 0;
207  preserveRotation = true;
208  maxCurvedCharAngleIn = 25.0;
209  maxCurvedCharAngleOut = -25.0;
210  priority = 5;
211  repeatDistance = 0;
213 
214  // rendering
215  scaleVisibility = false;
216  scaleMin = 1;
217  scaleMax = 10000000;
218  fontLimitPixelSize = false;
219  fontMinPixelSize = 0; //trigger to turn it on by default for map unit labels
220  fontMaxPixelSize = 10000;
221  displayAll = false;
223 
224  labelPerPart = false;
225  mergeLines = false;
226  minFeatureSize = 0.0;
227  limitNumLabels = false;
228  maxNumLabels = 2000;
229  obstacle = true;
230  obstacleFactor = 1.0;
232  zIndex = 0.0;
233 
234  // scale factors
235  vectorScaleFactor = 1.0;
236  rasterCompressFactor = 1.0;
237 
238  // data defined string and old-style index values
239  // NOTE: in QPair use -1 for second value (other values are for old-style layer properties migration)
240 
241  // text style
242  mDataDefinedNames.insert( Size, QPair<QString, int>( "Size", 0 ) );
243  mDataDefinedNames.insert( Bold, QPair<QString, int>( "Bold", 1 ) );
244  mDataDefinedNames.insert( Italic, QPair<QString, int>( "Italic", 2 ) );
245  mDataDefinedNames.insert( Underline, QPair<QString, int>( "Underline", 3 ) );
246  mDataDefinedNames.insert( Color, QPair<QString, int>( "Color", 4 ) );
247  mDataDefinedNames.insert( Strikeout, QPair<QString, int>( "Strikeout", 5 ) );
248  mDataDefinedNames.insert( Family, QPair<QString, int>( "Family", 6 ) );
249  mDataDefinedNames.insert( FontStyle, QPair<QString, int>( "FontStyle", -1 ) );
250  mDataDefinedNames.insert( FontSizeUnit, QPair<QString, int>( "FontSizeUnit", -1 ) );
251  mDataDefinedNames.insert( FontTransp, QPair<QString, int>( "FontTransp", 18 ) );
252  mDataDefinedNames.insert( FontCase, QPair<QString, int>( "FontCase", -1 ) );
253  mDataDefinedNames.insert( FontLetterSpacing, QPair<QString, int>( "FontLetterSpacing", -1 ) );
254  mDataDefinedNames.insert( FontWordSpacing, QPair<QString, int>( "FontWordSpacing", -1 ) );
255  mDataDefinedNames.insert( FontBlendMode, QPair<QString, int>( "FontBlendMode", -1 ) );
256 
257  // text formatting
258  mDataDefinedNames.insert( MultiLineWrapChar, QPair<QString, int>( "MultiLineWrapChar", -1 ) );
259  mDataDefinedNames.insert( MultiLineHeight, QPair<QString, int>( "MultiLineHeight", -1 ) );
260  mDataDefinedNames.insert( MultiLineAlignment, QPair<QString, int>( "MultiLineAlignment", -1 ) );
261  mDataDefinedNames.insert( DirSymbDraw, QPair<QString, int>( "DirSymbDraw", -1 ) );
262  mDataDefinedNames.insert( DirSymbLeft, QPair<QString, int>( "DirSymbLeft", -1 ) );
263  mDataDefinedNames.insert( DirSymbRight, QPair<QString, int>( "DirSymbRight", -1 ) );
264  mDataDefinedNames.insert( DirSymbPlacement, QPair<QString, int>( "DirSymbPlacement", -1 ) );
265  mDataDefinedNames.insert( DirSymbReverse, QPair<QString, int>( "DirSymbReverse", -1 ) );
266  mDataDefinedNames.insert( NumFormat, QPair<QString, int>( "NumFormat", -1 ) );
267  mDataDefinedNames.insert( NumDecimals, QPair<QString, int>( "NumDecimals", -1 ) );
268  mDataDefinedNames.insert( NumPlusSign, QPair<QString, int>( "NumPlusSign", -1 ) );
269 
270  // text buffer
271  mDataDefinedNames.insert( BufferDraw, QPair<QString, int>( "BufferDraw", -1 ) );
272  mDataDefinedNames.insert( BufferSize, QPair<QString, int>( "BufferSize", 7 ) );
273  mDataDefinedNames.insert( BufferUnit, QPair<QString, int>( "BufferUnit", -1 ) );
274  mDataDefinedNames.insert( BufferColor, QPair<QString, int>( "BufferColor", 8 ) );
275  mDataDefinedNames.insert( BufferTransp, QPair<QString, int>( "BufferTransp", 19 ) );
276  mDataDefinedNames.insert( BufferJoinStyle, QPair<QString, int>( "BufferJoinStyle", -1 ) );
277  mDataDefinedNames.insert( BufferBlendMode, QPair<QString, int>( "BufferBlendMode", -1 ) );
278 
279  // background
280  mDataDefinedNames.insert( ShapeDraw, QPair<QString, int>( "ShapeDraw", -1 ) );
281  mDataDefinedNames.insert( ShapeKind, QPair<QString, int>( "ShapeKind", -1 ) );
282  mDataDefinedNames.insert( ShapeSVGFile, QPair<QString, int>( "ShapeSVGFile", -1 ) );
283  mDataDefinedNames.insert( ShapeSizeType, QPair<QString, int>( "ShapeSizeType", -1 ) );
284  mDataDefinedNames.insert( ShapeSizeX, QPair<QString, int>( "ShapeSizeX", -1 ) );
285  mDataDefinedNames.insert( ShapeSizeY, QPair<QString, int>( "ShapeSizeY", -1 ) );
286  mDataDefinedNames.insert( ShapeSizeUnits, QPair<QString, int>( "ShapeSizeUnits", -1 ) );
287  mDataDefinedNames.insert( ShapeRotationType, QPair<QString, int>( "ShapeRotationType", -1 ) );
288  mDataDefinedNames.insert( ShapeRotation, QPair<QString, int>( "ShapeRotation", -1 ) );
289  mDataDefinedNames.insert( ShapeOffset, QPair<QString, int>( "ShapeOffset", -1 ) );
290  mDataDefinedNames.insert( ShapeOffsetUnits, QPair<QString, int>( "ShapeOffsetUnits", -1 ) );
291  mDataDefinedNames.insert( ShapeRadii, QPair<QString, int>( "ShapeRadii", -1 ) );
292  mDataDefinedNames.insert( ShapeRadiiUnits, QPair<QString, int>( "ShapeRadiiUnits", -1 ) );
293  mDataDefinedNames.insert( ShapeTransparency, QPair<QString, int>( "ShapeTransparency", -1 ) );
294  mDataDefinedNames.insert( ShapeBlendMode, QPair<QString, int>( "ShapeBlendMode", -1 ) );
295  mDataDefinedNames.insert( ShapeFillColor, QPair<QString, int>( "ShapeFillColor", -1 ) );
296  mDataDefinedNames.insert( ShapeBorderColor, QPair<QString, int>( "ShapeBorderColor", -1 ) );
297  mDataDefinedNames.insert( ShapeBorderWidth, QPair<QString, int>( "ShapeBorderWidth", -1 ) );
298  mDataDefinedNames.insert( ShapeBorderWidthUnits, QPair<QString, int>( "ShapeBorderWidthUnits", -1 ) );
299  mDataDefinedNames.insert( ShapeJoinStyle, QPair<QString, int>( "ShapeJoinStyle", -1 ) );
300 
301  // drop shadow
302  mDataDefinedNames.insert( ShadowDraw, QPair<QString, int>( "ShadowDraw", -1 ) );
303  mDataDefinedNames.insert( ShadowUnder, QPair<QString, int>( "ShadowUnder", -1 ) );
304  mDataDefinedNames.insert( ShadowOffsetAngle, QPair<QString, int>( "ShadowOffsetAngle", -1 ) );
305  mDataDefinedNames.insert( ShadowOffsetDist, QPair<QString, int>( "ShadowOffsetDist", -1 ) );
306  mDataDefinedNames.insert( ShadowOffsetUnits, QPair<QString, int>( "ShadowOffsetUnits", -1 ) );
307  mDataDefinedNames.insert( ShadowRadius, QPair<QString, int>( "ShadowRadius", -1 ) );
308  mDataDefinedNames.insert( ShadowRadiusUnits, QPair<QString, int>( "ShadowRadiusUnits", -1 ) );
309  mDataDefinedNames.insert( ShadowTransparency, QPair<QString, int>( "ShadowTransparency", -1 ) );
310  mDataDefinedNames.insert( ShadowScale, QPair<QString, int>( "ShadowScale", -1 ) );
311  mDataDefinedNames.insert( ShadowColor, QPair<QString, int>( "ShadowColor", -1 ) );
312  mDataDefinedNames.insert( ShadowBlendMode, QPair<QString, int>( "ShadowBlendMode", -1 ) );
313 
314  // placement
315  mDataDefinedNames.insert( CentroidWhole, QPair<QString, int>( "CentroidWhole", -1 ) );
316  mDataDefinedNames.insert( OffsetQuad, QPair<QString, int>( "OffsetQuad", -1 ) );
317  mDataDefinedNames.insert( OffsetXY, QPair<QString, int>( "OffsetXY", -1 ) );
318  mDataDefinedNames.insert( OffsetUnits, QPair<QString, int>( "OffsetUnits", -1 ) );
319  mDataDefinedNames.insert( LabelDistance, QPair<QString, int>( "LabelDistance", 13 ) );
320  mDataDefinedNames.insert( DistanceUnits, QPair<QString, int>( "DistanceUnits", -1 ) );
321  mDataDefinedNames.insert( OffsetRotation, QPair<QString, int>( "OffsetRotation", -1 ) );
322  mDataDefinedNames.insert( CurvedCharAngleInOut, QPair<QString, int>( "CurvedCharAngleInOut", -1 ) );
323  mDataDefinedNames.insert( RepeatDistance, QPair<QString, int>( "RepeatDistance", -1 ) );
324  mDataDefinedNames.insert( RepeatDistanceUnit, QPair<QString, int>( "RepeatDistanceUnit", -1 ) );
325  mDataDefinedNames.insert( Priority, QPair<QString, int>( "Priority", -1 ) );
326  mDataDefinedNames.insert( IsObstacle, QPair<QString, int>( "IsObstacle", -1 ) );
327  mDataDefinedNames.insert( ObstacleFactor, QPair<QString, int>( "ObstacleFactor", -1 ) );
328  mDataDefinedNames.insert( PredefinedPositionOrder, QPair<QString, int>( "PredefinedPositionOrder", -1 ) );
329 
330  // (data defined only)
331  mDataDefinedNames.insert( PositionX, QPair<QString, int>( "PositionX", 9 ) );
332  mDataDefinedNames.insert( PositionY, QPair<QString, int>( "PositionY", 10 ) );
333  mDataDefinedNames.insert( Hali, QPair<QString, int>( "Hali", 11 ) );
334  mDataDefinedNames.insert( Vali, QPair<QString, int>( "Vali", 12 ) );
335  mDataDefinedNames.insert( Rotation, QPair<QString, int>( "Rotation", 14 ) );
336 
337  //rendering
338  mDataDefinedNames.insert( ScaleVisibility, QPair<QString, int>( "ScaleVisibility", -1 ) );
339  mDataDefinedNames.insert( MinScale, QPair<QString, int>( "MinScale", 16 ) );
340  mDataDefinedNames.insert( MaxScale, QPair<QString, int>( "MaxScale", 17 ) );
341  mDataDefinedNames.insert( FontLimitPixel, QPair<QString, int>( "FontLimitPixel", -1 ) );
342  mDataDefinedNames.insert( FontMinPixel, QPair<QString, int>( "FontMinPixel", -1 ) );
343  mDataDefinedNames.insert( FontMaxPixel, QPair<QString, int>( "FontMaxPixel", -1 ) );
344  mDataDefinedNames.insert( ZIndex, QPair<QString, int>( "ZIndex", -1 ) );
345  // (data defined only)
346  mDataDefinedNames.insert( Show, QPair<QString, int>( "Show", 15 ) );
347  mDataDefinedNames.insert( AlwaysShow, QPair<QString, int>( "AlwaysShow", 20 ) );
348 
349  // temp stuff for when drawing label components (don't copy)
350  showingShadowRects = false;
351 }
352 
354  : mCurFeat( nullptr )
355  , fieldIndex( 0 )
356  , xform( nullptr )
357  , ct( nullptr )
358  , extentGeom( nullptr )
359  , mFeaturesToLabel( 0 )
360  , mFeatsSendingToPal( 0 )
361  , mFeatsRegPal( 0 )
362  , showingShadowRects( false )
363  , expression( nullptr )
364 {
365  *this = s;
366 }
367 
369 {
370  if ( this == &s )
371  return *this;
372 
373  // copy only permanent stuff
374 
375  enabled = s.enabled;
377 
378  // text style
379  fieldName = s.fieldName;
381  textFont = s.textFont;
385  textColor = s.textColor;
387  blendMode = s.blendMode;
389  // font processing info
392 
393  // text formatting
394  wrapChar = s.wrapChar;
403  decimals = s.decimals;
404  plusSign = s.plusSign;
405 
406  // text buffer
416 
417  // placement
418  placement = s.placement;
425  xOffset = s.xOffset;
426  yOffset = s.yOffset;
429  dist = s.dist;
437  priority = s.priority;
441 
442  // rendering
444  scaleMin = s.scaleMin;
445  scaleMax = s.scaleMax;
451 
457  obstacle = s.obstacle;
460  zIndex = s.zIndex;
461 
462  // shape background
463  shapeDraw = s.shapeDraw;
464  shapeType = s.shapeType;
467  shapeSize = s.shapeSize;
486 
487  // drop shadow
503 
504  // data defined
507  for ( ; it != s.dataDefinedProperties.constEnd(); ++it )
508  {
509  dataDefinedProperties.insert( it.key(), it.value() ? new QgsDataDefined( *it.value() ) : nullptr );
510  }
511  mDataDefinedNames = s.mDataDefinedNames;
512 
513  // scale factors
516  return *this;
517 }
518 
519 
521 {
522  // pal layer is deleted internally in PAL
523 
524  delete ct;
525  delete expression;
526  delete extentGeom;
527 
528  // delete all QgsDataDefined objects (which also deletes their QgsExpression object)
530 }
531 
532 
534 {
535  QgsPalLayerSettings settings;
536  settings.readFromLayer( layer );
537  return settings;
538 }
539 
540 
542 {
543  if ( !expression )
544  {
545  expression = new QgsExpression( fieldName );
546  }
547  return expression;
548 }
549 
550 static QColor _readColor( QgsVectorLayer* layer, const QString& property, const QColor& defaultColor = Qt::black, bool withAlpha = true )
551 {
552  int r = layer->customProperty( property + 'R', QVariant( defaultColor.red() ) ).toInt();
553  int g = layer->customProperty( property + 'G', QVariant( defaultColor.green() ) ).toInt();
554  int b = layer->customProperty( property + 'B', QVariant( defaultColor.blue() ) ).toInt();
555  int a = withAlpha ? layer->customProperty( property + 'A', QVariant( defaultColor.alpha() ) ).toInt() : 255;
556  return QColor( r, g, b, a );
557 }
558 
559 static void _writeColor( QgsVectorLayer* layer, const QString& property, const QColor& color, bool withAlpha = true )
560 {
561  layer->setCustomProperty( property + 'R', color.red() );
562  layer->setCustomProperty( property + 'G', color.green() );
563  layer->setCustomProperty( property + 'B', color.blue() );
564  if ( withAlpha )
565  layer->setCustomProperty( property + 'A', color.alpha() );
566 }
567 
569 {
570  if ( str.compare( "Point", Qt::CaseInsensitive ) == 0
571  || str.compare( "Points", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Points;
572  if ( str.compare( "MapUnit", Qt::CaseInsensitive ) == 0
573  || str.compare( "MapUnits", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::MapUnits;
574  if ( str.compare( "Percent", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Percent;
575  return QgsPalLayerSettings::MM; // "MM"
576 }
577 
578 static Qt::PenJoinStyle _decodePenJoinStyle( const QString& str )
579 {
580  if ( str.compare( "Miter", Qt::CaseInsensitive ) == 0 ) return Qt::MiterJoin;
581  if ( str.compare( "Round", Qt::CaseInsensitive ) == 0 ) return Qt::RoundJoin;
582  return Qt::BevelJoin; // "Bevel"
583 }
584 
585 void QgsPalLayerSettings::readDataDefinedPropertyMap( QgsVectorLayer* layer, QDomElement* parentElem,
587 {
588  if ( !layer && !parentElem )
589  {
590  return;
591  }
592 
594  while ( i.hasNext() )
595  {
596  i.next();
597  if ( layer )
598  {
599  // reading from layer's custom properties (old way)
600  readDataDefinedProperty( layer, i.key(), propertyMap );
601  }
602  else if ( parentElem )
603  {
604  // reading from XML (new way)
605  QDomElement e = parentElem->firstChildElement( i.value().first );
606  if ( !e.isNull() )
607  {
608  QgsDataDefined* dd = new QgsDataDefined();
609  if ( dd->setFromXmlElement( e ) )
610  propertyMap.insert( i.key(), dd );
611  else
612  delete dd;
613  }
614  }
615  }
616 }
617 
618 void QgsPalLayerSettings::writeDataDefinedPropertyMap( QgsVectorLayer* layer, QDomElement* parentElem,
620 {
621  if ( !layer && !parentElem )
622  {
623  return;
624  }
625 
627  while ( i.hasNext() )
628  {
629  i.next();
630  QString newPropertyName = "labeling/dataDefined/" + i.value().first;
631  QVariant propertyValue = QVariant();
632 
634  if ( it != propertyMap.constEnd() )
635  {
636  QgsDataDefined* dd = it.value();
637  if ( dd )
638  {
639  bool active = dd->isActive();
640  bool useExpr = dd->useExpression();
641  QString expr = dd->expressionString();
642  QString field = dd->field();
643 
644  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
645 
646  if ( !defaultVals )
647  {
648  // TODO: update this when project settings for labeling are migrated to better XML layout
649  QStringList values;
650  values << ( active ? "1" : "0" );
651  values << ( useExpr ? "1" : "0" );
652  values << expr;
653  values << field;
654  if ( !values.isEmpty() )
655  {
656  propertyValue = QVariant( values.join( "~~" ) );
657  }
658  }
659 
660  if ( parentElem )
661  {
662  // writing to XML document (instead of writing to layer)
663  QDomDocument doc = parentElem->ownerDocument();
664  QDomElement e = dd->toXmlElement( doc, i.value().first );
665  parentElem->appendChild( e );
666  }
667  }
668  }
669 
670  if ( layer )
671  {
672  // writing to layer's custom properties (old method)
673 
674  if ( propertyValue.isValid() )
675  {
676  layer->setCustomProperty( newPropertyName, propertyValue );
677  }
678  else
679  {
680  // remove unused properties
681  layer->removeCustomProperty( newPropertyName );
682  }
683 
684  if ( layer->customProperty( newPropertyName, QVariant() ).isValid() && i.value().second > -1 )
685  {
686  // remove old-style field index-based property, if still present
687  layer->removeCustomProperty( QString( "labeling/dataDefinedProperty" ) + QString::number( i.value().second ) );
688  }
689  }
690  }
691 }
692 
693 void QgsPalLayerSettings::readDataDefinedProperty( QgsVectorLayer* layer,
696 {
697  QString newPropertyName = "labeling/dataDefined/" + mDataDefinedNames.value( p ).first;
698  QVariant newPropertyField = layer->customProperty( newPropertyName, QVariant() );
699 
700  QString ddString = QString();
701  if ( newPropertyField.isValid() )
702  {
703  ddString = newPropertyField.toString();
704  }
705  else // maybe working with old-style field index-based property (< QGIS 2.0)
706  {
707  int oldIndx = mDataDefinedNames.value( p ).second;
708 
709  if ( oldIndx < 0 ) // something went wrong and we are working with new-style
710  {
711  return;
712  }
713 
714  QString oldPropertyName = "labeling/dataDefinedProperty" + QString::number( oldIndx );
715  QVariant oldPropertyField = layer->customProperty( oldPropertyName, QVariant() );
716 
717  if ( !oldPropertyField.isValid() )
718  {
719  return;
720  }
721 
722  // switch from old-style field index- to name-based properties
723  bool conversionOk;
724  int indx = oldPropertyField.toInt( &conversionOk );
725 
726  if ( conversionOk )
727  {
728  // Fix to migrate from old-style vector api, where returned QMap keys possibly
729  // had 'holes' in sequence of field indices, e.g. 0,2,3
730  // QgsAttrPalIndexNameHash provides a means of access field name in sequences from
731  // providers that procuded holes (e.g. PostGIS skipped geom column), otherwise it is empty
732  QgsAttrPalIndexNameHash oldIndicesToNames = layer->dataProvider()->palAttributeIndexNames();
733 
734  if ( !oldIndicesToNames.isEmpty() )
735  {
736  ddString = oldIndicesToNames.value( indx );
737  }
738  else
739  {
740  QgsFields fields = layer->dataProvider()->fields();
741  if ( indx < fields.size() ) // in case field count has changed
742  {
743  ddString = fields.at( indx ).name();
744  }
745  }
746  }
747 
748  if ( !ddString.isEmpty() )
749  {
750  //upgrade any existing property to field name-based
751  layer->setCustomProperty( newPropertyName, QVariant( updateDataDefinedString( ddString ) ) );
752 
753  // fix for buffer drawing triggered off of just its data defined size in the past (<2.0)
754  if ( oldIndx == 7 ) // old bufferSize enum
755  {
756  bufferDraw = true;
757  layer->setCustomProperty( "labeling/bufferDraw", true );
758  }
759 
760  // fix for scale visibility limits triggered off of just its data defined values in the past (<2.0)
761  if ( oldIndx == 16 || oldIndx == 17 ) // old minScale and maxScale enums
762  {
763  scaleVisibility = true;
764  layer->setCustomProperty( "labeling/scaleVisibility", true );
765  }
766  }
767 
768  // remove old-style field index-based property
769  layer->removeCustomProperty( oldPropertyName );
770  }
771 
772  if ( !ddString.isEmpty() && ddString != QLatin1String( "0~~0~~~~" ) )
773  {
774  // TODO: update this when project settings for labeling are migrated to better XML layout
775  QString newStyleString = updateDataDefinedString( ddString );
776  QStringList ddv = newStyleString.split( "~~" );
777 
778  QgsDataDefined* dd = new QgsDataDefined( ddv.at( 0 ).toInt(), ddv.at( 1 ).toInt(), ddv.at( 2 ), ddv.at( 3 ) );
779  propertyMap.insert( p, dd );
780  }
781  else
782  {
783  // remove unused properties
784  layer->removeCustomProperty( newPropertyName );
785  }
786 }
787 
789 {
790  if ( layer->customProperty( "labeling" ).toString() != QLatin1String( "pal" ) )
791  {
792  if ( layer->geometryType() == QGis::Point )
794 
795  // for polygons the "over point" (over centroid) placement is better than the default
796  // "around point" (around centroid) which is more suitable for points
797  if ( layer->geometryType() == QGis::Polygon )
799 
800  return; // there's no information available
801  }
802 
803  // NOTE: set defaults for newly added properties, for backwards compatibility
804 
805  enabled = layer->labelsEnabled();
806  drawLabels = layer->customProperty( "labeling/drawLabels", true ).toBool();
807 
808  // text style
809  fieldName = layer->customProperty( "labeling/fieldName" ).toString();
810  isExpression = layer->customProperty( "labeling/isExpression" ).toBool();
811  QFont appFont = QApplication::font();
812  mTextFontFamily = layer->customProperty( "labeling/fontFamily", QVariant( appFont.family() ) ).toString();
813  QString fontFamily = mTextFontFamily;
815  {
816  // trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
817  mTextFontFound = false;
818 
819  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
820  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
821 
822  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
823  fontFamily = appFont.family();
824  }
825 
826  double fontSize = layer->customProperty( "labeling/fontSize" ).toDouble();
827  fontSizeInMapUnits = layer->customProperty( "labeling/fontSizeInMapUnits" ).toBool();
828  if ( layer->customProperty( "labeling/fontSizeMapUnitScale" ).toString().isEmpty() )
829  {
830  //fallback to older property
831  fontSizeMapUnitScale.minScale = layer->customProperty( "labeling/fontSizeMapUnitMinScale", 0.0 ).toDouble();
832  fontSizeMapUnitScale.maxScale = layer->customProperty( "labeling/fontSizeMapUnitMaxScale", 0.0 ).toDouble();
833  }
834  else
835  {
836  fontSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/fontSizeMapUnitScale" ).toString() );
837  }
838  int fontWeight = layer->customProperty( "labeling/fontWeight" ).toInt();
839  bool fontItalic = layer->customProperty( "labeling/fontItalic" ).toBool();
840  textFont = QFont( fontFamily, fontSize, fontWeight, fontItalic );
841  textFont.setPointSizeF( fontSize ); //double precision needed because of map units
842  textNamedStyle = QgsFontUtils::translateNamedStyle( layer->customProperty( "labeling/namedStyle", QVariant( "" ) ).toString() );
843  QgsFontUtils::updateFontViaStyle( textFont, textNamedStyle ); // must come after textFont.setPointSizeF()
844  textFont.setCapitalization( static_cast< QFont::Capitalization >( layer->customProperty( "labeling/fontCapitals", QVariant( 0 ) ).toUInt() ) );
845  textFont.setUnderline( layer->customProperty( "labeling/fontUnderline" ).toBool() );
846  textFont.setStrikeOut( layer->customProperty( "labeling/fontStrikeout" ).toBool() );
847  textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( "labeling/fontLetterSpacing", QVariant( 0.0 ) ).toDouble() );
848  textFont.setWordSpacing( layer->customProperty( "labeling/fontWordSpacing", QVariant( 0.0 ) ).toDouble() );
849  textColor = _readColor( layer, "labeling/textColor", Qt::black, false );
850  textTransp = layer->customProperty( "labeling/textTransp" ).toInt();
852  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/blendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
853  previewBkgrdColor = QColor( layer->customProperty( "labeling/previewBkgrdColor", QVariant( "#ffffff" ) ).toString() );
854 
855 
856  // text formatting
857  wrapChar = layer->customProperty( "labeling/wrapChar" ).toString();
858  multilineHeight = layer->customProperty( "labeling/multilineHeight", QVariant( 1.0 ) ).toDouble();
859  multilineAlign = static_cast< MultiLineAlign >( layer->customProperty( "labeling/multilineAlign", QVariant( MultiFollowPlacement ) ).toUInt() );
860  addDirectionSymbol = layer->customProperty( "labeling/addDirectionSymbol" ).toBool();
861  leftDirectionSymbol = layer->customProperty( "labeling/leftDirectionSymbol", QVariant( "<" ) ).toString();
862  rightDirectionSymbol = layer->customProperty( "labeling/rightDirectionSymbol", QVariant( ">" ) ).toString();
863  reverseDirectionSymbol = layer->customProperty( "labeling/reverseDirectionSymbol" ).toBool();
864  placeDirectionSymbol = static_cast< DirectionSymbols >( layer->customProperty( "labeling/placeDirectionSymbol", QVariant( SymbolLeftRight ) ).toUInt() );
865  formatNumbers = layer->customProperty( "labeling/formatNumbers" ).toBool();
866  decimals = layer->customProperty( "labeling/decimals" ).toInt();
867  plusSign = layer->customProperty( "labeling/plussign" ).toBool();
868 
869  // text buffer
870  double bufSize = layer->customProperty( "labeling/bufferSize", QVariant( 0.0 ) ).toDouble();
871 
872  // fix for buffer being keyed off of just its size in the past (<2.0)
873  QVariant drawBuffer = layer->customProperty( "labeling/bufferDraw", QVariant() );
874  if ( drawBuffer.isValid() )
875  {
876  bufferDraw = drawBuffer.toBool();
877  bufferSize = bufSize;
878  }
879  else if ( bufSize != 0.0 )
880  {
881  bufferDraw = true;
882  bufferSize = bufSize;
883  }
884  else
885  {
886  // keep bufferSize at new 1.0 default
887  bufferDraw = false;
888  }
889 
890  bufferSizeInMapUnits = layer->customProperty( "labeling/bufferSizeInMapUnits" ).toBool();
891  if ( layer->customProperty( "labeling/bufferSizeMapUnitScale" ).toString().isEmpty() )
892  {
893  //fallback to older property
894  bufferSizeMapUnitScale.minScale = layer->customProperty( "labeling/bufferSizeMapUnitMinScale", 0.0 ).toDouble();
895  bufferSizeMapUnitScale.maxScale = layer->customProperty( "labeling/bufferSizeMapUnitMaxScale", 0.0 ).toDouble();
896  }
897  else
898  {
899  bufferSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/bufferSizeMapUnitScale" ).toString() );
900  }
901  bufferColor = _readColor( layer, "labeling/bufferColor", Qt::white, false );
902  bufferTransp = layer->customProperty( "labeling/bufferTransp" ).toInt();
904  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/bufferBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
905  bufferJoinStyle = static_cast< Qt::PenJoinStyle >( layer->customProperty( "labeling/bufferJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt() );
906  bufferNoFill = layer->customProperty( "labeling/bufferNoFill", QVariant( false ) ).toBool();
907 
908  // background
909  shapeDraw = layer->customProperty( "labeling/shapeDraw", QVariant( false ) ).toBool();
910  shapeType = static_cast< ShapeType >( layer->customProperty( "labeling/shapeType", QVariant( ShapeRectangle ) ).toUInt() );
911  shapeSVGFile = layer->customProperty( "labeling/shapeSVGFile", QVariant( "" ) ).toString();
912  shapeSizeType = static_cast< SizeType >( layer->customProperty( "labeling/shapeSizeType", QVariant( SizeBuffer ) ).toUInt() );
913  shapeSize = QPointF( layer->customProperty( "labeling/shapeSizeX", QVariant( 0.0 ) ).toDouble(),
914  layer->customProperty( "labeling/shapeSizeY", QVariant( 0.0 ) ).toDouble() );
915  shapeSizeUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeSizeUnits", QVariant( MM ) ).toUInt() );
916  if ( layer->customProperty( "labeling/shapeSizeMapUnitScale" ).toString().isEmpty() )
917  {
918  //fallback to older property
919  shapeSizeMapUnitScale.minScale = layer->customProperty( "labeling/shapeSizeMapUnitMinScale", 0.0 ).toDouble();
920  shapeSizeMapUnitScale.maxScale = layer->customProperty( "labeling/shapeSizeMapUnitMaxScale", 0.0 ).toDouble();
921  }
922  else
923  {
924  shapeSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeSizeMapUnitScale" ).toString() );
925  }
926  shapeRotationType = static_cast< RotationType >( layer->customProperty( "labeling/shapeRotationType", QVariant( RotationSync ) ).toUInt() );
927  shapeRotation = layer->customProperty( "labeling/shapeRotation", QVariant( 0.0 ) ).toDouble();
928  shapeOffset = QPointF( layer->customProperty( "labeling/shapeOffsetX", QVariant( 0.0 ) ).toDouble(),
929  layer->customProperty( "labeling/shapeOffsetY", QVariant( 0.0 ) ).toDouble() );
930  shapeOffsetUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeOffsetUnits", QVariant( MM ) ).toUInt() );
931  if ( layer->customProperty( "labeling/shapeOffsetMapUnitScale" ).toString().isEmpty() )
932  {
933  //fallback to older property
934  shapeOffsetMapUnitScale.minScale = layer->customProperty( "labeling/shapeOffsetMapUnitMinScale", 0.0 ).toDouble();
935  shapeOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/shapeOffsetMapUnitMaxScale", 0.0 ).toDouble();
936  }
937  else
938  {
939  shapeOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeOffsetMapUnitScale" ).toString() );
940  }
941  shapeRadii = QPointF( layer->customProperty( "labeling/shapeRadiiX", QVariant( 0.0 ) ).toDouble(),
942  layer->customProperty( "labeling/shapeRadiiY", QVariant( 0.0 ) ).toDouble() );
943  shapeRadiiUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeRadiiUnits", QVariant( MM ) ).toUInt() );
944  if ( layer->customProperty( "labeling/shapeRadiiMapUnitScale" ).toString().isEmpty() )
945  {
946  //fallback to older property
947  shapeRadiiMapUnitScale.minScale = layer->customProperty( "labeling/shapeRadiiMapUnitMinScale", 0.0 ).toDouble();
948  shapeRadiiMapUnitScale.maxScale = layer->customProperty( "labeling/shapeRadiiMapUnitMaxScale", 0.0 ).toDouble();
949  }
950  else
951  {
952  shapeRadiiMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeRadiiMapUnitScale" ).toString() );
953  }
954  shapeFillColor = _readColor( layer, "labeling/shapeFillColor", Qt::white, true );
955  shapeBorderColor = _readColor( layer, "labeling/shapeBorderColor", Qt::darkGray, true );
956  shapeBorderWidth = layer->customProperty( "labeling/shapeBorderWidth", QVariant( .0 ) ).toDouble();
957  shapeBorderWidthUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeBorderWidthUnits", QVariant( MM ) ).toUInt() );
958  if ( layer->customProperty( "labeling/shapeBorderWidthMapUnitScale" ).toString().isEmpty() )
959  {
960  //fallback to older property
961  shapeBorderWidthMapUnitScale.minScale = layer->customProperty( "labeling/shapeBorderWidthMapUnitMinScale", 0.0 ).toDouble();
962  shapeBorderWidthMapUnitScale.maxScale = layer->customProperty( "labeling/shapeBorderWidthMapUnitMaxScale", 0.0 ).toDouble();
963  }
964  else
965  {
966  shapeBorderWidthMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeBorderWidthMapUnitScale" ).toString() );
967  }
968  shapeJoinStyle = static_cast< Qt::PenJoinStyle >( layer->customProperty( "labeling/shapeJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt() );
969  shapeTransparency = layer->customProperty( "labeling/shapeTransparency", QVariant( 0 ) ).toInt();
971  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/shapeBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
972 
973  // drop shadow
974  shadowDraw = layer->customProperty( "labeling/shadowDraw", QVariant( false ) ).toBool();
975  shadowUnder = static_cast< ShadowType >( layer->customProperty( "labeling/shadowUnder", QVariant( ShadowLowest ) ).toUInt() );//ShadowLowest;
976  shadowOffsetAngle = layer->customProperty( "labeling/shadowOffsetAngle", QVariant( 135 ) ).toInt();
977  shadowOffsetDist = layer->customProperty( "labeling/shadowOffsetDist", QVariant( 1.0 ) ).toDouble();
978  shadowOffsetUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shadowOffsetUnits", QVariant( MM ) ).toUInt() );
979  if ( layer->customProperty( "labeling/shadowOffsetMapUnitScale" ).toString().isEmpty() )
980  {
981  //fallback to older property
982  shadowOffsetMapUnitScale.minScale = layer->customProperty( "labeling/shadowOffsetMapUnitMinScale", 0.0 ).toDouble();
983  shadowOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/shadowOffsetMapUnitMaxScale", 0.0 ).toDouble();
984  }
985  else
986  {
987  shadowOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shadowOffsetMapUnitScale" ).toString() );
988  }
989  shadowOffsetGlobal = layer->customProperty( "labeling/shadowOffsetGlobal", QVariant( true ) ).toBool();
990  shadowRadius = layer->customProperty( "labeling/shadowRadius", QVariant( 1.5 ) ).toDouble();
991  shadowRadiusUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shadowRadiusUnits", QVariant( MM ) ).toUInt() );
992  if ( layer->customProperty( "labeling/shadowRadiusMapUnitScale" ).toString().isEmpty() )
993  {
994  //fallback to older property
995  shadowRadiusMapUnitScale.minScale = layer->customProperty( "labeling/shadowRadiusMapUnitMinScale", 0.0 ).toDouble();
996  shadowRadiusMapUnitScale.maxScale = layer->customProperty( "labeling/shadowRadiusMapUnitMaxScale", 0.0 ).toDouble();
997  }
998  else
999  {
1000  shadowRadiusMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shadowRadiusMapUnitScale" ).toString() );
1001  }
1002  shadowRadiusAlphaOnly = layer->customProperty( "labeling/shadowRadiusAlphaOnly", QVariant( false ) ).toBool();
1003  shadowTransparency = layer->customProperty( "labeling/shadowTransparency", QVariant( 30 ) ).toInt();
1004  shadowScale = layer->customProperty( "labeling/shadowScale", QVariant( 100 ) ).toInt();
1005  shadowColor = _readColor( layer, "labeling/shadowColor", Qt::black, false );
1007  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/shadowBlendMode", QVariant( QgsMapRenderer::BlendMultiply ) ).toUInt() ) );
1008 
1009  // placement
1010  placement = static_cast< Placement >( layer->customProperty( "labeling/placement" ).toInt() );
1011  placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt();
1012  centroidWhole = layer->customProperty( "labeling/centroidWhole", QVariant( false ) ).toBool();
1013  centroidInside = layer->customProperty( "labeling/centroidInside", QVariant( false ) ).toBool();
1014  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( layer->customProperty( "labeling/predefinedPositionOrder" ).toString() );
1016  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
1017  fitInPolygonOnly = layer->customProperty( "labeling/fitInPolygonOnly", QVariant( false ) ).toBool();
1018  dist = layer->customProperty( "labeling/dist" ).toDouble();
1019  distInMapUnits = layer->customProperty( "labeling/distInMapUnits" ).toBool();
1020  if ( layer->customProperty( "labeling/distMapUnitScale" ).toString().isEmpty() )
1021  {
1022  //fallback to older property
1023  distMapUnitScale.minScale = layer->customProperty( "labeling/distMapUnitMinScale", 0.0 ).toDouble();
1024  distMapUnitScale.maxScale = layer->customProperty( "labeling/distMapUnitMaxScale", 0.0 ).toDouble();
1025  }
1026  else
1027  {
1028  distMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/distMapUnitScale" ).toString() );
1029  }
1030  offsetType = static_cast< OffsetType >( layer->customProperty( "labeling/offsetType", QVariant( FromPoint ) ).toUInt() );
1031  quadOffset = static_cast< QuadrantPosition >( layer->customProperty( "labeling/quadOffset", QVariant( QuadrantOver ) ).toUInt() );
1032  xOffset = layer->customProperty( "labeling/xOffset", QVariant( 0.0 ) ).toDouble();
1033  yOffset = layer->customProperty( "labeling/yOffset", QVariant( 0.0 ) ).toDouble();
1034  labelOffsetInMapUnits = layer->customProperty( "labeling/labelOffsetInMapUnits", QVariant( true ) ).toBool();
1035  if ( layer->customProperty( "labeling/labelOffsetMapUnitScale" ).toString().isEmpty() )
1036  {
1037  //fallback to older property
1038  labelOffsetMapUnitScale.minScale = layer->customProperty( "labeling/labelOffsetMapUnitMinScale", 0.0 ).toDouble();
1039  labelOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/labelOffsetMapUnitMaxScale", 0.0 ).toDouble();
1040  }
1041  else
1042  {
1043  labelOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/labelOffsetMapUnitScale" ).toString() );
1044  }
1045  angleOffset = layer->customProperty( "labeling/angleOffset", QVariant( 0.0 ) ).toDouble();
1046  preserveRotation = layer->customProperty( "labeling/preserveRotation", QVariant( true ) ).toBool();
1047  maxCurvedCharAngleIn = layer->customProperty( "labeling/maxCurvedCharAngleIn", QVariant( 25.0 ) ).toDouble();
1048  maxCurvedCharAngleOut = layer->customProperty( "labeling/maxCurvedCharAngleOut", QVariant( -25.0 ) ).toDouble();
1049  priority = layer->customProperty( "labeling/priority" ).toInt();
1050  repeatDistance = layer->customProperty( "labeling/repeatDistance", 0.0 ).toDouble();
1051  repeatDistanceUnit = static_cast< SizeUnit >( layer->customProperty( "labeling/repeatDistanceUnit", QVariant( MM ) ).toUInt() );
1052  if ( layer->customProperty( "labeling/repeatDistanceMapUnitScale" ).toString().isEmpty() )
1053  {
1054  //fallback to older property
1055  repeatDistanceMapUnitScale.minScale = layer->customProperty( "labeling/repeatDistanceMapUnitMinScale", 0.0 ).toDouble();
1056  repeatDistanceMapUnitScale.maxScale = layer->customProperty( "labeling/repeatDistanceMapUnitMaxScale", 0.0 ).toDouble();
1057  }
1058  else
1059  {
1060  repeatDistanceMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/repeatDistanceMapUnitScale" ).toString() );
1061  }
1062 
1063  // rendering
1064  int scalemn = layer->customProperty( "labeling/scaleMin", QVariant( 0 ) ).toInt();
1065  int scalemx = layer->customProperty( "labeling/scaleMax", QVariant( 0 ) ).toInt();
1066 
1067  // fix for scale visibility limits being keyed off of just its values in the past (<2.0)
1068  QVariant scalevis = layer->customProperty( "labeling/scaleVisibility", QVariant() );
1069  if ( scalevis.isValid() )
1070  {
1071  scaleVisibility = scalevis.toBool();
1072  scaleMin = scalemn;
1073  scaleMax = scalemx;
1074  }
1075  else if ( scalemn > 0 || scalemx > 0 )
1076  {
1077  scaleVisibility = true;
1078  scaleMin = scalemn;
1079  scaleMax = scalemx;
1080  }
1081  else
1082  {
1083  // keep scaleMin and scaleMax at new 1.0 defaults (1 and 10000000, were 0 and 0)
1084  scaleVisibility = false;
1085  }
1086 
1087 
1088  fontLimitPixelSize = layer->customProperty( "labeling/fontLimitPixelSize", QVariant( false ) ).toBool();
1089  fontMinPixelSize = layer->customProperty( "labeling/fontMinPixelSize", QVariant( 0 ) ).toInt();
1090  fontMaxPixelSize = layer->customProperty( "labeling/fontMaxPixelSize", QVariant( 10000 ) ).toInt();
1091  displayAll = layer->customProperty( "labeling/displayAll", QVariant( false ) ).toBool();
1092  upsidedownLabels = static_cast< UpsideDownLabels >( layer->customProperty( "labeling/upsidedownLabels", QVariant( Upright ) ).toUInt() );
1093 
1094  labelPerPart = layer->customProperty( "labeling/labelPerPart" ).toBool();
1095  mergeLines = layer->customProperty( "labeling/mergeLines" ).toBool();
1096  minFeatureSize = layer->customProperty( "labeling/minFeatureSize" ).toDouble();
1097  limitNumLabels = layer->customProperty( "labeling/limitNumLabels", QVariant( false ) ).toBool();
1098  maxNumLabels = layer->customProperty( "labeling/maxNumLabels", QVariant( 2000 ) ).toInt();
1099  obstacle = layer->customProperty( "labeling/obstacle", QVariant( true ) ).toBool();
1100  obstacleFactor = layer->customProperty( "labeling/obstacleFactor", QVariant( 1.0 ) ).toDouble();
1101  obstacleType = static_cast< ObstacleType >( layer->customProperty( "labeling/obstacleType", QVariant( PolygonInterior ) ).toUInt() );
1102  zIndex = layer->customProperty( "labeling/zIndex", QVariant( 0.0 ) ).toDouble();
1103 
1104  readDataDefinedPropertyMap( layer, nullptr, dataDefinedProperties );
1105 }
1106 
1108 {
1109  // this is a mark that labeling information is present
1110  layer->setCustomProperty( "labeling", "pal" );
1111 
1112  layer->setCustomProperty( "labeling/enabled", enabled );
1113  layer->setCustomProperty( "labeling/drawLabels", drawLabels );
1114 
1115  // text style
1116  layer->setCustomProperty( "labeling/fieldName", fieldName );
1117  layer->setCustomProperty( "labeling/isExpression", isExpression );
1118  layer->setCustomProperty( "labeling/fontFamily", textFont.family() );
1119  layer->setCustomProperty( "labeling/namedStyle", QgsFontUtils::untranslateNamedStyle( textNamedStyle ) );
1120  layer->setCustomProperty( "labeling/fontSize", textFont.pointSizeF() );
1121  layer->setCustomProperty( "labeling/fontSizeInMapUnits", fontSizeInMapUnits );
1122  layer->setCustomProperty( "labeling/fontSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( fontSizeMapUnitScale ) );
1123  layer->setCustomProperty( "labeling/fontWeight", textFont.weight() );
1124  layer->setCustomProperty( "labeling/fontItalic", textFont.italic() );
1125  layer->setCustomProperty( "labeling/fontStrikeout", textFont.strikeOut() );
1126  layer->setCustomProperty( "labeling/fontUnderline", textFont.underline() );
1127  _writeColor( layer, "labeling/textColor", textColor );
1128  layer->setCustomProperty( "labeling/fontCapitals", static_cast< unsigned int >( textFont.capitalization() ) );
1129  layer->setCustomProperty( "labeling/fontLetterSpacing", textFont.letterSpacing() );
1130  layer->setCustomProperty( "labeling/fontWordSpacing", textFont.wordSpacing() );
1131  layer->setCustomProperty( "labeling/textTransp", textTransp );
1132  layer->setCustomProperty( "labeling/blendMode", QgsMapRenderer::getBlendModeEnum( blendMode ) );
1133  layer->setCustomProperty( "labeling/previewBkgrdColor", previewBkgrdColor.name() );
1134 
1135  // text formatting
1136  layer->setCustomProperty( "labeling/wrapChar", wrapChar );
1137  layer->setCustomProperty( "labeling/multilineHeight", multilineHeight );
1138  layer->setCustomProperty( "labeling/multilineAlign", static_cast< unsigned int >( multilineAlign ) );
1139  layer->setCustomProperty( "labeling/addDirectionSymbol", addDirectionSymbol );
1140  layer->setCustomProperty( "labeling/leftDirectionSymbol", leftDirectionSymbol );
1141  layer->setCustomProperty( "labeling/rightDirectionSymbol", rightDirectionSymbol );
1142  layer->setCustomProperty( "labeling/reverseDirectionSymbol", reverseDirectionSymbol );
1143  layer->setCustomProperty( "labeling/placeDirectionSymbol", static_cast< unsigned int >( placeDirectionSymbol ) );
1144  layer->setCustomProperty( "labeling/formatNumbers", formatNumbers );
1145  layer->setCustomProperty( "labeling/decimals", decimals );
1146  layer->setCustomProperty( "labeling/plussign", plusSign );
1147 
1148  // text buffer
1149  layer->setCustomProperty( "labeling/bufferDraw", bufferDraw );
1150  layer->setCustomProperty( "labeling/bufferSize", bufferSize );
1151  layer->setCustomProperty( "labeling/bufferSizeInMapUnits", bufferSizeInMapUnits );
1152  layer->setCustomProperty( "labeling/bufferSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( bufferSizeMapUnitScale ) );
1153  _writeColor( layer, "labeling/bufferColor", bufferColor );
1154  layer->setCustomProperty( "labeling/bufferNoFill", bufferNoFill );
1155  layer->setCustomProperty( "labeling/bufferTransp", bufferTransp );
1156  layer->setCustomProperty( "labeling/bufferJoinStyle", static_cast< unsigned int >( bufferJoinStyle ) );
1157  layer->setCustomProperty( "labeling/bufferBlendMode", QgsMapRenderer::getBlendModeEnum( bufferBlendMode ) );
1158 
1159  // background
1160  layer->setCustomProperty( "labeling/shapeDraw", shapeDraw );
1161  layer->setCustomProperty( "labeling/shapeType", static_cast< unsigned int >( shapeType ) );
1162  layer->setCustomProperty( "labeling/shapeSVGFile", shapeSVGFile );
1163  layer->setCustomProperty( "labeling/shapeSizeType", static_cast< unsigned int >( shapeSizeType ) );
1164  layer->setCustomProperty( "labeling/shapeSizeX", shapeSize.x() );
1165  layer->setCustomProperty( "labeling/shapeSizeY", shapeSize.y() );
1166  layer->setCustomProperty( "labeling/shapeSizeUnits", static_cast< unsigned int >( shapeSizeUnits ) );
1167  layer->setCustomProperty( "labeling/shapeSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeSizeMapUnitScale ) );
1168  layer->setCustomProperty( "labeling/shapeRotationType", static_cast< unsigned int >( shapeRotationType ) );
1169  layer->setCustomProperty( "labeling/shapeRotation", shapeRotation );
1170  layer->setCustomProperty( "labeling/shapeOffsetX", shapeOffset.x() );
1171  layer->setCustomProperty( "labeling/shapeOffsetY", shapeOffset.y() );
1172  layer->setCustomProperty( "labeling/shapeOffsetUnits", static_cast< unsigned int >( shapeOffsetUnits ) );
1173  layer->setCustomProperty( "labeling/shapeOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeOffsetMapUnitScale ) );
1174  layer->setCustomProperty( "labeling/shapeRadiiX", shapeRadii.x() );
1175  layer->setCustomProperty( "labeling/shapeRadiiY", shapeRadii.y() );
1176  layer->setCustomProperty( "labeling/shapeRadiiUnits", static_cast< unsigned int >( shapeRadiiUnits ) );
1177  layer->setCustomProperty( "labeling/shapeRadiiMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeRadiiMapUnitScale ) );
1178  _writeColor( layer, "labeling/shapeFillColor", shapeFillColor, true );
1179  _writeColor( layer, "labeling/shapeBorderColor", shapeBorderColor, true );
1180  layer->setCustomProperty( "labeling/shapeBorderWidth", shapeBorderWidth );
1181  layer->setCustomProperty( "labeling/shapeBorderWidthUnits", static_cast< unsigned int >( shapeBorderWidthUnits ) );
1182  layer->setCustomProperty( "labeling/shapeBorderWidthMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeBorderWidthMapUnitScale ) );
1183  layer->setCustomProperty( "labeling/shapeJoinStyle", static_cast< unsigned int >( shapeJoinStyle ) );
1184  layer->setCustomProperty( "labeling/shapeTransparency", shapeTransparency );
1185  layer->setCustomProperty( "labeling/shapeBlendMode", QgsMapRenderer::getBlendModeEnum( shapeBlendMode ) );
1186 
1187  // drop shadow
1188  layer->setCustomProperty( "labeling/shadowDraw", shadowDraw );
1189  layer->setCustomProperty( "labeling/shadowUnder", static_cast< unsigned int >( shadowUnder ) );
1190  layer->setCustomProperty( "labeling/shadowOffsetAngle", shadowOffsetAngle );
1191  layer->setCustomProperty( "labeling/shadowOffsetDist", shadowOffsetDist );
1192  layer->setCustomProperty( "labeling/shadowOffsetUnits", static_cast< unsigned int >( shadowOffsetUnits ) );
1193  layer->setCustomProperty( "labeling/shadowOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowOffsetMapUnitScale ) );
1194  layer->setCustomProperty( "labeling/shadowOffsetGlobal", shadowOffsetGlobal );
1195  layer->setCustomProperty( "labeling/shadowRadius", shadowRadius );
1196  layer->setCustomProperty( "labeling/shadowRadiusUnits", static_cast< unsigned int >( shadowRadiusUnits ) );
1197  layer->setCustomProperty( "labeling/shadowRadiusMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowRadiusMapUnitScale ) );
1198  layer->setCustomProperty( "labeling/shadowRadiusAlphaOnly", shadowRadiusAlphaOnly );
1199  layer->setCustomProperty( "labeling/shadowTransparency", shadowTransparency );
1200  layer->setCustomProperty( "labeling/shadowScale", shadowScale );
1201  _writeColor( layer, "labeling/shadowColor", shadowColor, false );
1202  layer->setCustomProperty( "labeling/shadowBlendMode", QgsMapRenderer::getBlendModeEnum( shadowBlendMode ) );
1203 
1204  // placement
1205  layer->setCustomProperty( "labeling/placement", placement );
1206  layer->setCustomProperty( "labeling/placementFlags", static_cast< unsigned int >( placementFlags ) );
1207  layer->setCustomProperty( "labeling/centroidWhole", centroidWhole );
1208  layer->setCustomProperty( "labeling/centroidInside", centroidInside );
1209  layer->setCustomProperty( "labeling/predefinedPositionOrder", QgsLabelingUtils::encodePredefinedPositionOrder( predefinedPositionOrder ) );
1210  layer->setCustomProperty( "labeling/fitInPolygonOnly", fitInPolygonOnly );
1211  layer->setCustomProperty( "labeling/dist", dist );
1212  layer->setCustomProperty( "labeling/distInMapUnits", distInMapUnits );
1213  layer->setCustomProperty( "labeling/distMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( distMapUnitScale ) );
1214  layer->setCustomProperty( "labeling/offsetType", static_cast< unsigned int >( offsetType ) );
1215  layer->setCustomProperty( "labeling/quadOffset", static_cast< unsigned int >( quadOffset ) );
1216  layer->setCustomProperty( "labeling/xOffset", xOffset );
1217  layer->setCustomProperty( "labeling/yOffset", yOffset );
1218  layer->setCustomProperty( "labeling/labelOffsetInMapUnits", labelOffsetInMapUnits );
1219  layer->setCustomProperty( "labeling/labelOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( labelOffsetMapUnitScale ) );
1220  layer->setCustomProperty( "labeling/angleOffset", angleOffset );
1221  layer->setCustomProperty( "labeling/preserveRotation", preserveRotation );
1222  layer->setCustomProperty( "labeling/maxCurvedCharAngleIn", maxCurvedCharAngleIn );
1223  layer->setCustomProperty( "labeling/maxCurvedCharAngleOut", maxCurvedCharAngleOut );
1224  layer->setCustomProperty( "labeling/priority", priority );
1225  layer->setCustomProperty( "labeling/repeatDistance", repeatDistance );
1226  layer->setCustomProperty( "labeling/repeatDistanceUnit", repeatDistanceUnit );
1227  layer->setCustomProperty( "labeling/repeatDistanceMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( repeatDistanceMapUnitScale ) );
1228 
1229  // rendering
1230  layer->setCustomProperty( "labeling/scaleVisibility", scaleVisibility );
1231  layer->setCustomProperty( "labeling/scaleMin", scaleMin );
1232  layer->setCustomProperty( "labeling/scaleMax", scaleMax );
1233  layer->setCustomProperty( "labeling/fontLimitPixelSize", fontLimitPixelSize );
1234  layer->setCustomProperty( "labeling/fontMinPixelSize", fontMinPixelSize );
1235  layer->setCustomProperty( "labeling/fontMaxPixelSize", fontMaxPixelSize );
1236  layer->setCustomProperty( "labeling/displayAll", displayAll );
1237  layer->setCustomProperty( "labeling/upsidedownLabels", static_cast< unsigned int >( upsidedownLabels ) );
1238 
1239  layer->setCustomProperty( "labeling/labelPerPart", labelPerPart );
1240  layer->setCustomProperty( "labeling/mergeLines", mergeLines );
1241  layer->setCustomProperty( "labeling/minFeatureSize", minFeatureSize );
1242  layer->setCustomProperty( "labeling/limitNumLabels", limitNumLabels );
1243  layer->setCustomProperty( "labeling/maxNumLabels", maxNumLabels );
1244  layer->setCustomProperty( "labeling/obstacle", obstacle );
1245  layer->setCustomProperty( "labeling/obstacleFactor", obstacleFactor );
1246  layer->setCustomProperty( "labeling/obstacleType", static_cast< unsigned int >( obstacleType ) );
1247  layer->setCustomProperty( "labeling/zIndex", zIndex );
1248 
1249  writeDataDefinedPropertyMap( layer, nullptr, dataDefinedProperties );
1250  layer->emitStyleChanged();
1251 }
1252 
1254 {
1255  enabled = true;
1256  drawLabels = true;
1257 
1258  // text style
1259  QDomElement textStyleElem = elem.firstChildElement( "text-style" );
1260  fieldName = textStyleElem.attribute( "fieldName" );
1261  isExpression = textStyleElem.attribute( "isExpression" ).toInt();
1262  QFont appFont = QApplication::font();
1263  mTextFontFamily = textStyleElem.attribute( "fontFamily", appFont.family() );
1264  QString fontFamily = mTextFontFamily;
1266  {
1267  // trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
1268  mTextFontFound = false;
1269 
1270  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
1271  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
1272 
1273  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
1274  fontFamily = appFont.family();
1275  }
1276 
1277  double fontSize = textStyleElem.attribute( "fontSize" ).toDouble();
1278  fontSizeInMapUnits = textStyleElem.attribute( "fontSizeInMapUnits" ).toInt();
1279  if ( !textStyleElem.hasAttribute( "fontSizeMapUnitScale" ) )
1280  {
1281  //fallback to older property
1282  fontSizeMapUnitScale.minScale = textStyleElem.attribute( "fontSizeMapUnitMinScale", "0" ).toDouble();
1283  fontSizeMapUnitScale.maxScale = textStyleElem.attribute( "fontSizeMapUnitMaxScale", "0" ).toDouble();
1284  }
1285  else
1286  {
1287  fontSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( textStyleElem.attribute( "fontSizeMapUnitScale" ) );
1288  }
1289  int fontWeight = textStyleElem.attribute( "fontWeight" ).toInt();
1290  bool fontItalic = textStyleElem.attribute( "fontItalic" ).toInt();
1291  textFont = QFont( fontFamily, fontSize, fontWeight, fontItalic );
1292  textFont.setPointSizeF( fontSize ); //double precision needed because of map units
1293  textNamedStyle = QgsFontUtils::translateNamedStyle( textStyleElem.attribute( "namedStyle" ) );
1294  QgsFontUtils::updateFontViaStyle( textFont, textNamedStyle ); // must come after textFont.setPointSizeF()
1295  textFont.setCapitalization( static_cast< QFont::Capitalization >( textStyleElem.attribute( "fontCapitals", "0" ).toUInt() ) );
1296  textFont.setUnderline( textStyleElem.attribute( "fontUnderline" ).toInt() );
1297  textFont.setStrikeOut( textStyleElem.attribute( "fontStrikeout" ).toInt() );
1298  textFont.setLetterSpacing( QFont::AbsoluteSpacing, textStyleElem.attribute( "fontLetterSpacing", "0" ).toDouble() );
1299  textFont.setWordSpacing( textStyleElem.attribute( "fontWordSpacing", "0" ).toDouble() );
1300  textColor = QgsSymbolLayerV2Utils::decodeColor( textStyleElem.attribute( "textColor", QgsSymbolLayerV2Utils::encodeColor( Qt::black ) ) );
1301  textTransp = textStyleElem.attribute( "textTransp" ).toInt();
1303  static_cast< QgsMapRenderer::BlendMode >( textStyleElem.attribute( "blendMode", QString::number( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
1304  previewBkgrdColor = QColor( textStyleElem.attribute( "previewBkgrdColor", "#ffffff" ) );
1305 
1306 
1307  // text formatting
1308  QDomElement textFormatElem = elem.firstChildElement( "text-format" );
1309  wrapChar = textFormatElem.attribute( "wrapChar" );
1310  multilineHeight = textFormatElem.attribute( "multilineHeight", "1" ).toDouble();
1311  multilineAlign = static_cast< MultiLineAlign >( textFormatElem.attribute( "multilineAlign", QString::number( MultiFollowPlacement ) ).toUInt() );
1312  addDirectionSymbol = textFormatElem.attribute( "addDirectionSymbol" ).toInt();
1313  leftDirectionSymbol = textFormatElem.attribute( "leftDirectionSymbol", "<" );
1314  rightDirectionSymbol = textFormatElem.attribute( "rightDirectionSymbol", ">" );
1315  reverseDirectionSymbol = textFormatElem.attribute( "reverseDirectionSymbol" ).toInt();
1316  placeDirectionSymbol = static_cast< DirectionSymbols >( textFormatElem.attribute( "placeDirectionSymbol", QString::number( SymbolLeftRight ) ).toUInt() );
1317  formatNumbers = textFormatElem.attribute( "formatNumbers" ).toInt();
1318  decimals = textFormatElem.attribute( "decimals" ).toInt();
1319  plusSign = textFormatElem.attribute( "plussign" ).toInt();
1320 
1321  // text buffer
1322  QDomElement textBufferElem = elem.firstChildElement( "text-buffer" );
1323  double bufSize = textBufferElem.attribute( "bufferSize", "0" ).toDouble();
1324 
1325  // fix for buffer being keyed off of just its size in the past (<2.0)
1326  QVariant drawBuffer = textBufferElem.attribute( "bufferDraw" );
1327  if ( drawBuffer.isValid() )
1328  {
1329  bufferDraw = drawBuffer.toBool();
1330  bufferSize = bufSize;
1331  }
1332  else if ( bufSize != 0.0 )
1333  {
1334  bufferDraw = true;
1335  bufferSize = bufSize;
1336  }
1337  else
1338  {
1339  // keep bufferSize at new 1.0 default
1340  bufferDraw = false;
1341  }
1342 
1343  bufferSizeInMapUnits = textBufferElem.attribute( "bufferSizeInMapUnits" ).toInt();
1344  if ( !textBufferElem.hasAttribute( "bufferSizeMapUnitScale" ) )
1345  {
1346  //fallback to older property
1347  bufferSizeMapUnitScale.minScale = textBufferElem.attribute( "bufferSizeMapUnitMinScale", "0" ).toDouble();
1348  bufferSizeMapUnitScale.maxScale = textBufferElem.attribute( "bufferSizeMapUnitMaxScale", "0" ).toDouble();
1349  }
1350  else
1351  {
1352  bufferSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( textBufferElem.attribute( "bufferSizeMapUnitScale" ) );
1353  }
1354  bufferColor = QgsSymbolLayerV2Utils::decodeColor( textBufferElem.attribute( "bufferColor", QgsSymbolLayerV2Utils::encodeColor( Qt::white ) ) );
1355  bufferTransp = textBufferElem.attribute( "bufferTransp" ).toInt();
1357  static_cast< QgsMapRenderer::BlendMode >( textBufferElem.attribute( "bufferBlendMode", QString::number( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
1358  bufferJoinStyle = static_cast< Qt::PenJoinStyle >( textBufferElem.attribute( "bufferJoinStyle", QString::number( Qt::BevelJoin ) ).toUInt() );
1359  bufferNoFill = textBufferElem.attribute( "bufferNoFill", "0" ).toInt();
1360 
1361  // background
1362  QDomElement backgroundElem = elem.firstChildElement( "background" );
1363  shapeDraw = backgroundElem.attribute( "shapeDraw", "0" ).toInt();
1364  shapeType = static_cast< ShapeType >( backgroundElem.attribute( "shapeType", QString::number( ShapeRectangle ) ).toUInt() );
1365  shapeSVGFile = backgroundElem.attribute( "shapeSVGFile" );
1366  shapeSizeType = static_cast< SizeType >( backgroundElem.attribute( "shapeSizeType", QString::number( SizeBuffer ) ).toUInt() );
1367  shapeSize = QPointF( backgroundElem.attribute( "shapeSizeX", "0" ).toDouble(),
1368  backgroundElem.attribute( "shapeSizeY", "0" ).toDouble() );
1369  shapeSizeUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeSizeUnits", QString::number( MM ) ).toUInt() );
1370  if ( !backgroundElem.hasAttribute( "shapeSizeMapUnitScale" ) )
1371  {
1372  //fallback to older property
1373  shapeSizeMapUnitScale.minScale = backgroundElem.attribute( "shapeSizeMapUnitMinScale", "0" ).toDouble();
1374  shapeSizeMapUnitScale.maxScale = backgroundElem.attribute( "shapeSizeMapUnitMaxScale", "0" ).toDouble();
1375  }
1376  else
1377  {
1378  shapeSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeSizeMapUnitScale" ) );
1379  }
1380  shapeRotationType = static_cast< RotationType >( backgroundElem.attribute( "shapeRotationType", QString::number( RotationSync ) ).toUInt() );
1381  shapeRotation = backgroundElem.attribute( "shapeRotation", "0" ).toDouble();
1382  shapeOffset = QPointF( backgroundElem.attribute( "shapeOffsetX", "0" ).toDouble(),
1383  backgroundElem.attribute( "shapeOffsetY", "0" ).toDouble() );
1384  shapeOffsetUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeOffsetUnits", QString::number( MM ) ).toUInt() );
1385  if ( !backgroundElem.hasAttribute( "shapeOffsetMapUnitScale" ) )
1386  {
1387  //fallback to older property
1388  shapeOffsetMapUnitScale.minScale = backgroundElem.attribute( "shapeOffsetMapUnitMinScale", "0" ).toDouble();
1389  shapeOffsetMapUnitScale.maxScale = backgroundElem.attribute( "shapeOffsetMapUnitMaxScale", "0" ).toDouble();
1390  }
1391  else
1392  {
1393  shapeOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeOffsetMapUnitScale" ) );
1394  }
1395  shapeRadii = QPointF( backgroundElem.attribute( "shapeRadiiX", "0" ).toDouble(),
1396  backgroundElem.attribute( "shapeRadiiY", "0" ).toDouble() );
1397  shapeRadiiUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeRadiiUnits", QString::number( MM ) ).toUInt() );
1398  if ( !backgroundElem.hasAttribute( "shapeRadiiMapUnitScale" ) )
1399  {
1400  //fallback to older property
1401  shapeRadiiMapUnitScale.minScale = backgroundElem.attribute( "shapeRadiiMapUnitMinScale", "0" ).toDouble();
1402  shapeRadiiMapUnitScale.maxScale = backgroundElem.attribute( "shapeRadiiMapUnitMaxScale", "0" ).toDouble();
1403  }
1404  else
1405  {
1406  shapeRadiiMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeRadiiMapUnitScale" ) );
1407  }
1408  shapeFillColor = QgsSymbolLayerV2Utils::decodeColor( backgroundElem.attribute( "shapeFillColor", QgsSymbolLayerV2Utils::encodeColor( Qt::white ) ) );
1409  shapeBorderColor = QgsSymbolLayerV2Utils::decodeColor( backgroundElem.attribute( "shapeBorderColor", QgsSymbolLayerV2Utils::encodeColor( Qt::darkGray ) ) );
1410  shapeBorderWidth = backgroundElem.attribute( "shapeBorderWidth", "0" ).toDouble();
1411  shapeBorderWidthUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeBorderWidthUnits", QString::number( MM ) ).toUInt() );
1412  if ( !backgroundElem.hasAttribute( "shapeBorderWidthMapUnitScale" ) )
1413  {
1414  //fallback to older property
1415  shapeBorderWidthMapUnitScale.minScale = backgroundElem.attribute( "shapeBorderWidthMapUnitMinScale", "0" ).toDouble();
1416  shapeBorderWidthMapUnitScale.maxScale = backgroundElem.attribute( "shapeBorderWidthMapUnitMaxScale", "0" ).toDouble();
1417  }
1418  else
1419  {
1420  shapeBorderWidthMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeBorderWidthMapUnitScale" ) );
1421  }
1422  shapeJoinStyle = static_cast< Qt::PenJoinStyle >( backgroundElem.attribute( "shapeJoinStyle", QString::number( Qt::BevelJoin ) ).toUInt() );
1423  shapeTransparency = backgroundElem.attribute( "shapeTransparency", "0" ).toInt();
1425  static_cast< QgsMapRenderer::BlendMode >( backgroundElem.attribute( "shapeBlendMode", QString::number( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
1426 
1427  // drop shadow
1428  QDomElement shadowElem = elem.firstChildElement( "shadow" );
1429  shadowDraw = shadowElem.attribute( "shadowDraw", "0" ).toInt();
1430  shadowUnder = static_cast< ShadowType >( shadowElem.attribute( "shadowUnder", QString::number( ShadowLowest ) ).toUInt() );//ShadowLowest ;
1431  shadowOffsetAngle = shadowElem.attribute( "shadowOffsetAngle", "135" ).toInt();
1432  shadowOffsetDist = shadowElem.attribute( "shadowOffsetDist", "1" ).toDouble();
1433  shadowOffsetUnits = static_cast< SizeUnit >( shadowElem.attribute( "shadowOffsetUnits", QString::number( MM ) ).toUInt() );
1434  if ( !shadowElem.hasAttribute( "shadowOffsetMapUnitScale" ) )
1435  {
1436  //fallback to older property
1437  shadowOffsetMapUnitScale.minScale = shadowElem.attribute( "shadowOffsetMapUnitMinScale", "0" ).toDouble();
1438  shadowOffsetMapUnitScale.maxScale = shadowElem.attribute( "shadowOffsetMapUnitMaxScale", "0" ).toDouble();
1439  }
1440  else
1441  {
1442  shadowOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( shadowElem.attribute( "shadowOffsetMapUnitScale" ) );
1443  }
1444  shadowOffsetGlobal = shadowElem.attribute( "shadowOffsetGlobal", "1" ).toInt();
1445  shadowRadius = shadowElem.attribute( "shadowRadius", "1.5" ).toDouble();
1446  shadowRadiusUnits = static_cast< SizeUnit >( shadowElem.attribute( "shadowRadiusUnits", QString::number( MM ) ).toUInt() );
1447  if ( !shadowElem.hasAttribute( "shadowRadiusMapUnitScale" ) )
1448  {
1449  //fallback to older property
1450  shadowRadiusMapUnitScale.minScale = shadowElem.attribute( "shadowRadiusMapUnitMinScale", "0" ).toDouble();
1451  shadowRadiusMapUnitScale.maxScale = shadowElem.attribute( "shadowRadiusMapUnitMaxScale", "0" ).toDouble();
1452  }
1453  else
1454  {
1455  shadowRadiusMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( shadowElem.attribute( "shadowRadiusMapUnitScale" ) );
1456  }
1457  shadowRadiusAlphaOnly = shadowElem.attribute( "shadowRadiusAlphaOnly", "0" ).toInt();
1458  shadowTransparency = shadowElem.attribute( "shadowTransparency", "30" ).toInt();
1459  shadowScale = shadowElem.attribute( "shadowScale", "100" ).toInt();
1460  shadowColor = QgsSymbolLayerV2Utils::decodeColor( shadowElem.attribute( "shadowColor", QgsSymbolLayerV2Utils::encodeColor( Qt::black ) ) );
1462  static_cast< QgsMapRenderer::BlendMode >( shadowElem.attribute( "shadowBlendMode", QString::number( QgsMapRenderer::BlendMultiply ) ).toUInt() ) );
1463 
1464  // placement
1465  QDomElement placementElem = elem.firstChildElement( "placement" );
1466  placement = static_cast< Placement >( placementElem.attribute( "placement" ).toInt() );
1467  placementFlags = placementElem.attribute( "placementFlags" ).toUInt();
1468  centroidWhole = placementElem.attribute( "centroidWhole", "0" ).toInt();
1469  centroidInside = placementElem.attribute( "centroidInside", "0" ).toInt();
1470  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( placementElem.attribute( "predefinedPositionOrder" ) );
1472  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
1473  fitInPolygonOnly = placementElem.attribute( "fitInPolygonOnly", "0" ).toInt();
1474  dist = placementElem.attribute( "dist" ).toDouble();
1475  distInMapUnits = placementElem.attribute( "distInMapUnits" ).toInt();
1476  if ( !placementElem.hasAttribute( "distMapUnitScale" ) )
1477  {
1478  //fallback to older property
1479  distMapUnitScale.minScale = placementElem.attribute( "distMapUnitMinScale", "0" ).toDouble();
1480  distMapUnitScale.maxScale = placementElem.attribute( "distMapUnitMaxScale", "0" ).toDouble();
1481  }
1482  else
1483  {
1484  distMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( placementElem.attribute( "distMapUnitScale" ) );
1485  }
1486  offsetType = static_cast< OffsetType >( placementElem.attribute( "offsetType", QString::number( FromPoint ) ).toUInt() );
1487  quadOffset = static_cast< QuadrantPosition >( placementElem.attribute( "quadOffset", QString::number( QuadrantOver ) ).toUInt() );
1488  xOffset = placementElem.attribute( "xOffset", "0" ).toDouble();
1489  yOffset = placementElem.attribute( "yOffset", "0" ).toDouble();
1490  labelOffsetInMapUnits = placementElem.attribute( "labelOffsetInMapUnits", "1" ).toInt();
1491  if ( !placementElem.hasAttribute( "labelOffsetMapUnitScale" ) )
1492  {
1493  //fallback to older property
1494  labelOffsetMapUnitScale.minScale = placementElem.attribute( "labelOffsetMapUnitMinScale", "0" ).toDouble();
1495  labelOffsetMapUnitScale.maxScale = placementElem.attribute( "labelOffsetMapUnitMaxScale", "0" ).toDouble();
1496  }
1497  else
1498  {
1499  labelOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( placementElem.attribute( "labelOffsetMapUnitScale" ) );
1500  }
1501  angleOffset = placementElem.attribute( "angleOffset", "0" ).toDouble();
1502  preserveRotation = placementElem.attribute( "preserveRotation", "1" ).toInt();
1503  maxCurvedCharAngleIn = placementElem.attribute( "maxCurvedCharAngleIn", "25" ).toDouble();
1504  maxCurvedCharAngleOut = placementElem.attribute( "maxCurvedCharAngleOut", "-25" ).toDouble();
1505  priority = placementElem.attribute( "priority" ).toInt();
1506  repeatDistance = placementElem.attribute( "repeatDistance", "0" ).toDouble();
1507  repeatDistanceUnit = static_cast< SizeUnit >( placementElem.attribute( "repeatDistanceUnit", QString::number( MM ) ).toUInt() );
1508  if ( !placementElem.hasAttribute( "repeatDistanceMapUnitScale" ) )
1509  {
1510  //fallback to older property
1511  repeatDistanceMapUnitScale.minScale = placementElem.attribute( "repeatDistanceMapUnitMinScale", "0" ).toDouble();
1512  repeatDistanceMapUnitScale.maxScale = placementElem.attribute( "repeatDistanceMapUnitMaxScale", "0" ).toDouble();
1513  }
1514  else
1515  {
1516  repeatDistanceMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( placementElem.attribute( "repeatDistanceMapUnitScale" ) );
1517  }
1518 
1519  // rendering
1520  QDomElement renderingElem = elem.firstChildElement( "rendering" );
1521  scaleMin = renderingElem.attribute( "scaleMin", "0" ).toInt();
1522  scaleMax = renderingElem.attribute( "scaleMax", "0" ).toInt();
1523  scaleVisibility = renderingElem.attribute( "scaleVisibility" ).toInt();
1524 
1525  fontLimitPixelSize = renderingElem.attribute( "fontLimitPixelSize", "0" ).toInt();
1526  fontMinPixelSize = renderingElem.attribute( "fontMinPixelSize", "0" ).toInt();
1527  fontMaxPixelSize = renderingElem.attribute( "fontMaxPixelSize", "10000" ).toInt();
1528  displayAll = renderingElem.attribute( "displayAll", "0" ).toInt();
1529  upsidedownLabels = static_cast< UpsideDownLabels >( renderingElem.attribute( "upsidedownLabels", QString::number( Upright ) ).toUInt() );
1530 
1531  labelPerPart = renderingElem.attribute( "labelPerPart" ).toInt();
1532  mergeLines = renderingElem.attribute( "mergeLines" ).toInt();
1533  minFeatureSize = renderingElem.attribute( "minFeatureSize" ).toDouble();
1534  limitNumLabels = renderingElem.attribute( "limitNumLabels", "0" ).toInt();
1535  maxNumLabels = renderingElem.attribute( "maxNumLabels", "2000" ).toInt();
1536  obstacle = renderingElem.attribute( "obstacle", "1" ).toInt();
1537  obstacleFactor = renderingElem.attribute( "obstacleFactor", "1" ).toDouble();
1538  obstacleType = static_cast< ObstacleType >( renderingElem.attribute( "obstacleType", QString::number( PolygonInterior ) ).toUInt() );
1539  zIndex = renderingElem.attribute( "zIndex", "0.0" ).toDouble();
1540 
1541  QDomElement ddElem = elem.firstChildElement( "data-defined" );
1542  readDataDefinedPropertyMap( nullptr, &ddElem, dataDefinedProperties );
1543 }
1544 
1545 
1546 
1548 {
1549  // we assume (enabled == true && drawLabels == true) so those are not saved
1550 
1551  // text style
1552  QDomElement textStyleElem = doc.createElement( "text-style" );
1553  textStyleElem.setAttribute( "fieldName", fieldName );
1554  textStyleElem.setAttribute( "isExpression", isExpression );
1555  textStyleElem.setAttribute( "fontFamily", textFont.family() );
1556  textStyleElem.setAttribute( "namedStyle", QgsFontUtils::untranslateNamedStyle( textNamedStyle ) );
1557  textStyleElem.setAttribute( "fontSize", textFont.pointSizeF() );
1558  textStyleElem.setAttribute( "fontSizeInMapUnits", fontSizeInMapUnits );
1559  textStyleElem.setAttribute( "fontSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( fontSizeMapUnitScale ) );
1560  textStyleElem.setAttribute( "fontWeight", textFont.weight() );
1561  textStyleElem.setAttribute( "fontItalic", textFont.italic() );
1562  textStyleElem.setAttribute( "fontStrikeout", textFont.strikeOut() );
1563  textStyleElem.setAttribute( "fontUnderline", textFont.underline() );
1564  textStyleElem.setAttribute( "textColor", QgsSymbolLayerV2Utils::encodeColor( textColor ) );
1565  textStyleElem.setAttribute( "fontCapitals", static_cast< unsigned int >( textFont.capitalization() ) );
1566  textStyleElem.setAttribute( "fontLetterSpacing", textFont.letterSpacing() );
1567  textStyleElem.setAttribute( "fontWordSpacing", textFont.wordSpacing() );
1568  textStyleElem.setAttribute( "textTransp", textTransp );
1569  textStyleElem.setAttribute( "blendMode", QgsMapRenderer::getBlendModeEnum( blendMode ) );
1570  textStyleElem.setAttribute( "previewBkgrdColor", previewBkgrdColor.name() );
1571 
1572  // text formatting
1573  QDomElement textFormatElem = doc.createElement( "text-format" );
1574  textFormatElem.setAttribute( "wrapChar", wrapChar );
1575  textFormatElem.setAttribute( "multilineHeight", multilineHeight );
1576  textFormatElem.setAttribute( "multilineAlign", static_cast< unsigned int >( multilineAlign ) );
1577  textFormatElem.setAttribute( "addDirectionSymbol", addDirectionSymbol );
1578  textFormatElem.setAttribute( "leftDirectionSymbol", leftDirectionSymbol );
1579  textFormatElem.setAttribute( "rightDirectionSymbol", rightDirectionSymbol );
1580  textFormatElem.setAttribute( "reverseDirectionSymbol", reverseDirectionSymbol );
1581  textFormatElem.setAttribute( "placeDirectionSymbol", static_cast< unsigned int >( placeDirectionSymbol ) );
1582  textFormatElem.setAttribute( "formatNumbers", formatNumbers );
1583  textFormatElem.setAttribute( "decimals", decimals );
1584  textFormatElem.setAttribute( "plussign", plusSign );
1585 
1586  // text buffer
1587  QDomElement textBufferElem = doc.createElement( "text-buffer" );
1588  textBufferElem.setAttribute( "bufferDraw", bufferDraw );
1589  textBufferElem.setAttribute( "bufferSize", bufferSize );
1590  textBufferElem.setAttribute( "bufferSizeInMapUnits", bufferSizeInMapUnits );
1591  textBufferElem.setAttribute( "bufferSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( bufferSizeMapUnitScale ) );
1592  textBufferElem.setAttribute( "bufferColor", QgsSymbolLayerV2Utils::encodeColor( bufferColor ) );
1593  textBufferElem.setAttribute( "bufferNoFill", bufferNoFill );
1594  textBufferElem.setAttribute( "bufferTransp", bufferTransp );
1595  textBufferElem.setAttribute( "bufferJoinStyle", static_cast< unsigned int >( bufferJoinStyle ) );
1596  textBufferElem.setAttribute( "bufferBlendMode", QgsMapRenderer::getBlendModeEnum( bufferBlendMode ) );
1597 
1598  // background
1599  QDomElement backgroundElem = doc.createElement( "background" );
1600  backgroundElem.setAttribute( "shapeDraw", shapeDraw );
1601  backgroundElem.setAttribute( "shapeType", static_cast< unsigned int >( shapeType ) );
1602  backgroundElem.setAttribute( "shapeSVGFile", shapeSVGFile );
1603  backgroundElem.setAttribute( "shapeSizeType", static_cast< unsigned int >( shapeSizeType ) );
1604  backgroundElem.setAttribute( "shapeSizeX", shapeSize.x() );
1605  backgroundElem.setAttribute( "shapeSizeY", shapeSize.y() );
1606  backgroundElem.setAttribute( "shapeSizeUnits", static_cast< unsigned int >( shapeSizeUnits ) );
1607  backgroundElem.setAttribute( "shapeSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeSizeMapUnitScale ) );
1608  backgroundElem.setAttribute( "shapeRotationType", static_cast< unsigned int >( shapeRotationType ) );
1609  backgroundElem.setAttribute( "shapeRotation", shapeRotation );
1610  backgroundElem.setAttribute( "shapeOffsetX", shapeOffset.x() );
1611  backgroundElem.setAttribute( "shapeOffsetY", shapeOffset.y() );
1612  backgroundElem.setAttribute( "shapeOffsetUnits", static_cast< unsigned int >( shapeOffsetUnits ) );
1613  backgroundElem.setAttribute( "shapeOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeOffsetMapUnitScale ) );
1614  backgroundElem.setAttribute( "shapeRadiiX", shapeRadii.x() );
1615  backgroundElem.setAttribute( "shapeRadiiY", shapeRadii.y() );
1616  backgroundElem.setAttribute( "shapeRadiiUnits", static_cast< unsigned int >( shapeRadiiUnits ) );
1617  backgroundElem.setAttribute( "shapeRadiiMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeRadiiMapUnitScale ) );
1618  backgroundElem.setAttribute( "shapeFillColor", QgsSymbolLayerV2Utils::encodeColor( shapeFillColor ) );
1619  backgroundElem.setAttribute( "shapeBorderColor", QgsSymbolLayerV2Utils::encodeColor( shapeBorderColor ) );
1620  backgroundElem.setAttribute( "shapeBorderWidth", shapeBorderWidth );
1621  backgroundElem.setAttribute( "shapeBorderWidthUnits", static_cast< unsigned int >( shapeBorderWidthUnits ) );
1622  backgroundElem.setAttribute( "shapeBorderWidthMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeBorderWidthMapUnitScale ) );
1623  backgroundElem.setAttribute( "shapeJoinStyle", static_cast< unsigned int >( shapeJoinStyle ) );
1624  backgroundElem.setAttribute( "shapeTransparency", shapeTransparency );
1625  backgroundElem.setAttribute( "shapeBlendMode", QgsMapRenderer::getBlendModeEnum( shapeBlendMode ) );
1626 
1627  // drop shadow
1628  QDomElement shadowElem = doc.createElement( "shadow" );
1629  shadowElem.setAttribute( "shadowDraw", shadowDraw );
1630  shadowElem.setAttribute( "shadowUnder", static_cast< unsigned int >( shadowUnder ) );
1631  shadowElem.setAttribute( "shadowOffsetAngle", shadowOffsetAngle );
1632  shadowElem.setAttribute( "shadowOffsetDist", shadowOffsetDist );
1633  shadowElem.setAttribute( "shadowOffsetUnits", static_cast< unsigned int >( shadowOffsetUnits ) );
1634  shadowElem.setAttribute( "shadowOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowOffsetMapUnitScale ) );
1635  shadowElem.setAttribute( "shadowOffsetGlobal", shadowOffsetGlobal );
1636  shadowElem.setAttribute( "shadowRadius", shadowRadius );
1637  shadowElem.setAttribute( "shadowRadiusUnits", static_cast< unsigned int >( shadowRadiusUnits ) );
1638  shadowElem.setAttribute( "shadowRadiusMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowRadiusMapUnitScale ) );
1639  shadowElem.setAttribute( "shadowRadiusAlphaOnly", shadowRadiusAlphaOnly );
1640  shadowElem.setAttribute( "shadowTransparency", shadowTransparency );
1641  shadowElem.setAttribute( "shadowScale", shadowScale );
1642  shadowElem.setAttribute( "shadowColor", QgsSymbolLayerV2Utils::encodeColor( shadowColor ) );
1643  shadowElem.setAttribute( "shadowBlendMode", QgsMapRenderer::getBlendModeEnum( shadowBlendMode ) );
1644 
1645  // placement
1646  QDomElement placementElem = doc.createElement( "placement" );
1647  placementElem.setAttribute( "placement", placement );
1648  placementElem.setAttribute( "placementFlags", static_cast< unsigned int >( placementFlags ) );
1649  placementElem.setAttribute( "centroidWhole", centroidWhole );
1650  placementElem.setAttribute( "centroidInside", centroidInside );
1651  placementElem.setAttribute( "predefinedPositionOrder", QgsLabelingUtils::encodePredefinedPositionOrder( predefinedPositionOrder ) );
1652  placementElem.setAttribute( "fitInPolygonOnly", fitInPolygonOnly );
1653  placementElem.setAttribute( "dist", dist );
1654  placementElem.setAttribute( "distInMapUnits", distInMapUnits );
1655  placementElem.setAttribute( "distMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( distMapUnitScale ) );
1656  placementElem.setAttribute( "offsetType", static_cast< unsigned int >( offsetType ) );
1657  placementElem.setAttribute( "quadOffset", static_cast< unsigned int >( quadOffset ) );
1658  placementElem.setAttribute( "xOffset", xOffset );
1659  placementElem.setAttribute( "yOffset", yOffset );
1660  placementElem.setAttribute( "labelOffsetInMapUnits", labelOffsetInMapUnits );
1661  placementElem.setAttribute( "labelOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( labelOffsetMapUnitScale ) );
1662  placementElem.setAttribute( "angleOffset", angleOffset );
1663  placementElem.setAttribute( "preserveRotation", preserveRotation );
1664  placementElem.setAttribute( "maxCurvedCharAngleIn", maxCurvedCharAngleIn );
1665  placementElem.setAttribute( "maxCurvedCharAngleOut", maxCurvedCharAngleOut );
1666  placementElem.setAttribute( "priority", priority );
1667  placementElem.setAttribute( "repeatDistance", repeatDistance );
1668  placementElem.setAttribute( "repeatDistanceUnit", repeatDistanceUnit );
1669  placementElem.setAttribute( "repeatDistanceMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( repeatDistanceMapUnitScale ) );
1670 
1671  // rendering
1672  QDomElement renderingElem = doc.createElement( "rendering" );
1673  renderingElem.setAttribute( "scaleVisibility", scaleVisibility );
1674  renderingElem.setAttribute( "scaleMin", scaleMin );
1675  renderingElem.setAttribute( "scaleMax", scaleMax );
1676  renderingElem.setAttribute( "fontLimitPixelSize", fontLimitPixelSize );
1677  renderingElem.setAttribute( "fontMinPixelSize", fontMinPixelSize );
1678  renderingElem.setAttribute( "fontMaxPixelSize", fontMaxPixelSize );
1679  renderingElem.setAttribute( "displayAll", displayAll );
1680  renderingElem.setAttribute( "upsidedownLabels", static_cast< unsigned int >( upsidedownLabels ) );
1681 
1682  renderingElem.setAttribute( "labelPerPart", labelPerPart );
1683  renderingElem.setAttribute( "mergeLines", mergeLines );
1684  renderingElem.setAttribute( "minFeatureSize", minFeatureSize );
1685  renderingElem.setAttribute( "limitNumLabels", limitNumLabels );
1686  renderingElem.setAttribute( "maxNumLabels", maxNumLabels );
1687  renderingElem.setAttribute( "obstacle", obstacle );
1688  renderingElem.setAttribute( "obstacleFactor", obstacleFactor );
1689  renderingElem.setAttribute( "obstacleType", static_cast< unsigned int >( obstacleType ) );
1690  renderingElem.setAttribute( "zIndex", zIndex );
1691 
1692  QDomElement ddElem = doc.createElement( "data-defined" );
1693  writeDataDefinedPropertyMap( nullptr, &ddElem, dataDefinedProperties );
1694 
1695  QDomElement elem = doc.createElement( "settings" );
1696  elem.appendChild( textStyleElem );
1697  elem.appendChild( textFormatElem );
1698  elem.appendChild( textBufferElem );
1699  elem.appendChild( backgroundElem );
1700  elem.appendChild( shadowElem );
1701  elem.appendChild( placementElem );
1702  elem.appendChild( renderingElem );
1703  elem.appendChild( ddElem );
1704  return elem;
1705 }
1706 
1708  bool active, bool useExpr, const QString& expr, const QString& field )
1709 {
1710  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
1711 
1712  if ( dataDefinedProperties.contains( p ) )
1713  {
1715  if ( it != dataDefinedProperties.constEnd() )
1716  {
1717  QgsDataDefined* dd = it.value();
1718  dd->setActive( active );
1719  dd->setExpressionString( expr );
1720  dd->setField( field );
1721  dd->setUseExpression( useExpr );
1722  }
1723  }
1724  else if ( !defaultVals )
1725  {
1726  QgsDataDefined* dd = new QgsDataDefined( active, useExpr, expr, field );
1727  dataDefinedProperties.insert( p, dd );
1728  }
1729 }
1730 
1732 {
1734  if ( it != dataDefinedProperties.end() )
1735  {
1736  delete( it.value() );
1738  }
1739 }
1740 
1742 {
1743  qDeleteAll( dataDefinedProperties );
1745 }
1746 
1748 {
1749  // TODO: update or remove this when project settings for labeling are migrated to better XML layout
1750  QString newValue = value;
1751  if ( !value.isEmpty() && !value.contains( "~~" ) )
1752  {
1753  QStringList values;
1754  values << "1"; // all old-style values are active if not empty
1755  values << "0";
1756  values << "";
1757  values << value; // all old-style values are only field names
1758  newValue = values.join( "~~" );
1759  }
1760 
1761  return newValue;
1762 }
1763 
1765 {
1767  return nullptr;
1768 
1770  if ( it != dataDefinedProperties.constEnd() )
1771  {
1772  return it.value();
1773  }
1774  return nullptr;
1775 }
1776 
1778 {
1781  if ( it != dataDefinedProperties.constEnd() )
1782  {
1783  return it.value()->toMap();
1784  }
1785  return map;
1786 }
1787 
1789 {
1791  {
1792  return QVariant();
1793  }
1794 
1795  //try to keep < 2.12 API - handle no passed expression context
1797  if ( !context )
1798  {
1799  scopedEc.reset( new QgsExpressionContext() );
1800  scopedEc->setFeature( f );
1801  scopedEc->setFields( fields );
1802  }
1803  const QgsExpressionContext* ec = context ? context : scopedEc.data();
1804 
1805  QgsDataDefined* dd = nullptr;
1807  if ( it != dataDefinedProperties.constEnd() )
1808  {
1809  dd = it.value();
1810  }
1811 
1812  if ( !dd )
1813  {
1814  return QVariant();
1815  }
1816 
1817  if ( !dd->isActive() )
1818  {
1819  return QVariant();
1820  }
1821 
1822  QVariant result = QVariant();
1823  bool useExpression = dd->useExpression();
1824  QString field = dd->field();
1825 
1826  //QgsDebugMsgLevel( QString( "isActive:" ) + isActive ? "1" : "0", 4 );
1827  //QgsDebugMsgLevel( QString( "useExpression:" ) + useExpression ? "1" : "0", 4 );
1828  //QgsDebugMsgLevel( QString( "expression:" ) + dd->expressionString(), 4 );
1829  //QgsDebugMsgLevel( QString( "field:" ) + field, 4 );
1830 
1831  if ( useExpression && dd->expressionIsPrepared() )
1832  {
1833  QgsExpression* expr = dd->expression();
1834  //QgsDebugMsgLevel( QString( "expr columns:" ) + expr->referencedColumns().join( "," ), 4 );
1835 
1836  result = expr->evaluate( ec );
1837  if ( expr->hasEvalError() )
1838  {
1839  QgsDebugMsgLevel( QString( "Evaluate error:" ) + expr->evalErrorString(), 4 );
1840  return QVariant();
1841  }
1842  }
1843  else if ( !useExpression && !field.isEmpty() )
1844  {
1845  // use direct attribute access instead of evaluating "field" expression (much faster)
1846  int indx = fields.indexFromName( field );
1847  if ( indx != -1 )
1848  {
1849  result = f.attribute( indx );
1850  }
1851  }
1852  return result;
1853 }
1854 
1856 {
1857  // null passed-around QVariant
1858  exprVal.clear();
1860  return false;
1861 
1862  //try to keep < 2.12 API - handle no passed expression context
1864  if ( !context )
1865  {
1866  scopedEc.reset( new QgsExpressionContext() );
1867  scopedEc->setFeature( *mCurFeat );
1868  scopedEc->setFields( mCurFields );
1869  }
1870  QgsExpressionContext* ec = context ? context : scopedEc.data();
1871 
1872  ec->setOriginalValueVariable( originalValue );
1873  QVariant result = dataDefinedValue( p, *mCurFeat, mCurFields, ec );
1874 
1875  if ( result.isValid() && !result.isNull() )
1876  {
1877  //QgsDebugMsgLevel( QString( "result type:" ) + QString( result.typeName() ), 4 );
1878  //QgsDebugMsgLevel( QString( "result string:" ) + result.toString(), 4 );
1879  exprVal = result;
1880  return true;
1881  }
1882 
1883  return false;
1884 }
1885 
1887 {
1889  return false;
1890 
1891  bool isActive = false;
1892 
1894  if ( it != dataDefinedProperties.constEnd() )
1895  {
1896  isActive = it.value()->isActive();
1897  }
1898 
1899  return isActive;
1900 }
1901 
1903 {
1905  return false;
1906 
1907  bool useExpression = false;
1909  if ( it != dataDefinedProperties.constEnd() )
1910  {
1911  useExpression = it.value()->useExpression();
1912  }
1913 
1914  return useExpression;
1915 }
1916 
1917 bool QgsPalLayerSettings::checkMinimumSizeMM( const QgsRenderContext& ct, const QgsGeometry* geom, double minSize ) const
1918 {
1919  return QgsPalLabeling::checkMinimumSizeMM( ct, geom, minSize );
1920 }
1921 
1922 void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString text, double& labelX, double& labelY, QgsFeature* f, QgsRenderContext *context )
1923 {
1924  if ( !fm || !f )
1925  {
1926  return;
1927  }
1928 
1929  //try to keep < 2.12 API - handle no passed render context
1931  if ( !context )
1932  {
1933  scopedRc.reset( new QgsRenderContext() );
1934  if ( f )
1935  scopedRc->expressionContext().setFeature( *f );
1936  }
1937  QgsRenderContext* rc = context ? context : scopedRc.data();
1938 
1939  QString wrapchr = wrapChar;
1940  double multilineH = multilineHeight;
1941 
1942  bool addDirSymb = addDirectionSymbol;
1943  QString leftDirSymb = leftDirectionSymbol;
1944  QString rightDirSymb = rightDirectionSymbol;
1946 
1947  if ( f == mCurFeat ) // called internally, use any stored data defined values
1948  {
1949  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
1950  {
1951  wrapchr = dataDefinedValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
1952  }
1953 
1954  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
1955  {
1956  multilineH = dataDefinedValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
1957  }
1958 
1959  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
1960  {
1961  addDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
1962  }
1963 
1964  if ( addDirSymb )
1965  {
1966 
1967  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
1968  {
1969  leftDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
1970  }
1971  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbRight ) )
1972  {
1973  rightDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
1974  }
1975 
1976  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
1977  {
1978  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( dataDefinedValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt() );
1979  }
1980 
1981  }
1982 
1983  }
1984  else // called externally with passed-in feature, evaluate data defined
1985  {
1988  if ( exprVal.isValid() )
1989  {
1990  wrapchr = exprVal.toString();
1991  }
1992  exprVal.clear();
1993  rc->expressionContext().setOriginalValueVariable( multilineH );
1995  if ( exprVal.isValid() )
1996  {
1997  bool ok;
1998  double size = exprVal.toDouble( &ok );
1999  if ( ok )
2000  {
2001  multilineH = size;
2002  }
2003  }
2004 
2005  exprVal.clear();
2006  rc->expressionContext().setOriginalValueVariable( addDirSymb );
2008  if ( exprVal.isValid() )
2009  {
2010  addDirSymb = exprVal.toBool();
2011  }
2012 
2013  if ( addDirSymb ) // don't do extra evaluations if not adding a direction symbol
2014  {
2015  exprVal.clear();
2016  rc->expressionContext().setOriginalValueVariable( leftDirSymb );
2018  if ( exprVal.isValid() )
2019  {
2020  leftDirSymb = exprVal.toString();
2021  }
2022  exprVal.clear();
2023  rc->expressionContext().setOriginalValueVariable( rightDirSymb );
2025  if ( exprVal.isValid() )
2026  {
2027  rightDirSymb = exprVal.toString();
2028  }
2029  exprVal.clear();
2030  rc->expressionContext().setOriginalValueVariable( static_cast< int >( placeDirSymb ) );
2032  if ( exprVal.isValid() )
2033  {
2034  bool ok;
2035  int enmint = exprVal.toInt( &ok );
2036  if ( ok )
2037  {
2038  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( enmint );
2039  }
2040  }
2041  }
2042 
2043  }
2044 
2045  if ( wrapchr.isEmpty() )
2046  {
2047  wrapchr = QLatin1String( "\n" ); // default to new line delimiter
2048  }
2049 
2050  //consider the space needed for the direction symbol
2051  if ( addDirSymb && placement == QgsPalLayerSettings::Line
2052  && ( !leftDirSymb.isEmpty() || !rightDirSymb.isEmpty() ) )
2053  {
2054  QString dirSym = leftDirSymb;
2055 
2056  if ( fm->width( rightDirSymb ) > fm->width( dirSym ) )
2057  dirSym = rightDirSymb;
2058 
2059  if ( placeDirSymb == QgsPalLayerSettings::SymbolLeftRight )
2060  {
2061  text.append( dirSym );
2062  }
2063  else
2064  {
2065  text.prepend( dirSym + QLatin1String( "\n" ) ); // SymbolAbove or SymbolBelow
2066  }
2067  }
2068 
2069  double w = 0.0, h = 0.0;
2070  QStringList multiLineSplit = QgsPalLabeling::splitToLines( text, wrapchr );
2071  int lines = multiLineSplit.size();
2072 
2073  double labelHeight = fm->ascent() + fm->descent(); // ignore +1 for baseline
2074 
2075  h += fm->height() + static_cast< double >(( lines - 1 ) * labelHeight * multilineH );
2076  h /= rasterCompressFactor;
2077 
2078  for ( int i = 0; i < lines; ++i )
2079  {
2080  double width = fm->width( multiLineSplit.at( i ) );
2081  if ( width > w )
2082  {
2083  w = width;
2084  }
2085  }
2086  w /= rasterCompressFactor;
2087 
2088 #if 0 // XXX strk
2089  QgsPoint ptSize = xform->toMapCoordinatesF( w, h );
2090  labelX = qAbs( ptSize.x() - ptZero.x() );
2091  labelY = qAbs( ptSize.y() - ptZero.y() );
2092 #else
2093  double uPP = xform->mapUnitsPerPixel();
2094  labelX = w * uPP;
2095  labelY = h * uPP;
2096 #endif
2097 }
2098 
2099 void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &context, QgsLabelFeature** labelFeature , QgsGeometry* obstacleGeometry )
2100 {
2101  // either used in QgsPalLabeling (palLayer is set) or in QgsLabelingEngineV2 (labelFeature is set)
2102  Q_ASSERT( labelFeature );
2103 
2104  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2105  mCurFeat = &f;
2106 
2107  // data defined is obstacle? calculate this first, to avoid wasting time working with obstacles we don't require
2108  bool isObstacle = obstacle; // start with layer default
2110  {
2111  isObstacle = exprVal.toBool();
2112  }
2113 
2114  if ( !drawLabels )
2115  {
2116  if ( isObstacle )
2117  {
2118  registerObstacleFeature( f, context, labelFeature, obstacleGeometry );
2119  }
2120  return;
2121  }
2122 
2123 // mCurFields = &layer->pendingFields();
2124 
2125  // store data defined-derived values for later adding to label feature for use during rendering
2126  dataDefinedValues.clear();
2127 
2128  // data defined show label? defaults to show label if not 0
2130  {
2131  bool showLabel = dataDefinedEvaluate( QgsPalLayerSettings::Show, exprVal, &context.expressionContext(), true );
2132  showLabel &= exprVal.toBool();
2133  QgsDebugMsgLevel( QString( "exprVal Show:%1" ).arg( showLabel ? "true" : "false" ), 4 );
2134  if ( !showLabel )
2135  {
2136  return;
2137  }
2138  }
2139 
2140  // data defined scale visibility?
2141  bool useScaleVisibility = scaleVisibility;
2143  {
2144  QgsDebugMsgLevel( QString( "exprVal ScaleVisibility:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
2145  useScaleVisibility = exprVal.toBool();
2146  }
2147 
2148  if ( useScaleVisibility )
2149  {
2150  // data defined min scale?
2151  double minScale = scaleMin;
2153  {
2154  QgsDebugMsgLevel( QString( "exprVal MinScale:%1" ).arg( exprVal.toDouble() ), 4 );
2155  bool conversionOk;
2156  double mins = exprVal.toDouble( &conversionOk );
2157  if ( conversionOk )
2158  {
2159  minScale = mins;
2160  }
2161  }
2162 
2163  // scales closer than 1:1
2164  if ( minScale < 0 )
2165  {
2166  minScale = 1 / qAbs( minScale );
2167  }
2168 
2169  if ( !qgsDoubleNear( minScale, 0.0 ) && context.rendererScale() < minScale )
2170  {
2171  return;
2172  }
2173 
2174  // data defined max scale?
2175  double maxScale = scaleMax;
2177  {
2178  QgsDebugMsgLevel( QString( "exprVal MaxScale:%1" ).arg( exprVal.toDouble() ), 4 );
2179  bool conversionOk;
2180  double maxs = exprVal.toDouble( &conversionOk );
2181  if ( conversionOk )
2182  {
2183  maxScale = maxs;
2184  }
2185  }
2186 
2187  // scales closer than 1:1
2188  if ( maxScale < 0 )
2189  {
2190  maxScale = 1 / qAbs( maxScale );
2191  }
2192 
2193  if ( !qgsDoubleNear( maxScale, 0.0 ) && context.rendererScale() > maxScale )
2194  {
2195  return;
2196  }
2197  }
2198 
2199  QFont labelFont = textFont;
2200  // labelFont will be added to label feature for use during label painting
2201 
2202  // data defined font units?
2205  {
2206  QString units = exprVal.toString().trimmed();
2207  QgsDebugMsgLevel( QString( "exprVal Font units:%1" ).arg( units ), 4 );
2208  if ( !units.isEmpty() )
2209  {
2210  fontunits = _decodeUnits( units );
2211  }
2212  }
2213 
2214  //data defined label size?
2215  double fontSize = labelFont.pointSizeF(); // font size doesn't have its own class data member
2216  if ( dataDefinedEvaluate( QgsPalLayerSettings::Size, exprVal, &context.expressionContext(), fontSize ) )
2217  {
2218  QgsDebugMsgLevel( QString( "exprVal Size:%1" ).arg( exprVal.toDouble() ), 4 );
2219  bool ok;
2220  double size = exprVal.toDouble( &ok );
2221  if ( ok )
2222  {
2223  fontSize = size;
2224  }
2225  }
2226  if ( fontSize <= 0.0 )
2227  {
2228  return;
2229  }
2230 
2231  int fontPixelSize = sizeToPixel( fontSize, context, fontunits, true, fontSizeMapUnitScale );
2232  // don't try to show font sizes less than 1 pixel (Qt complains)
2233  if ( fontPixelSize < 1 )
2234  {
2235  return;
2236  }
2237  labelFont.setPixelSize( fontPixelSize );
2238 
2239  // NOTE: labelFont now always has pixelSize set, so pointSize or pointSizeF might return -1
2240 
2241  // defined 'minimum/maximum pixel font size'?
2242  if ( fontunits == QgsPalLayerSettings::MapUnits )
2243  {
2244  bool useFontLimitPixelSize = fontLimitPixelSize;
2246  {
2247  QgsDebugMsgLevel( QString( "exprVal FontLimitPixel:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
2248  useFontLimitPixelSize = exprVal.toBool();
2249  }
2250 
2251  if ( useFontLimitPixelSize )
2252  {
2253  int fontMinPixel = fontMinPixelSize;
2255  {
2256  bool ok;
2257  int sizeInt = exprVal.toInt( &ok );
2258  QgsDebugMsgLevel( QString( "exprVal FontMinPixel:%1" ).arg( sizeInt ), 4 );
2259  if ( ok )
2260  {
2261  fontMinPixel = sizeInt;
2262  }
2263  }
2264 
2265  int fontMaxPixel = fontMaxPixelSize;
2267  {
2268  bool ok;
2269  int sizeInt = exprVal.toInt( &ok );
2270  QgsDebugMsgLevel( QString( "exprVal FontMaxPixel:%1" ).arg( sizeInt ), 4 );
2271  if ( ok )
2272  {
2273  fontMaxPixel = sizeInt;
2274  }
2275  }
2276 
2277  if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
2278  {
2279  return;
2280  }
2281  }
2282  }
2283 
2284  // NOTE: the following parsing functions calculate and store any data defined values for later use in QgsPalLabeling::drawLabeling
2285  // this is done to provide clarity, and because such parsing is not directly related to PAL feature registration calculations
2286 
2287  // calculate rest of font attributes and store any data defined values
2288  // this is done here for later use in making label backgrounds part of collision management (when implemented)
2289  parseTextStyle( labelFont, fontunits, context );
2290  parseTextFormatting( context );
2291  parseTextBuffer( context );
2292  parseShapeBackground( context );
2293  parseDropShadow( context );
2294 
2295  QString labelText;
2296 
2297  // Check to see if we are a expression string.
2298  if ( isExpression )
2299  {
2301  if ( exp->hasParserError() )
2302  {
2303  QgsDebugMsgLevel( QString( "Expression parser error:%1" ).arg( exp->parserErrorString() ), 4 );
2304  return;
2305  }
2306  exp->setScale( context.rendererScale() );
2307 // QVariant result = exp->evaluate( &f, layer->pendingFields() );
2308  QVariant result = exp->evaluate( &context.expressionContext() ); // expression prepared in QgsPalLabeling::prepareLayer()
2309  if ( exp->hasEvalError() )
2310  {
2311  QgsDebugMsgLevel( QString( "Expression parser eval error:%1" ).arg( exp->evalErrorString() ), 4 );
2312  return;
2313  }
2314  labelText = result.isNull() ? "" : result.toString();
2315  }
2316  else
2317  {
2318  const QVariant &v = f.attribute( fieldIndex );
2319  labelText = v.isNull() ? "" : v.toString();
2320  }
2321 
2322  // data defined format numbers?
2323  bool formatnum = formatNumbers;
2325  {
2326  formatnum = exprVal.toBool();
2327  QgsDebugMsgLevel( QString( "exprVal NumFormat:%1" ).arg( formatnum ? "true" : "false" ), 4 );
2328  }
2329 
2330  // format number if label text is coercible to a number
2331  if ( formatnum )
2332  {
2333  // data defined decimal places?
2334  int decimalPlaces = decimals;
2336  {
2337  bool ok;
2338  int dInt = exprVal.toInt( &ok );
2339  QgsDebugMsgLevel( QString( "exprVal NumDecimals:%1" ).arg( dInt ), 4 );
2340  if ( ok && dInt > 0 ) // needs to be positive
2341  {
2342  decimalPlaces = dInt;
2343  }
2344  }
2345 
2346  // data defined plus sign?
2347  bool signPlus = plusSign;
2349  {
2350  signPlus = exprVal.toBool();
2351  QgsDebugMsgLevel( QString( "exprVal NumPlusSign:%1" ).arg( signPlus ? "true" : "false" ), 4 );
2352  }
2353 
2354  QVariant textV( labelText );
2355  bool ok;
2356  double d = textV.toDouble( &ok );
2357  if ( ok )
2358  {
2359  QString numberFormat;
2360  if ( d > 0 && signPlus )
2361  {
2362  numberFormat.append( '+' );
2363  }
2364  numberFormat.append( "%1" );
2365  labelText = numberFormat.arg( d, 0, 'f', decimalPlaces );
2366  }
2367  }
2368 
2369  // NOTE: this should come AFTER any option that affects font metrics
2370  QScopedPointer<QFontMetricsF> labelFontMetrics( new QFontMetricsF( labelFont ) );
2371  double labelX, labelY; // will receive label size
2372  calculateLabelSize( labelFontMetrics.data(), labelText, labelX, labelY, mCurFeat, &context );
2373 
2374 
2375  // maximum angle between curved label characters (hardcoded defaults used in QGIS <2.0)
2376  //
2377  double maxcharanglein = 20.0; // range 20.0-60.0
2378  double maxcharangleout = -20.0; // range 20.0-95.0
2379 
2381  {
2382  maxcharanglein = maxCurvedCharAngleIn;
2383  maxcharangleout = maxCurvedCharAngleOut;
2384 
2385  //data defined maximum angle between curved label characters?
2387  {
2388  QString ptstr = exprVal.toString().trimmed();
2389  QgsDebugMsgLevel( QString( "exprVal CurvedCharAngleInOut:%1" ).arg( ptstr ), 4 );
2390 
2391  if ( !ptstr.isEmpty() )
2392  {
2393  QPointF maxcharanglePt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
2394  maxcharanglein = qBound( 20.0, static_cast< double >( maxcharanglePt.x() ), 60.0 );
2395  maxcharangleout = qBound( 20.0, static_cast< double >( maxcharanglePt.y() ), 95.0 );
2396  }
2397  }
2398  // make sure maxcharangleout is always negative
2399  maxcharangleout = -( qAbs( maxcharangleout ) );
2400  }
2401 
2402  // data defined centroid whole or clipped?
2403  bool wholeCentroid = centroidWhole;
2405  {
2406  QString str = exprVal.toString().trimmed();
2407  QgsDebugMsgLevel( QString( "exprVal CentroidWhole:%1" ).arg( str ), 4 );
2408 
2409  if ( !str.isEmpty() )
2410  {
2411  if ( str.compare( "Visible", Qt::CaseInsensitive ) == 0 )
2412  {
2413  wholeCentroid = false;
2414  }
2415  else if ( str.compare( "Whole", Qt::CaseInsensitive ) == 0 )
2416  {
2417  wholeCentroid = true;
2418  }
2419  }
2420  }
2421 
2422  const QgsGeometry* geom = f.constGeometry();
2423  if ( !geom )
2424  {
2425  return;
2426  }
2427 
2428  // simplify?
2429  const QgsVectorSimplifyMethod &simplifyMethod = context.vectorSimplifyMethod();
2430  QScopedPointer<QgsGeometry> scopedClonedGeom;
2431  if ( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && simplifyMethod.forceLocalOptimization() )
2432  {
2433  int simplifyHints = simplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
2435  QgsGeometry* g = new QgsGeometry( *geom );
2436 
2437  if ( QgsMapToPixelSimplifier::simplifyGeometry( g, simplifyHints, simplifyMethod.tolerance(), simplifyAlgorithm ) )
2438  {
2439  geom = g;
2440  scopedClonedGeom.reset( g );
2441  }
2442  else
2443  {
2444  delete g;
2445  }
2446  }
2447 
2448  // whether we're going to create a centroid for polygon
2449  bool centroidPoly = (( placement == QgsPalLayerSettings::AroundPoint
2451  && geom->type() == QGis::Polygon );
2452 
2453  // CLIP the geometry if it is bigger than the extent
2454  // don't clip if centroid is requested for whole feature
2455  bool doClip = false;
2456  if ( !centroidPoly || !wholeCentroid )
2457  {
2458  doClip = true;
2459  }
2460 
2461  // if using fitInPolygonOnly option, generate the permissible zone (must happen before geometry is modified - eg
2462  // as a result of using perimeter based labeling and the geometry is converted to a boundary)
2463  QgsGeometry permissibleZone;
2464  if ( geom->type() == QGis::Polygon && fitInPolygonOnly )
2465  {
2466  permissibleZone = *geom;
2467  if ( QgsPalLabeling::geometryRequiresPreparation( &permissibleZone, context, ct, doClip ? extentGeom : nullptr ) )
2468  {
2469  QgsGeometry* preparedZone = QgsPalLabeling::prepareGeometry( &permissibleZone, context, ct, doClip ? extentGeom : nullptr );
2470  permissibleZone = *preparedZone;
2471  delete preparedZone;
2472  }
2473  }
2474 
2475  // if using perimeter based labeling for polygons, get the polygon's
2476  // linear boundary and use that for the label geometry
2477  if (( geom->type() == QGis::Polygon )
2478  && ( placement == Line || placement == PerimeterCurved ) )
2479  {
2480  QgsGeometry* boundaryGeom = new QgsGeometry( geom->geometry()->boundary() );
2481  geom = boundaryGeom;
2482  scopedClonedGeom.reset( boundaryGeom );
2483  }
2484 
2485  const GEOSGeometry* geos_geom = nullptr;
2486  const QgsGeometry* preparedGeom = geom;
2487  QScopedPointer<QgsGeometry> scopedPreparedGeom;
2488  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, doClip ? extentGeom : nullptr ) )
2489  {
2490  scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, doClip ? extentGeom : nullptr ) );
2491 
2492  if ( !scopedPreparedGeom.data() )
2493  return;
2494  preparedGeom = scopedPreparedGeom.data();
2495  geos_geom = scopedPreparedGeom.data()->asGeos();
2496  }
2497  else
2498  {
2499  geos_geom = geom->asGeos();
2500  }
2501  const GEOSGeometry* geosObstacleGeom = nullptr;
2502  QScopedPointer<QgsGeometry> scopedObstacleGeom;
2503  if ( isObstacle )
2504  {
2505  if ( obstacleGeometry && QgsPalLabeling::geometryRequiresPreparation( obstacleGeometry, context, ct, doClip ? extentGeom : nullptr ) )
2506  {
2507  scopedObstacleGeom.reset( QgsPalLabeling::prepareGeometry( obstacleGeometry, context, ct, doClip ? extentGeom : nullptr ) );
2508  obstacleGeometry = scopedObstacleGeom.data();
2509  }
2510  if ( obstacleGeometry )
2511  {
2512  geosObstacleGeom = obstacleGeometry->asGeos();
2513  }
2514  }
2515 
2516  if ( minFeatureSize > 0 && !checkMinimumSizeMM( context, preparedGeom, minFeatureSize ) )
2517  return;
2518 
2519  if ( !geos_geom )
2520  return; // invalid geometry
2521 
2522  // likelihood exists label will be registered with PAL and may be drawn
2523  // check if max number of features to label (already registered with PAL) has been reached
2524  // Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
2525  if ( limitNumLabels )
2526  {
2527  if ( !maxNumLabels )
2528  {
2529  return;
2530  }
2531  if ( mFeatsRegPal >= maxNumLabels )
2532  {
2533  return;
2534  }
2535 
2536  int divNum = static_cast< int >(( static_cast< double >( mFeaturesToLabel ) / maxNumLabels ) + 0.5 );
2537  if ( divNum && ( mFeatsRegPal == static_cast< int >( mFeatsSendingToPal / divNum ) ) )
2538  {
2539  mFeatsSendingToPal += 1;
2540  if ( divNum && mFeatsSendingToPal % divNum )
2541  {
2542  return;
2543  }
2544  }
2545  }
2546 
2547  GEOSGeometry* geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
2548  GEOSGeometry* geosObstacleGeomClone = nullptr;
2549  if ( geosObstacleGeom )
2550  {
2551  geosObstacleGeomClone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geosObstacleGeom );
2552  }
2553 
2554 
2555  //data defined position / alignment / rotation?
2556  bool dataDefinedPosition = false;
2557  bool layerDefinedRotation = false;
2558  bool dataDefinedRotation = false;
2559  double xPos = 0.0, yPos = 0.0, angle = 0.0;
2560  bool ddXPos = false, ddYPos = false;
2561  double quadOffsetX = 0.0, quadOffsetY = 0.0;
2562  double offsetX = 0.0, offsetY = 0.0;
2563 
2564  //data defined quadrant offset?
2565  bool ddFixedQuad = false;
2566  QuadrantPosition quadOff = quadOffset;
2567  if ( dataDefinedEvaluate( QgsPalLayerSettings::OffsetQuad, exprVal, &context.expressionContext(), static_cast< int >( quadOff ) ) )
2568  {
2569  bool ok;
2570  int quadInt = exprVal.toInt( &ok );
2571  QgsDebugMsgLevel( QString( "exprVal OffsetQuad:%1" ).arg( quadInt ), 4 );
2572  if ( ok && 0 <= quadInt && quadInt <= 8 )
2573  {
2574  quadOff = static_cast< QuadrantPosition >( quadInt );
2575  ddFixedQuad = true;
2576  }
2577  }
2578 
2579  // adjust quadrant offset of labels
2580  switch ( quadOff )
2581  {
2582  case QuadrantAboveLeft:
2583  quadOffsetX = -1.0;
2584  quadOffsetY = 1.0;
2585  break;
2586  case QuadrantAbove:
2587  quadOffsetX = 0.0;
2588  quadOffsetY = 1.0;
2589  break;
2590  case QuadrantAboveRight:
2591  quadOffsetX = 1.0;
2592  quadOffsetY = 1.0;
2593  break;
2594  case QuadrantLeft:
2595  quadOffsetX = -1.0;
2596  quadOffsetY = 0.0;
2597  break;
2598  case QuadrantRight:
2599  quadOffsetX = 1.0;
2600  quadOffsetY = 0.0;
2601  break;
2602  case QuadrantBelowLeft:
2603  quadOffsetX = -1.0;
2604  quadOffsetY = -1.0;
2605  break;
2606  case QuadrantBelow:
2607  quadOffsetX = 0.0;
2608  quadOffsetY = -1.0;
2609  break;
2610  case QuadrantBelowRight:
2611  quadOffsetX = 1.0;
2612  quadOffsetY = -1.0;
2613  break;
2614  case QuadrantOver:
2615  default:
2616  break;
2617  }
2618 
2619  //data defined label offset?
2620  double xOff = xOffset;
2621  double yOff = yOffset;
2623  {
2624  QString ptstr = exprVal.toString().trimmed();
2625  QgsDebugMsgLevel( QString( "exprVal OffsetXY:%1" ).arg( ptstr ), 4 );
2626 
2627  if ( !ptstr.isEmpty() )
2628  {
2629  QPointF ddOffPt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
2630  xOff = ddOffPt.x();
2631  yOff = ddOffPt.y();
2632  }
2633  }
2634 
2635  // data defined label offset units?
2636  bool offinmapunits = labelOffsetInMapUnits;
2638  {
2639  QString units = exprVal.toString().trimmed();
2640  QgsDebugMsgLevel( QString( "exprVal OffsetUnits:%1" ).arg( units ), 4 );
2641  if ( !units.isEmpty() )
2642  {
2643  offinmapunits = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2644  }
2645  }
2646 
2647  // adjust offset of labels to match chosen unit and map scale
2648  // offsets match those of symbology: -x = left, -y = up
2649  double mapUntsPerMM = labelOffsetMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor();
2650  if ( !qgsDoubleNear( xOff, 0.0 ) )
2651  {
2652  offsetX = xOff; // must be positive to match symbology offset direction
2653  if ( !offinmapunits )
2654  {
2655  offsetX *= mapUntsPerMM; //convert offset from mm to map units
2656  }
2657  }
2658  if ( !qgsDoubleNear( yOff, 0.0 ) )
2659  {
2660  offsetY = -yOff; // must be negative to match symbology offset direction
2661  if ( !offinmapunits )
2662  {
2663  offsetY *= mapUntsPerMM; //convert offset from mm to map units
2664  }
2665  }
2666 
2667  // layer defined rotation?
2668  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2670  {
2671  layerDefinedRotation = true;
2672  angle = angleOffset * M_PI / 180; // convert to radians
2673  }
2674 
2675  const QgsMapToPixel& m2p = context.mapToPixel();
2676  //data defined rotation?
2678  {
2679  bool ok;
2680  double rotD = exprVal.toDouble( &ok );
2681  QgsDebugMsgLevel( QString( "exprVal Rotation:%1" ).arg( rotD ), 4 );
2682  if ( ok )
2683  {
2684  dataDefinedRotation = true;
2685  // TODO: add setting to disable having data defined rotation follow
2686  // map rotation ?
2687  rotD -= m2p.mapRotation();
2688  angle = rotD * M_PI / 180.0;
2689  }
2690  }
2691 
2693  {
2694  if ( !exprVal.isNull() )
2695  xPos = exprVal.toDouble( &ddXPos );
2696  QgsDebugMsgLevel( QString( "exprVal PositionX:%1" ).arg( xPos ), 4 );
2697 
2699  {
2700  //data defined position. But field values could be NULL -> positions will be generated by PAL
2701  if ( !exprVal.isNull() )
2702  yPos = exprVal.toDouble( &ddYPos );
2703  QgsDebugMsgLevel( QString( "exprVal PositionY:%1" ).arg( yPos ), 4 );
2704 
2705  if ( ddXPos && ddYPos )
2706  {
2707  dataDefinedPosition = true;
2708  // layer rotation set, but don't rotate pinned labels unless data defined
2709  if ( layerDefinedRotation && !dataDefinedRotation )
2710  {
2711  angle = 0.0;
2712  }
2713 
2714  //x/y shift in case of alignment
2715  double xdiff = 0.0;
2716  double ydiff = 0.0;
2717 
2718  //horizontal alignment
2719  if ( dataDefinedEvaluate( QgsPalLayerSettings::Hali, exprVal, &context.expressionContext() ) )
2720  {
2721  QString haliString = exprVal.toString();
2722  QgsDebugMsgLevel( QString( "exprVal Hali:%1" ).arg( haliString ), 4 );
2723  if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
2724  {
2725  xdiff -= labelX / 2.0;
2726  }
2727  else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
2728  {
2729  xdiff -= labelX;
2730  }
2731  }
2732 
2733  //vertical alignment
2734  if ( dataDefinedEvaluate( QgsPalLayerSettings::Vali, exprVal, &context.expressionContext() ) )
2735  {
2736  QString valiString = exprVal.toString();
2737  QgsDebugMsgLevel( QString( "exprVal Vali:%1" ).arg( valiString ), 4 );
2738 
2739  if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
2740  {
2741  if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 )
2742  {
2743  ydiff -= labelY;
2744  }
2745  else
2746  {
2747  double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
2748  if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
2749  {
2750  ydiff -= labelY * descentRatio;
2751  }
2752  else //'Cap' or 'Half'
2753  {
2754  double capHeightRatio = ( labelFontMetrics->boundingRect( 'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
2755  ydiff -= labelY * capHeightRatio;
2756  if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
2757  {
2758  ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
2759  }
2760  }
2761  }
2762  }
2763  }
2764 
2765  if ( dataDefinedRotation )
2766  {
2767  //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
2768  double xd = xdiff * cos( angle ) - ydiff * sin( angle );
2769  double yd = xdiff * sin( angle ) + ydiff * cos( angle );
2770  xdiff = xd;
2771  ydiff = yd;
2772  }
2773 
2774  //project xPos and yPos from layer to map CRS
2775  double z = 0;
2776  if ( ct )
2777  {
2778  try
2779  {
2780  ct->transformInPlace( xPos, yPos, z );
2781  }
2782  catch ( QgsCsException &e )
2783  {
2784  Q_UNUSED( e );
2785  QgsDebugMsgLevel( QString( "Ignoring feature %1 due transformation exception on data-defined position" ).arg( f.id() ), 4 );
2786  return;
2787  }
2788  }
2789 
2790  //rotate position with map if data-defined
2791  if ( dataDefinedPosition && m2p.mapRotation() )
2792  {
2793  const QgsPoint& center = context.extent().center();
2794  QTransform t = QTransform::fromTranslate( center.x(), center.y() );
2795  t.rotate( -m2p.mapRotation() );
2796  t.translate( -center.x(), -center.y() );
2797  qreal xPosR, yPosR;
2798  qreal xPos_qreal = xPos, yPos_qreal = yPos;
2799  t.map( xPos_qreal, yPos_qreal, &xPosR, &yPosR );
2800  xPos = xPosR;
2801  yPos = yPosR;
2802 
2803  }
2804 
2805  xPos += xdiff;
2806  yPos += ydiff;
2807  }
2808  else
2809  {
2810  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2811  if ( dataDefinedRotation && placement != QgsPalLayerSettings::OverPoint )
2812  {
2813  angle = 0.0;
2814  }
2815  }
2816  }
2817  }
2818 
2819  // data defined always show?
2820  bool alwaysShow = false;
2822  {
2823  alwaysShow = exprVal.toBool();
2824  }
2825 
2826  // set repeat distance
2827  // data defined repeat distance?
2828  double repeatDist = repeatDistance;
2830  {
2831  bool ok;
2832  double distD = exprVal.toDouble( &ok );
2833  if ( ok )
2834  {
2835  repeatDist = distD;
2836  }
2837  }
2838 
2839  // data defined label-repeat distance units?
2840  bool repeatdistinmapunit = repeatDistanceUnit == QgsPalLayerSettings::MapUnits;
2842  {
2843  QString units = exprVal.toString().trimmed();
2844  QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 );
2845  if ( !units.isEmpty() )
2846  {
2847  repeatdistinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2848  }
2849  }
2850 
2851  if ( !qgsDoubleNear( repeatDist, 0.0 ) )
2852  {
2853  if ( !repeatdistinmapunit )
2854  {
2855  repeatDist *= mapUntsPerMM; //convert repeat distance from mm to map units
2856  }
2857  }
2858 
2859  // feature to the layer
2860  QgsTextLabelFeature* lf = new QgsTextLabelFeature( f.id(), geos_geom_clone, QSizeF( labelX, labelY ) );
2861  mFeatsRegPal++;
2862 
2863  *labelFeature = lf;
2864  ( *labelFeature )->setHasFixedPosition( dataDefinedPosition );
2865  ( *labelFeature )->setFixedPosition( QgsPoint( xPos, yPos ) );
2866  // use layer-level defined rotation, but not if position fixed
2867  ( *labelFeature )->setHasFixedAngle( dataDefinedRotation || ( !dataDefinedPosition && !qgsDoubleNear( angle, 0.0 ) ) );
2868  ( *labelFeature )->setFixedAngle( angle );
2869  ( *labelFeature )->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
2870  ( *labelFeature )->setPositionOffset( QgsPoint( offsetX, offsetY ) );
2871  ( *labelFeature )->setOffsetType( offsetType );
2872  ( *labelFeature )->setAlwaysShow( alwaysShow );
2873  ( *labelFeature )->setRepeatDistance( repeatDist );
2874  ( *labelFeature )->setLabelText( labelText );
2875  ( *labelFeature )->setPermissibleZone( permissibleZone );
2876  if ( geosObstacleGeomClone )
2877  {
2878  ( *labelFeature )->setObstacleGeometry( geosObstacleGeomClone );
2879 
2880  if ( geom->type() == QGis::Point )
2881  {
2882  //register symbol size
2883  ( *labelFeature )->setSymbolSize( QSizeF( obstacleGeometry->boundingBox().width(),
2884  obstacleGeometry->boundingBox().height() ) );
2885  }
2886  }
2887 
2888  //set label's visual margin so that top visual margin is the leading, and bottom margin is the font's descent
2889  //this makes labels align to the font's baseline or highest character
2890  double topMargin = qMax( 0.25 * labelFontMetrics->ascent(), 0.0 );
2891  double bottomMargin = 1.0 + labelFontMetrics->descent();
2892  QgsLabelFeature::VisualMargin vm( topMargin, 0.0, bottomMargin, 0.0 );
2894  ( *labelFeature )->setVisualMargin( vm );
2895 
2896  // store the label's calculated font for later use during painting
2897  QgsDebugMsgLevel( QString( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString(), labelFont.styleName() ), 4 );
2898  lf->setDefinedFont( labelFont );
2899 
2900  // TODO: only for placement which needs character info
2901  // account for any data defined font metrics adjustments
2903  labelFontMetrics.data(), xform, rasterCompressFactor, maxcharanglein, maxcharangleout );
2904  // for labelFeature the LabelInfo is passed to feat when it is registered
2905 
2906  // TODO: allow layer-wide feature dist in PAL...?
2907 
2908  // data defined label-feature distance?
2909  double distance = dist;
2911  {
2912  bool ok;
2913  double distD = exprVal.toDouble( &ok );
2914  if ( ok )
2915  {
2916  distance = distD;
2917  }
2918  }
2919 
2920  // data defined label-feature distance units?
2921  bool distinmapunit = distInMapUnits;
2923  {
2924  QString units = exprVal.toString().trimmed();
2925  QgsDebugMsgLevel( QString( "exprVal DistanceUnits:%1" ).arg( units ), 4 );
2926  if ( !units.isEmpty() )
2927  {
2928  distinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2929  }
2930  }
2931 
2932  if ( distinmapunit ) //convert distance from mm/map units to pixels
2933  {
2934  distance /= distMapUnitScale.computeMapUnitsPerPixel( context );
2935  }
2936  else //mm
2937  {
2938  distance *= vectorScaleFactor;
2939  }
2940 
2941  // when using certain placement modes, we force a tiny minimum distance. This ensures that
2942  // candidates are created just offset from a border and avoids candidates being incorrectly flagged as colliding with neighbours
2944  {
2945  distance = qMax( distance, 1.0 );
2946  }
2947 
2948  if ( !qgsDoubleNear( distance, 0.0 ) )
2949  {
2950  double d = ptOne.distance( ptZero ) * distance;
2951  ( *labelFeature )->setDistLabel( d );
2952  }
2953 
2954  if ( ddFixedQuad )
2955  {
2956  ( *labelFeature )->setHasFixedQuadrant( true );
2957  }
2958 
2959  // data defined z-index?
2960  double z = zIndex;
2962  {
2963  bool ok;
2964  double zIndexD = exprVal.toDouble( &ok );
2965  if ( ok )
2966  {
2967  z = zIndexD;
2968  }
2969  }
2970  ( *labelFeature )->setZIndex( z );
2971 
2972  // data defined priority?
2974  {
2975  bool ok;
2976  double priorityD = exprVal.toDouble( &ok );
2977  if ( ok )
2978  {
2979  priorityD = qBound( 0.0, priorityD, 10.0 );
2980  priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
2981  ( *labelFeature )->setPriority( priorityD );
2982  }
2983  }
2984 
2985  ( *labelFeature )->setIsObstacle( isObstacle );
2986 
2987  double featObstacleFactor = obstacleFactor;
2989  {
2990  bool ok;
2991  double factorD = exprVal.toDouble( &ok );
2992  if ( ok )
2993  {
2994  factorD = qBound( 0.0, factorD, 10.0 );
2995  factorD = factorD / 5.0 + 0.0001; // convert 0 -> 10 to 0.0001 -> 2.0
2996  featObstacleFactor = factorD;
2997  }
2998  }
2999  ( *labelFeature )->setObstacleFactor( featObstacleFactor );
3000 
3002  if ( positionOrder.isEmpty() )
3003  positionOrder = QgsPalLayerSettings::DEFAULT_PLACEMENT_ORDER;
3004 
3006  {
3007  QString orderD = exprVal.toString();
3008  positionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( orderD );
3009  }
3010  ( *labelFeature )->setPredefinedPositionOrder( positionOrder );
3011 
3012  // add parameters for data defined labeling to label feature
3013  lf->setDataDefinedValues( dataDefinedValues );
3014 }
3015 
3016 void QgsPalLayerSettings::registerObstacleFeature( QgsFeature& f, QgsRenderContext &context, QgsLabelFeature** obstacleFeature, QgsGeometry* obstacleGeometry )
3017 {
3018  mCurFeat = &f;
3019 
3020  const QgsGeometry* geom = nullptr;
3021  if ( obstacleGeometry )
3022  {
3023  geom = obstacleGeometry;
3024  }
3025  else
3026  {
3027  geom = f.constGeometry();
3028  }
3029 
3030  if ( !geom )
3031  {
3032  return;
3033  }
3034 
3035  // simplify?
3036  const QgsVectorSimplifyMethod &simplifyMethod = context.vectorSimplifyMethod();
3037  QScopedPointer<QgsGeometry> scopedClonedGeom;
3038  if ( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && simplifyMethod.forceLocalOptimization() )
3039  {
3040  int simplifyHints = simplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
3042  QgsGeometry* g = new QgsGeometry( *geom );
3043 
3044  if ( QgsMapToPixelSimplifier::simplifyGeometry( g, simplifyHints, simplifyMethod.tolerance(), simplifyAlgorithm ) )
3045  {
3046  geom = g;
3047  scopedClonedGeom.reset( g );
3048  }
3049  else
3050  {
3051  delete g;
3052  }
3053  }
3054 
3055  const GEOSGeometry* geos_geom = nullptr;
3056  QScopedPointer<QgsGeometry> scopedPreparedGeom;
3057 
3058  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, extentGeom ) )
3059  {
3060  scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, extentGeom ) );
3061  if ( !scopedPreparedGeom.data() )
3062  return;
3063  geos_geom = scopedPreparedGeom.data()->asGeos();
3064  }
3065  else
3066  {
3067  geos_geom = geom->asGeos();
3068  }
3069 
3070  if ( !geos_geom )
3071  return; // invalid geometry
3072 
3073  GEOSGeometry* geos_geom_clone;
3074  geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
3075 
3076  // feature to the layer
3077  *obstacleFeature = new QgsLabelFeature( f.id(), geos_geom_clone, QSizeF( 0, 0 ) );
3078  ( *obstacleFeature )->setIsObstacle( true );
3079  mFeatsRegPal++;
3080 }
3081 
3082 bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
3084  QVariant& exprVal, QgsExpressionContext& context, const QVariant& originalValue )
3085 {
3086  if ( dataDefinedEvaluate( p, exprVal, &context, originalValue ) )
3087  {
3088 #ifdef QGISDEBUG
3089  QString dbgStr = QString( "exprVal %1:" ).arg( mDataDefinedNames.value( p ).first ) + "%1"; // clazy:exclude=unused-non-trivial-variable
3090 #endif
3091 
3092  switch ( valType )
3093  {
3094  case DDBool:
3095  {
3096  bool bol = exprVal.toBool();
3097  QgsDebugMsgLevel( dbgStr.arg( bol ? "true" : "false" ), 4 );
3098  dataDefinedValues.insert( p, QVariant( bol ) );
3099  return true;
3100  }
3101  case DDInt:
3102  {
3103  bool ok;
3104  int size = exprVal.toInt( &ok );
3105  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3106 
3107  if ( ok )
3108  {
3109  dataDefinedValues.insert( p, QVariant( size ) );
3110  return true;
3111  }
3112  return false;
3113  }
3114  case DDIntPos:
3115  {
3116  bool ok;
3117  int size = exprVal.toInt( &ok );
3118  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3119 
3120  if ( ok && size > 0 )
3121  {
3122  dataDefinedValues.insert( p, QVariant( size ) );
3123  return true;
3124  }
3125  return false;
3126  }
3127  case DDDouble:
3128  {
3129  bool ok;
3130  double size = exprVal.toDouble( &ok );
3131  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3132 
3133  if ( ok )
3134  {
3135  dataDefinedValues.insert( p, QVariant( size ) );
3136  return true;
3137  }
3138  return false;
3139  }
3140  case DDDoublePos:
3141  {
3142  bool ok;
3143  double size = exprVal.toDouble( &ok );
3144  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3145 
3146  if ( ok && size > 0.0 )
3147  {
3148  dataDefinedValues.insert( p, QVariant( size ) );
3149  return true;
3150  }
3151  return false;
3152  }
3153  case DDRotation180:
3154  {
3155  bool ok;
3156  double rot = exprVal.toDouble( &ok );
3157  QgsDebugMsgLevel( dbgStr.arg( rot ), 4 );
3158  if ( ok )
3159  {
3160  if ( rot < -180.0 && rot >= -360 )
3161  {
3162  rot += 360;
3163  }
3164  if ( rot > 180.0 && rot <= 360 )
3165  {
3166  rot -= 360;
3167  }
3168  if ( rot >= -180 && rot <= 180 )
3169  {
3170  dataDefinedValues.insert( p, QVariant( rot ) );
3171  return true;
3172  }
3173  }
3174  return false;
3175  }
3176  case DDTransparency:
3177  {
3178  bool ok;
3179  int size = exprVal.toInt( &ok );
3180  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3181  if ( ok && size >= 0 && size <= 100 )
3182  {
3183  dataDefinedValues.insert( p, QVariant( size ) );
3184  return true;
3185  }
3186  return false;
3187  }
3188  case DDString:
3189  {
3190  QString str = exprVal.toString(); // don't trim whitespace
3191  QgsDebugMsgLevel( dbgStr.arg( str ), 4 );
3192 
3193  dataDefinedValues.insert( p, QVariant( str ) ); // let it stay empty if it is
3194  return true;
3195  }
3196  case DDUnits:
3197  {
3198  QString unitstr = exprVal.toString().trimmed();
3199  QgsDebugMsgLevel( dbgStr.arg( unitstr ), 4 );
3200 
3201  if ( !unitstr.isEmpty() )
3202  {
3203  dataDefinedValues.insert( p, QVariant( static_cast< int >( _decodeUnits( unitstr ) ) ) );
3204  return true;
3205  }
3206  return false;
3207  }
3208  case DDColor:
3209  {
3210  QString colorstr = exprVal.toString().trimmed();
3211  QgsDebugMsgLevel( dbgStr.arg( colorstr ), 4 );
3212  QColor color = QgsSymbolLayerV2Utils::decodeColor( colorstr );
3213 
3214  if ( color.isValid() )
3215  {
3216  dataDefinedValues.insert( p, QVariant( color ) );
3217  return true;
3218  }
3219  return false;
3220  }
3221  case DDJoinStyle:
3222  {
3223  QString joinstr = exprVal.toString().trimmed();
3224  QgsDebugMsgLevel( dbgStr.arg( joinstr ), 4 );
3225 
3226  if ( !joinstr.isEmpty() )
3227  {
3228  dataDefinedValues.insert( p, QVariant( static_cast< int >( _decodePenJoinStyle( joinstr ) ) ) );
3229  return true;
3230  }
3231  return false;
3232  }
3233  case DDBlendMode:
3234  {
3235  QString blendstr = exprVal.toString().trimmed();
3236  QgsDebugMsgLevel( dbgStr.arg( blendstr ), 4 );
3237 
3238  if ( !blendstr.isEmpty() )
3239  {
3240  dataDefinedValues.insert( p, QVariant( static_cast< int >( QgsSymbolLayerV2Utils::decodeBlendMode( blendstr ) ) ) );
3241  return true;
3242  }
3243  return false;
3244  }
3245  case DDPointF:
3246  {
3247  QString ptstr = exprVal.toString().trimmed();
3248  QgsDebugMsgLevel( dbgStr.arg( ptstr ), 4 );
3249 
3250  if ( !ptstr.isEmpty() )
3251  {
3252  dataDefinedValues.insert( p, QVariant( QgsSymbolLayerV2Utils::decodePoint( ptstr ) ) );
3253  return true;
3254  }
3255  return false;
3256  }
3257  }
3258  }
3259  return false;
3260 }
3261 
3262 void QgsPalLayerSettings::parseTextStyle( QFont& labelFont,
3264  QgsRenderContext &context )
3265 {
3266  // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
3267 
3268  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3269 
3270  // Two ways to generate new data defined font:
3271  // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
3272  // 2) Family + named style (bold or italic is ignored)
3273 
3274  // data defined font family?
3275  QString ddFontFamily( "" );
3276  if ( dataDefinedEvaluate( QgsPalLayerSettings::Family, exprVal, &context.expressionContext(), labelFont.family() ) )
3277  {
3278  QString family = exprVal.toString().trimmed();
3279  QgsDebugMsgLevel( QString( "exprVal Font family:%1" ).arg( family ), 4 );
3280 
3281  if ( labelFont.family() != family )
3282  {
3283  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
3284  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
3285  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
3286  {
3287  ddFontFamily = family;
3288  }
3289  }
3290  }
3291 
3292  // data defined named font style?
3293  QString ddFontStyle( "" );
3295  {
3296  QString fontstyle = exprVal.toString().trimmed();
3297  QgsDebugMsgLevel( QString( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
3298  ddFontStyle = fontstyle;
3299  }
3300 
3301  // data defined bold font style?
3302  bool ddBold = false;
3303  if ( dataDefinedEvaluate( QgsPalLayerSettings::Bold, exprVal, &context.expressionContext(), labelFont.bold() ) )
3304  {
3305  bool bold = exprVal.toBool();
3306  QgsDebugMsgLevel( QString( "exprVal Font bold:%1" ).arg( bold ? "true" : "false" ), 4 );
3307  ddBold = bold;
3308  }
3309 
3310  // data defined italic font style?
3311  bool ddItalic = false;
3312  if ( dataDefinedEvaluate( QgsPalLayerSettings::Italic, exprVal, &context.expressionContext(), labelFont.italic() ) )
3313  {
3314  bool italic = exprVal.toBool();
3315  QgsDebugMsgLevel( QString( "exprVal Font italic:%1" ).arg( italic ? "true" : "false" ), 4 );
3316  ddItalic = italic;
3317  }
3318 
3319  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
3320  // (currently defaults to what has been read in from layer settings)
3321  QFont newFont;
3322  QFont appFont = QApplication::font();
3323  bool newFontBuilt = false;
3324  if ( ddBold || ddItalic )
3325  {
3326  // new font needs built, since existing style needs removed
3327  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
3328  newFontBuilt = true;
3329  newFont.setBold( ddBold );
3330  newFont.setItalic( ddItalic );
3331  }
3332  else if ( !ddFontStyle.isEmpty()
3333  && ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
3334  {
3335  if ( !ddFontFamily.isEmpty() )
3336  {
3337  // both family and style are different, build font from database
3338  QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
3339  if ( appFont != styledfont )
3340  {
3341  newFont = styledfont;
3342  newFontBuilt = true;
3343  }
3344  }
3345 
3346  // update the font face style
3347  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
3348  }
3349  else if ( !ddFontFamily.isEmpty() )
3350  {
3351  if ( ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
3352  {
3353  // just family is different, build font from database
3354  QFont styledfont = mFontDB.font( ddFontFamily, textNamedStyle, appFont.pointSize() );
3355  if ( appFont != styledfont )
3356  {
3357  newFont = styledfont;
3358  newFontBuilt = true;
3359  }
3360  }
3361  else
3362  {
3363  newFont = QFont( ddFontFamily );
3364  newFontBuilt = true;
3365  }
3366  }
3367 
3368  if ( newFontBuilt )
3369  {
3370  // copy over existing font settings
3371  //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
3372  newFont.setPixelSize( labelFont.pixelSize() );
3373  newFont.setCapitalization( labelFont.capitalization() );
3374  newFont.setUnderline( labelFont.underline() );
3375  newFont.setStrikeOut( labelFont.strikeOut() );
3376  newFont.setWordSpacing( labelFont.wordSpacing() );
3377  newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
3378 
3379  labelFont = newFont;
3380  }
3381 
3382  // data defined word spacing?
3383  double wordspace = labelFont.wordSpacing();
3384  if ( dataDefinedEvaluate( QgsPalLayerSettings::FontWordSpacing, exprVal, &context.expressionContext(), wordspace ) )
3385  {
3386  bool ok;
3387  double wspacing = exprVal.toDouble( &ok );
3388  QgsDebugMsgLevel( QString( "exprVal FontWordSpacing:%1" ).arg( wspacing ), 4 );
3389  if ( ok )
3390  {
3391  wordspace = wspacing;
3392  }
3393  }
3394  labelFont.setWordSpacing( scaleToPixelContext( wordspace, context, fontunits, false, fontSizeMapUnitScale ) );
3395 
3396  // data defined letter spacing?
3397  double letterspace = labelFont.letterSpacing();
3398  if ( dataDefinedEvaluate( QgsPalLayerSettings::FontLetterSpacing, exprVal, &context.expressionContext(), letterspace ) )
3399  {
3400  bool ok;
3401  double lspacing = exprVal.toDouble( &ok );
3402  QgsDebugMsgLevel( QString( "exprVal FontLetterSpacing:%1" ).arg( lspacing ), 4 );
3403  if ( ok )
3404  {
3405  letterspace = lspacing;
3406  }
3407  }
3408  labelFont.setLetterSpacing( QFont::AbsoluteSpacing, scaleToPixelContext( letterspace, context, fontunits, false, fontSizeMapUnitScale ) );
3409 
3410  // data defined font capitalization?
3411  QFont::Capitalization fontcaps = labelFont.capitalization();
3413  {
3414  QString fcase = exprVal.toString().trimmed();
3415  QgsDebugMsgLevel( QString( "exprVal FontCase:%1" ).arg( fcase ), 4 );
3416 
3417  if ( !fcase.isEmpty() )
3418  {
3419  if ( fcase.compare( "NoChange", Qt::CaseInsensitive ) == 0 )
3420  {
3421  fontcaps = QFont::MixedCase;
3422  }
3423  else if ( fcase.compare( "Upper", Qt::CaseInsensitive ) == 0 )
3424  {
3425  fontcaps = QFont::AllUppercase;
3426  }
3427  else if ( fcase.compare( "Lower", Qt::CaseInsensitive ) == 0 )
3428  {
3429  fontcaps = QFont::AllLowercase;
3430  }
3431  else if ( fcase.compare( "Capitalize", Qt::CaseInsensitive ) == 0 )
3432  {
3433  fontcaps = QFont::Capitalize;
3434  }
3435 
3436  if ( fontcaps != labelFont.capitalization() )
3437  {
3438  labelFont.setCapitalization( fontcaps );
3439  }
3440  }
3441  }
3442 
3443  // data defined strikeout font style?
3444  if ( dataDefinedEvaluate( QgsPalLayerSettings::Strikeout, exprVal, &context.expressionContext(), labelFont.strikeOut() ) )
3445  {
3446  bool strikeout = exprVal.toBool();
3447  QgsDebugMsgLevel( QString( "exprVal Font strikeout:%1" ).arg( strikeout ? "true" : "false" ), 4 );
3448  labelFont.setStrikeOut( strikeout );
3449  }
3450 
3451  // data defined underline font style?
3452  if ( dataDefinedEvaluate( QgsPalLayerSettings::Underline, exprVal, &context.expressionContext(), labelFont.underline() ) )
3453  {
3454  bool underline = exprVal.toBool();
3455  QgsDebugMsgLevel( QString( "exprVal Font underline:%1" ).arg( underline ? "true" : "false" ), 4 );
3456  labelFont.setUnderline( underline );
3457  }
3458 
3459  // pass the rest on to QgsPalLabeling::drawLabeling
3460 
3461  // data defined font color?
3462  dataDefinedValEval( DDColor, QgsPalLayerSettings::Color, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( textColor ) );
3463 
3464  // data defined font transparency?
3465  dataDefinedValEval( DDTransparency, QgsPalLayerSettings::FontTransp, exprVal, context.expressionContext(), textTransp );
3466 
3467  // data defined font blend mode?
3468  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::FontBlendMode, exprVal, context.expressionContext() );
3469 
3470 }
3471 
3472 void QgsPalLayerSettings::parseTextBuffer( QgsRenderContext &context )
3473 {
3474  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3475 
3476  // data defined draw buffer?
3477  bool drawBuffer = bufferDraw;
3478  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::BufferDraw, exprVal, context.expressionContext(), bufferDraw ) )
3479  {
3480  drawBuffer = exprVal.toBool();
3481  }
3482 
3483  if ( !drawBuffer )
3484  {
3485  return;
3486  }
3487 
3488  // data defined buffer size?
3489  double bufrSize = bufferSize;
3490  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::BufferSize, exprVal, context.expressionContext(), bufferSize ) )
3491  {
3492  bufrSize = exprVal.toDouble();
3493  }
3494 
3495  // data defined buffer transparency?
3496  int bufTransp = bufferTransp;
3497  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::BufferTransp, exprVal, context.expressionContext(), bufferTransp ) )
3498  {
3499  bufTransp = exprVal.toInt();
3500  }
3501 
3502  drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufTransp < 100 );
3503 
3504  if ( !drawBuffer )
3505  {
3506  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
3507  dataDefinedValues.remove( QgsPalLayerSettings::BufferSize );
3508  dataDefinedValues.remove( QgsPalLayerSettings::BufferTransp );
3509  return; // don't bother evaluating values that won't be used
3510  }
3511 
3512  // data defined buffer units?
3513  dataDefinedValEval( DDUnits, QgsPalLayerSettings::BufferUnit, exprVal, context.expressionContext() );
3514 
3515  // data defined buffer color?
3516  dataDefinedValEval( DDColor, QgsPalLayerSettings::BufferColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( bufferColor ) );
3517 
3518  // data defined buffer pen join style?
3520 
3521  // data defined buffer blend mode?
3522  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::BufferBlendMode, exprVal, context.expressionContext() );
3523 }
3524 
3525 void QgsPalLayerSettings::parseTextFormatting( QgsRenderContext &context )
3526 {
3527  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3528 
3529  // data defined multiline wrap character?
3530  QString wrapchr = wrapChar;
3531  if ( dataDefinedValEval( DDString, QgsPalLayerSettings::MultiLineWrapChar, exprVal, context.expressionContext(), wrapChar ) )
3532  {
3533  wrapchr = exprVal.toString();
3534  }
3535 
3536  // data defined multiline height?
3537  dataDefinedValEval( DDDouble, QgsPalLayerSettings::MultiLineHeight, exprVal, context.expressionContext() );
3538 
3539  // data defined multiline text align?
3541  {
3542  QString str = exprVal.toString().trimmed();
3543  QgsDebugMsgLevel( QString( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
3544 
3545  if ( !str.isEmpty() )
3546  {
3547  // "Left"
3549 
3550  if ( str.compare( "Center", Qt::CaseInsensitive ) == 0 )
3551  {
3553  }
3554  else if ( str.compare( "Right", Qt::CaseInsensitive ) == 0 )
3555  {
3556  aligntype = QgsPalLayerSettings::MultiRight;
3557  }
3558  else if ( str.compare( "Follow", Qt::CaseInsensitive ) == 0 )
3559  {
3561  }
3562  dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant( static_cast< int >( aligntype ) ) );
3563  }
3564  }
3565 
3566  // data defined direction symbol?
3567  bool drawDirSymb = addDirectionSymbol;
3568  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbDraw, exprVal, context.expressionContext(), addDirectionSymbol ) )
3569  {
3570  drawDirSymb = exprVal.toBool();
3571  }
3572 
3573  if ( drawDirSymb )
3574  {
3575  // data defined direction left symbol?
3576  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbLeft, exprVal, context.expressionContext(), leftDirectionSymbol );
3577 
3578  // data defined direction right symbol?
3579  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbRight, exprVal, context.expressionContext(), rightDirectionSymbol );
3580 
3581  // data defined direction symbol placement?
3583  {
3584  QString str = exprVal.toString().trimmed();
3585  QgsDebugMsgLevel( QString( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
3586 
3587  if ( !str.isEmpty() )
3588  {
3589  // "LeftRight"
3591 
3592  if ( str.compare( "Above", Qt::CaseInsensitive ) == 0 )
3593  {
3595  }
3596  else if ( str.compare( "Below", Qt::CaseInsensitive ) == 0 )
3597  {
3599  }
3600  dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant( static_cast< int >( placetype ) ) );
3601  }
3602  }
3603 
3604  // data defined direction symbol reversed?
3605  dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbReverse, exprVal, context.expressionContext(), reverseDirectionSymbol );
3606  }
3607 
3608  // formatting for numbers is inline with generation of base label text and not passed to label painting
3609 }
3610 
3611 void QgsPalLayerSettings::parseShapeBackground( QgsRenderContext &context )
3612 {
3613  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3614 
3615  // data defined draw shape?
3616  bool drawShape = shapeDraw;
3617  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShapeDraw, exprVal, context.expressionContext(), shapeDraw ) )
3618  {
3619  drawShape = exprVal.toBool();
3620  }
3621 
3622  if ( !drawShape )
3623  {
3624  return;
3625  }
3626 
3627  // data defined shape transparency?
3628  int shapeTransp = shapeTransparency;
3629  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::ShapeTransparency, exprVal, context.expressionContext(), shapeTransparency ) )
3630  {
3631  shapeTransp = exprVal.toInt();
3632  }
3633 
3634  drawShape = ( drawShape && shapeTransp < 100 ); // size is not taken into account (could be)
3635 
3636  if ( !drawShape )
3637  {
3638  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
3639  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
3640  return; // don't bother evaluating values that won't be used
3641  }
3642 
3643  // data defined shape kind?
3646  {
3647  QString skind = exprVal.toString().trimmed();
3648  QgsDebugMsgLevel( QString( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
3649 
3650  if ( !skind.isEmpty() )
3651  {
3652  // "Rectangle"
3654 
3655  if ( skind.compare( "Square", Qt::CaseInsensitive ) == 0 )
3656  {
3658  }
3659  else if ( skind.compare( "Ellipse", Qt::CaseInsensitive ) == 0 )
3660  {
3662  }
3663  else if ( skind.compare( "Circle", Qt::CaseInsensitive ) == 0 )
3664  {
3666  }
3667  else if ( skind.compare( "SVG", Qt::CaseInsensitive ) == 0 )
3668  {
3670  }
3671  shapeKind = shpkind;
3672  dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant( static_cast< int >( shpkind ) ) );
3673  }
3674  }
3675 
3676  // data defined shape SVG path?
3677  QString svgPath = shapeSVGFile;
3679  {
3680  QString svgfile = exprVal.toString().trimmed();
3681  QgsDebugMsgLevel( QString( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
3682 
3683  // '' empty paths are allowed
3684  svgPath = svgfile;
3685  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgfile ) );
3686  }
3687 
3688  // data defined shape size type?
3691  {
3692  QString stype = exprVal.toString().trimmed();
3693  QgsDebugMsgLevel( QString( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
3694 
3695  if ( !stype.isEmpty() )
3696  {
3697  // "Buffer"
3699 
3700  if ( stype.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
3701  {
3703  }
3704  shpSizeType = sizType;
3705  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant( static_cast< int >( sizType ) ) );
3706  }
3707  }
3708 
3709  // data defined shape size X? (SVGs only use X for sizing)
3710  double ddShpSizeX = shapeSize.x();
3711  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeX, exprVal, context.expressionContext(), ddShpSizeX ) )
3712  {
3713  ddShpSizeX = exprVal.toDouble();
3714  }
3715 
3716  // data defined shape size Y?
3717  double ddShpSizeY = shapeSize.y();
3718  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeY, exprVal, context.expressionContext(), ddShpSizeY ) )
3719  {
3720  ddShpSizeY = exprVal.toDouble();
3721  }
3722 
3723  // don't continue under certain circumstances (e.g. size is fixed)
3724  bool skip = false;
3725  if ( shapeKind == QgsPalLayerSettings::ShapeSVG
3726  && ( svgPath.isEmpty()
3727  || ( !svgPath.isEmpty()
3728  && shpSizeType == QgsPalLayerSettings::SizeFixed
3729  && ddShpSizeX == 0.0 ) ) )
3730  {
3731  skip = true;
3732  }
3733  if ( shapeKind != QgsPalLayerSettings::ShapeSVG
3734  && shpSizeType == QgsPalLayerSettings::SizeFixed
3735  && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
3736  {
3737  skip = true;
3738  }
3739 
3740  if ( skip )
3741  {
3742  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
3743  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
3744  dataDefinedValues.remove( QgsPalLayerSettings::ShapeKind );
3745  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSVGFile );
3746  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeX );
3747  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeY );
3748  return; // don't bother evaluating values that won't be used
3749  }
3750 
3751  // data defined shape size units?
3752  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeSizeUnits, exprVal, context.expressionContext() );
3753 
3754  // data defined shape rotation type?
3756  {
3757  QString rotstr = exprVal.toString().trimmed();
3758  QgsDebugMsgLevel( QString( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
3759 
3760  if ( !rotstr.isEmpty() )
3761  {
3762  // "Sync"
3764 
3765  if ( rotstr.compare( "Offset", Qt::CaseInsensitive ) == 0 )
3766  {
3768  }
3769  else if ( rotstr.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
3770  {
3772  }
3773  dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant( static_cast< int >( rottype ) ) );
3774  }
3775  }
3776 
3777  // data defined shape rotation?
3778  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShapeRotation, exprVal, context.expressionContext(), shapeRotation );
3779 
3780  // data defined shape offset?
3781  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeOffset, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodePoint( shapeOffset ) );
3782 
3783  // data defined shape offset units?
3784  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeOffsetUnits, exprVal, context.expressionContext() );
3785 
3786  // data defined shape radii?
3787  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeRadii, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodePoint( shapeRadii ) );
3788 
3789  // data defined shape radii units?
3790  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeRadiiUnits, exprVal, context.expressionContext() );
3791 
3792  // data defined shape blend mode?
3793  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShapeBlendMode, exprVal, context.expressionContext() );
3794 
3795  // data defined shape fill color?
3796  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShapeFillColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( shapeFillColor ) );
3797 
3798  // data defined shape border color?
3800 
3801  // data defined shape border width?
3802  dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShapeBorderWidth, exprVal, context.expressionContext(), shapeBorderWidth );
3803 
3804  // data defined shape border width units?
3805  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeBorderWidthUnits, exprVal, context.expressionContext() );
3806 
3807  // data defined shape join style?
3809 
3810 }
3811 
3812 void QgsPalLayerSettings::parseDropShadow( QgsRenderContext &context )
3813 {
3814  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3815 
3816  // data defined draw shadow?
3817  bool drawShadow = shadowDraw;
3818  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShadowDraw, exprVal, context.expressionContext(), shadowDraw ) )
3819  {
3820  drawShadow = exprVal.toBool();
3821  }
3822 
3823  if ( !drawShadow )
3824  {
3825  return;
3826  }
3827 
3828  // data defined shadow transparency?
3829  int shadowTransp = shadowTransparency;
3830  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::ShadowTransparency, exprVal, context.expressionContext(), shadowTransparency ) )
3831  {
3832  shadowTransp = exprVal.toInt();
3833  }
3834 
3835  // data defined shadow offset distance?
3836  double shadowOffDist = shadowOffsetDist;
3837  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowOffsetDist, exprVal, context.expressionContext(), shadowOffsetDist ) )
3838  {
3839  shadowOffDist = exprVal.toDouble();
3840  }
3841 
3842  // data defined shadow offset distance?
3843  double shadowRad = shadowRadius;
3844  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRadius ) )
3845  {
3846  shadowRad = exprVal.toDouble();
3847  }
3848 
3849  drawShadow = ( drawShadow && shadowTransp < 100 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
3850 
3851  if ( !drawShadow )
3852  {
3853  dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
3854  dataDefinedValues.remove( QgsPalLayerSettings::ShadowTransparency );
3855  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOffsetDist );
3856  dataDefinedValues.remove( QgsPalLayerSettings::ShadowRadius );
3857  return; // don't bother evaluating values that won't be used
3858  }
3859 
3860  // data defined shadow under type?
3862  {
3863  QString str = exprVal.toString().trimmed();
3864  QgsDebugMsgLevel( QString( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
3865 
3866  if ( !str.isEmpty() )
3867  {
3868  // "Lowest"
3870 
3871  if ( str.compare( "Text", Qt::CaseInsensitive ) == 0 )
3872  {
3874  }
3875  else if ( str.compare( "Buffer", Qt::CaseInsensitive ) == 0 )
3876  {
3878  }
3879  else if ( str.compare( "Background", Qt::CaseInsensitive ) == 0 )
3880  {
3882  }
3883  dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant( static_cast< int >( shdwtype ) ) );
3884  }
3885  }
3886 
3887  // data defined shadow offset angle?
3888  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShadowOffsetAngle, exprVal, context.expressionContext(), shadowOffsetAngle );
3889 
3890  // data defined shadow offset units?
3891  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowOffsetUnits, exprVal, context.expressionContext() );
3892 
3893  // data defined shadow radius?
3894  dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRadius );
3895 
3896  // data defined shadow radius units?
3897  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowRadiusUnits, exprVal, context.expressionContext() );
3898 
3899  // data defined shadow scale? ( gui bounds to 0-2000, no upper bound here )
3900  dataDefinedValEval( DDIntPos, QgsPalLayerSettings::ShadowScale, exprVal, context.expressionContext(), shadowScale );
3901 
3902  // data defined shadow color?
3903  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShadowColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( shadowColor ) );
3904 
3905  // data defined shadow blend mode?
3906  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShadowBlendMode, exprVal, context.expressionContext() );
3907 }
3908 
3909 int QgsPalLayerSettings::sizeToPixel( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor, const QgsMapUnitScale& mapUnitScale ) const
3910 {
3911  return static_cast< int >( scaleToPixelContext( size, c, unit, rasterfactor, mapUnitScale ) + 0.5 );
3912 }
3913 
3914 double QgsPalLayerSettings::scaleToPixelContext( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor, const QgsMapUnitScale& mapUnitScale ) const
3915 {
3916  // if render context is that of device (i.e. not a scaled map), just return size
3917  double mapUnitsPerPixel = mapUnitScale.computeMapUnitsPerPixel( c );
3918 
3919  if ( unit == MapUnits && mapUnitsPerPixel > 0.0 )
3920  {
3921  size = size / mapUnitsPerPixel * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3922  }
3923  else // e.g. in points or mm
3924  {
3925  double ptsTomm = ( unit == Points ? 0.352778 : 1 );
3926  size *= ptsTomm * c.scaleFactor() * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3927  }
3928  return size;
3929 }
3930 
3931 // -------------
3932 
3934  : mEngine( new QgsLabelingEngineV2() )
3935 {
3936 }
3937 
3939 {
3940  delete mEngine;
3941  mEngine = nullptr;
3942 }
3943 
3945 {
3946  return staticWillUseLayer( layer );
3947 }
3948 
3950 {
3951  QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( layerID ) );
3952  if ( !layer )
3953  return false;
3954  return staticWillUseLayer( layer );
3955 }
3956 
3957 
3959 {
3960  // don't do QgsPalLayerSettings::readFromLayer( layer ) if not needed
3961  bool enabled = false;
3962  if