QGIS API Documentation  2.17.0-Master (8784312)
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 = 20.0;
209  maxCurvedCharAngleOut = -20.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  // for polygons the "over point" (over centroid) placement is better than the default
793  // "around point" (around centroid) which is more suitable for points
794  if ( layer->geometryType() == QGis::Polygon )
796 
797  return; // there's no information available
798  }
799 
800  // NOTE: set defaults for newly added properties, for backwards compatibility
801 
802  enabled = layer->labelsEnabled();
803  drawLabels = layer->customProperty( "labeling/drawLabels", true ).toBool();
804 
805  // text style
806  fieldName = layer->customProperty( "labeling/fieldName" ).toString();
807  isExpression = layer->customProperty( "labeling/isExpression" ).toBool();
808  QFont appFont = QApplication::font();
809  mTextFontFamily = layer->customProperty( "labeling/fontFamily", QVariant( appFont.family() ) ).toString();
810  QString fontFamily = mTextFontFamily;
812  {
813  // trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
814  mTextFontFound = false;
815 
816  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
817  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
818 
819  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
820  fontFamily = appFont.family();
821  }
822 
823  double fontSize = layer->customProperty( "labeling/fontSize" ).toDouble();
824  fontSizeInMapUnits = layer->customProperty( "labeling/fontSizeInMapUnits" ).toBool();
825  if ( layer->customProperty( "labeling/fontSizeMapUnitScale" ).toString().isEmpty() )
826  {
827  //fallback to older property
828  fontSizeMapUnitScale.minScale = layer->customProperty( "labeling/fontSizeMapUnitMinScale", 0.0 ).toDouble();
829  fontSizeMapUnitScale.maxScale = layer->customProperty( "labeling/fontSizeMapUnitMaxScale", 0.0 ).toDouble();
830  }
831  else
832  {
833  fontSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/fontSizeMapUnitScale" ).toString() );
834  }
835  int fontWeight = layer->customProperty( "labeling/fontWeight" ).toInt();
836  bool fontItalic = layer->customProperty( "labeling/fontItalic" ).toBool();
837  textFont = QFont( fontFamily, fontSize, fontWeight, fontItalic );
838  textFont.setPointSizeF( fontSize ); //double precision needed because of map units
839  textNamedStyle = QgsFontUtils::translateNamedStyle( layer->customProperty( "labeling/namedStyle", QVariant( "" ) ).toString() );
840  QgsFontUtils::updateFontViaStyle( textFont, textNamedStyle ); // must come after textFont.setPointSizeF()
841  textFont.setCapitalization( static_cast< QFont::Capitalization >( layer->customProperty( "labeling/fontCapitals", QVariant( 0 ) ).toUInt() ) );
842  textFont.setUnderline( layer->customProperty( "labeling/fontUnderline" ).toBool() );
843  textFont.setStrikeOut( layer->customProperty( "labeling/fontStrikeout" ).toBool() );
844  textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( "labeling/fontLetterSpacing", QVariant( 0.0 ) ).toDouble() );
845  textFont.setWordSpacing( layer->customProperty( "labeling/fontWordSpacing", QVariant( 0.0 ) ).toDouble() );
846  textColor = _readColor( layer, "labeling/textColor", Qt::black, false );
847  textTransp = layer->customProperty( "labeling/textTransp" ).toInt();
849  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/blendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
850  previewBkgrdColor = QColor( layer->customProperty( "labeling/previewBkgrdColor", QVariant( "#ffffff" ) ).toString() );
851 
852 
853  // text formatting
854  wrapChar = layer->customProperty( "labeling/wrapChar" ).toString();
855  multilineHeight = layer->customProperty( "labeling/multilineHeight", QVariant( 1.0 ) ).toDouble();
856  multilineAlign = static_cast< MultiLineAlign >( layer->customProperty( "labeling/multilineAlign", QVariant( MultiLeft ) ).toUInt() );
857  addDirectionSymbol = layer->customProperty( "labeling/addDirectionSymbol" ).toBool();
858  leftDirectionSymbol = layer->customProperty( "labeling/leftDirectionSymbol", QVariant( "<" ) ).toString();
859  rightDirectionSymbol = layer->customProperty( "labeling/rightDirectionSymbol", QVariant( ">" ) ).toString();
860  reverseDirectionSymbol = layer->customProperty( "labeling/reverseDirectionSymbol" ).toBool();
861  placeDirectionSymbol = static_cast< DirectionSymbols >( layer->customProperty( "labeling/placeDirectionSymbol", QVariant( SymbolLeftRight ) ).toUInt() );
862  formatNumbers = layer->customProperty( "labeling/formatNumbers" ).toBool();
863  decimals = layer->customProperty( "labeling/decimals" ).toInt();
864  plusSign = layer->customProperty( "labeling/plussign" ).toBool();
865 
866  // text buffer
867  double bufSize = layer->customProperty( "labeling/bufferSize", QVariant( 0.0 ) ).toDouble();
868 
869  // fix for buffer being keyed off of just its size in the past (<2.0)
870  QVariant drawBuffer = layer->customProperty( "labeling/bufferDraw", QVariant() );
871  if ( drawBuffer.isValid() )
872  {
873  bufferDraw = drawBuffer.toBool();
874  bufferSize = bufSize;
875  }
876  else if ( bufSize != 0.0 )
877  {
878  bufferDraw = true;
879  bufferSize = bufSize;
880  }
881  else
882  {
883  // keep bufferSize at new 1.0 default
884  bufferDraw = false;
885  }
886 
887  bufferSizeInMapUnits = layer->customProperty( "labeling/bufferSizeInMapUnits" ).toBool();
888  if ( layer->customProperty( "labeling/bufferSizeMapUnitScale" ).toString().isEmpty() )
889  {
890  //fallback to older property
891  bufferSizeMapUnitScale.minScale = layer->customProperty( "labeling/bufferSizeMapUnitMinScale", 0.0 ).toDouble();
892  bufferSizeMapUnitScale.maxScale = layer->customProperty( "labeling/bufferSizeMapUnitMaxScale", 0.0 ).toDouble();
893  }
894  else
895  {
896  bufferSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/bufferSizeMapUnitScale" ).toString() );
897  }
898  bufferColor = _readColor( layer, "labeling/bufferColor", Qt::white, false );
899  bufferTransp = layer->customProperty( "labeling/bufferTransp" ).toInt();
901  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/bufferBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
902  bufferJoinStyle = static_cast< Qt::PenJoinStyle >( layer->customProperty( "labeling/bufferJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt() );
903  bufferNoFill = layer->customProperty( "labeling/bufferNoFill", QVariant( false ) ).toBool();
904 
905  // background
906  shapeDraw = layer->customProperty( "labeling/shapeDraw", QVariant( false ) ).toBool();
907  shapeType = static_cast< ShapeType >( layer->customProperty( "labeling/shapeType", QVariant( ShapeRectangle ) ).toUInt() );
908  shapeSVGFile = layer->customProperty( "labeling/shapeSVGFile", QVariant( "" ) ).toString();
909  shapeSizeType = static_cast< SizeType >( layer->customProperty( "labeling/shapeSizeType", QVariant( SizeBuffer ) ).toUInt() );
910  shapeSize = QPointF( layer->customProperty( "labeling/shapeSizeX", QVariant( 0.0 ) ).toDouble(),
911  layer->customProperty( "labeling/shapeSizeY", QVariant( 0.0 ) ).toDouble() );
912  shapeSizeUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeSizeUnits", QVariant( MM ) ).toUInt() );
913  if ( layer->customProperty( "labeling/shapeSizeMapUnitScale" ).toString().isEmpty() )
914  {
915  //fallback to older property
916  shapeSizeMapUnitScale.minScale = layer->customProperty( "labeling/shapeSizeMapUnitMinScale", 0.0 ).toDouble();
917  shapeSizeMapUnitScale.maxScale = layer->customProperty( "labeling/shapeSizeMapUnitMaxScale", 0.0 ).toDouble();
918  }
919  else
920  {
921  shapeSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeSizeMapUnitScale" ).toString() );
922  }
923  shapeRotationType = static_cast< RotationType >( layer->customProperty( "labeling/shapeRotationType", QVariant( RotationSync ) ).toUInt() );
924  shapeRotation = layer->customProperty( "labeling/shapeRotation", QVariant( 0.0 ) ).toDouble();
925  shapeOffset = QPointF( layer->customProperty( "labeling/shapeOffsetX", QVariant( 0.0 ) ).toDouble(),
926  layer->customProperty( "labeling/shapeOffsetY", QVariant( 0.0 ) ).toDouble() );
927  shapeOffsetUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeOffsetUnits", QVariant( MM ) ).toUInt() );
928  if ( layer->customProperty( "labeling/shapeOffsetMapUnitScale" ).toString().isEmpty() )
929  {
930  //fallback to older property
931  shapeOffsetMapUnitScale.minScale = layer->customProperty( "labeling/shapeOffsetMapUnitMinScale", 0.0 ).toDouble();
932  shapeOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/shapeOffsetMapUnitMaxScale", 0.0 ).toDouble();
933  }
934  else
935  {
936  shapeOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeOffsetMapUnitScale" ).toString() );
937  }
938  shapeRadii = QPointF( layer->customProperty( "labeling/shapeRadiiX", QVariant( 0.0 ) ).toDouble(),
939  layer->customProperty( "labeling/shapeRadiiY", QVariant( 0.0 ) ).toDouble() );
940  shapeRadiiUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeRadiiUnits", QVariant( MM ) ).toUInt() );
941  if ( layer->customProperty( "labeling/shapeRadiiMapUnitScale" ).toString().isEmpty() )
942  {
943  //fallback to older property
944  shapeRadiiMapUnitScale.minScale = layer->customProperty( "labeling/shapeRadiiMapUnitMinScale", 0.0 ).toDouble();
945  shapeRadiiMapUnitScale.maxScale = layer->customProperty( "labeling/shapeRadiiMapUnitMaxScale", 0.0 ).toDouble();
946  }
947  else
948  {
949  shapeRadiiMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeRadiiMapUnitScale" ).toString() );
950  }
951  shapeFillColor = _readColor( layer, "labeling/shapeFillColor", Qt::white, true );
952  shapeBorderColor = _readColor( layer, "labeling/shapeBorderColor", Qt::darkGray, true );
953  shapeBorderWidth = layer->customProperty( "labeling/shapeBorderWidth", QVariant( .0 ) ).toDouble();
954  shapeBorderWidthUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeBorderWidthUnits", QVariant( MM ) ).toUInt() );
955  if ( layer->customProperty( "labeling/shapeBorderWidthMapUnitScale" ).toString().isEmpty() )
956  {
957  //fallback to older property
958  shapeBorderWidthMapUnitScale.minScale = layer->customProperty( "labeling/shapeBorderWidthMapUnitMinScale", 0.0 ).toDouble();
959  shapeBorderWidthMapUnitScale.maxScale = layer->customProperty( "labeling/shapeBorderWidthMapUnitMaxScale", 0.0 ).toDouble();
960  }
961  else
962  {
963  shapeBorderWidthMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeBorderWidthMapUnitScale" ).toString() );
964  }
965  shapeJoinStyle = static_cast< Qt::PenJoinStyle >( layer->customProperty( "labeling/shapeJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt() );
966  shapeTransparency = layer->customProperty( "labeling/shapeTransparency", QVariant( 0 ) ).toInt();
968  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/shapeBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
969 
970  // drop shadow
971  shadowDraw = layer->customProperty( "labeling/shadowDraw", QVariant( false ) ).toBool();
972  shadowUnder = static_cast< ShadowType >( layer->customProperty( "labeling/shadowUnder", QVariant( ShadowLowest ) ).toUInt() );//ShadowLowest;
973  shadowOffsetAngle = layer->customProperty( "labeling/shadowOffsetAngle", QVariant( 135 ) ).toInt();
974  shadowOffsetDist = layer->customProperty( "labeling/shadowOffsetDist", QVariant( 1.0 ) ).toDouble();
975  shadowOffsetUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shadowOffsetUnits", QVariant( MM ) ).toUInt() );
976  if ( layer->customProperty( "labeling/shadowOffsetMapUnitScale" ).toString().isEmpty() )
977  {
978  //fallback to older property
979  shadowOffsetMapUnitScale.minScale = layer->customProperty( "labeling/shadowOffsetMapUnitMinScale", 0.0 ).toDouble();
980  shadowOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/shadowOffsetMapUnitMaxScale", 0.0 ).toDouble();
981  }
982  else
983  {
984  shadowOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shadowOffsetMapUnitScale" ).toString() );
985  }
986  shadowOffsetGlobal = layer->customProperty( "labeling/shadowOffsetGlobal", QVariant( true ) ).toBool();
987  shadowRadius = layer->customProperty( "labeling/shadowRadius", QVariant( 1.5 ) ).toDouble();
988  shadowRadiusUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shadowRadiusUnits", QVariant( MM ) ).toUInt() );
989  if ( layer->customProperty( "labeling/shadowRadiusMapUnitScale" ).toString().isEmpty() )
990  {
991  //fallback to older property
992  shadowRadiusMapUnitScale.minScale = layer->customProperty( "labeling/shadowRadiusMapUnitMinScale", 0.0 ).toDouble();
993  shadowRadiusMapUnitScale.maxScale = layer->customProperty( "labeling/shadowRadiusMapUnitMaxScale", 0.0 ).toDouble();
994  }
995  else
996  {
997  shadowRadiusMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shadowRadiusMapUnitScale" ).toString() );
998  }
999  shadowRadiusAlphaOnly = layer->customProperty( "labeling/shadowRadiusAlphaOnly", QVariant( false ) ).toBool();
1000  shadowTransparency = layer->customProperty( "labeling/shadowTransparency", QVariant( 30 ) ).toInt();
1001  shadowScale = layer->customProperty( "labeling/shadowScale", QVariant( 100 ) ).toInt();
1002  shadowColor = _readColor( layer, "labeling/shadowColor", Qt::black, false );
1004  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/shadowBlendMode", QVariant( QgsMapRenderer::BlendMultiply ) ).toUInt() ) );
1005 
1006  // placement
1007  placement = static_cast< Placement >( layer->customProperty( "labeling/placement" ).toInt() );
1008  placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt();
1009  centroidWhole = layer->customProperty( "labeling/centroidWhole", QVariant( false ) ).toBool();
1010  centroidInside = layer->customProperty( "labeling/centroidInside", QVariant( false ) ).toBool();
1011  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( layer->customProperty( "labeling/predefinedPositionOrder" ).toString() );
1013  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
1014  fitInPolygonOnly = layer->customProperty( "labeling/fitInPolygonOnly", QVariant( false ) ).toBool();
1015  dist = layer->customProperty( "labeling/dist" ).toDouble();
1016  distInMapUnits = layer->customProperty( "labeling/distInMapUnits" ).toBool();
1017  if ( layer->customProperty( "labeling/distMapUnitScale" ).toString().isEmpty() )
1018  {
1019  //fallback to older property
1020  distMapUnitScale.minScale = layer->customProperty( "labeling/distMapUnitMinScale", 0.0 ).toDouble();
1021  distMapUnitScale.maxScale = layer->customProperty( "labeling/distMapUnitMaxScale", 0.0 ).toDouble();
1022  }
1023  else
1024  {
1025  distMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/distMapUnitScale" ).toString() );
1026  }
1027  offsetType = static_cast< OffsetType >( layer->customProperty( "labeling/offsetType", QVariant( FromPoint ) ).toUInt() );
1028  quadOffset = static_cast< QuadrantPosition >( layer->customProperty( "labeling/quadOffset", QVariant( QuadrantOver ) ).toUInt() );
1029  xOffset = layer->customProperty( "labeling/xOffset", QVariant( 0.0 ) ).toDouble();
1030  yOffset = layer->customProperty( "labeling/yOffset", QVariant( 0.0 ) ).toDouble();
1031  labelOffsetInMapUnits = layer->customProperty( "labeling/labelOffsetInMapUnits", QVariant( true ) ).toBool();
1032  if ( layer->customProperty( "labeling/labelOffsetMapUnitScale" ).toString().isEmpty() )
1033  {
1034  //fallback to older property
1035  labelOffsetMapUnitScale.minScale = layer->customProperty( "labeling/labelOffsetMapUnitMinScale", 0.0 ).toDouble();
1036  labelOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/labelOffsetMapUnitMaxScale", 0.0 ).toDouble();
1037  }
1038  else
1039  {
1040  labelOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/labelOffsetMapUnitScale" ).toString() );
1041  }
1042  angleOffset = layer->customProperty( "labeling/angleOffset", QVariant( 0.0 ) ).toDouble();
1043  preserveRotation = layer->customProperty( "labeling/preserveRotation", QVariant( true ) ).toBool();
1044  maxCurvedCharAngleIn = layer->customProperty( "labeling/maxCurvedCharAngleIn", QVariant( 20.0 ) ).toDouble();
1045  maxCurvedCharAngleOut = layer->customProperty( "labeling/maxCurvedCharAngleOut", QVariant( -20.0 ) ).toDouble();
1046  priority = layer->customProperty( "labeling/priority" ).toInt();
1047  repeatDistance = layer->customProperty( "labeling/repeatDistance", 0.0 ).toDouble();
1048  repeatDistanceUnit = static_cast< SizeUnit >( layer->customProperty( "labeling/repeatDistanceUnit", QVariant( MM ) ).toUInt() );
1049  if ( layer->customProperty( "labeling/repeatDistanceMapUnitScale" ).toString().isEmpty() )
1050  {
1051  //fallback to older property
1052  repeatDistanceMapUnitScale.minScale = layer->customProperty( "labeling/repeatDistanceMapUnitMinScale", 0.0 ).toDouble();
1053  repeatDistanceMapUnitScale.maxScale = layer->customProperty( "labeling/repeatDistanceMapUnitMaxScale", 0.0 ).toDouble();
1054  }
1055  else
1056  {
1057  repeatDistanceMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/repeatDistanceMapUnitScale" ).toString() );
1058  }
1059 
1060  // rendering
1061  int scalemn = layer->customProperty( "labeling/scaleMin", QVariant( 0 ) ).toInt();
1062  int scalemx = layer->customProperty( "labeling/scaleMax", QVariant( 0 ) ).toInt();
1063 
1064  // fix for scale visibility limits being keyed off of just its values in the past (<2.0)
1065  QVariant scalevis = layer->customProperty( "labeling/scaleVisibility", QVariant() );
1066  if ( scalevis.isValid() )
1067  {
1068  scaleVisibility = scalevis.toBool();
1069  scaleMin = scalemn;
1070  scaleMax = scalemx;
1071  }
1072  else if ( scalemn > 0 || scalemx > 0 )
1073  {
1074  scaleVisibility = true;
1075  scaleMin = scalemn;
1076  scaleMax = scalemx;
1077  }
1078  else
1079  {
1080  // keep scaleMin and scaleMax at new 1.0 defaults (1 and 10000000, were 0 and 0)
1081  scaleVisibility = false;
1082  }
1083 
1084 
1085  fontLimitPixelSize = layer->customProperty( "labeling/fontLimitPixelSize", QVariant( false ) ).toBool();
1086  fontMinPixelSize = layer->customProperty( "labeling/fontMinPixelSize", QVariant( 0 ) ).toInt();
1087  fontMaxPixelSize = layer->customProperty( "labeling/fontMaxPixelSize", QVariant( 10000 ) ).toInt();
1088  displayAll = layer->customProperty( "labeling/displayAll", QVariant( false ) ).toBool();
1089  upsidedownLabels = static_cast< UpsideDownLabels >( layer->customProperty( "labeling/upsidedownLabels", QVariant( Upright ) ).toUInt() );
1090 
1091  labelPerPart = layer->customProperty( "labeling/labelPerPart" ).toBool();
1092  mergeLines = layer->customProperty( "labeling/mergeLines" ).toBool();
1093  minFeatureSize = layer->customProperty( "labeling/minFeatureSize" ).toDouble();
1094  limitNumLabels = layer->customProperty( "labeling/limitNumLabels", QVariant( false ) ).toBool();
1095  maxNumLabels = layer->customProperty( "labeling/maxNumLabels", QVariant( 2000 ) ).toInt();
1096  obstacle = layer->customProperty( "labeling/obstacle", QVariant( true ) ).toBool();
1097  obstacleFactor = layer->customProperty( "labeling/obstacleFactor", QVariant( 1.0 ) ).toDouble();
1098  obstacleType = static_cast< ObstacleType >( layer->customProperty( "labeling/obstacleType", QVariant( PolygonInterior ) ).toUInt() );
1099  zIndex = layer->customProperty( "labeling/zIndex", QVariant( 0.0 ) ).toDouble();
1100 
1101  readDataDefinedPropertyMap( layer, nullptr, dataDefinedProperties );
1102 }
1103 
1105 {
1106  // this is a mark that labeling information is present
1107  layer->setCustomProperty( "labeling", "pal" );
1108 
1109  layer->setCustomProperty( "labeling/enabled", enabled );
1110  layer->setCustomProperty( "labeling/drawLabels", drawLabels );
1111 
1112  // text style
1113  layer->setCustomProperty( "labeling/fieldName", fieldName );
1114  layer->setCustomProperty( "labeling/isExpression", isExpression );
1115  layer->setCustomProperty( "labeling/fontFamily", textFont.family() );
1116  layer->setCustomProperty( "labeling/namedStyle", QgsFontUtils::untranslateNamedStyle( textNamedStyle ) );
1117  layer->setCustomProperty( "labeling/fontSize", textFont.pointSizeF() );
1118  layer->setCustomProperty( "labeling/fontSizeInMapUnits", fontSizeInMapUnits );
1119  layer->setCustomProperty( "labeling/fontSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( fontSizeMapUnitScale ) );
1120  layer->setCustomProperty( "labeling/fontWeight", textFont.weight() );
1121  layer->setCustomProperty( "labeling/fontItalic", textFont.italic() );
1122  layer->setCustomProperty( "labeling/fontStrikeout", textFont.strikeOut() );
1123  layer->setCustomProperty( "labeling/fontUnderline", textFont.underline() );
1124  _writeColor( layer, "labeling/textColor", textColor );
1125  layer->setCustomProperty( "labeling/fontCapitals", static_cast< unsigned int >( textFont.capitalization() ) );
1126  layer->setCustomProperty( "labeling/fontLetterSpacing", textFont.letterSpacing() );
1127  layer->setCustomProperty( "labeling/fontWordSpacing", textFont.wordSpacing() );
1128  layer->setCustomProperty( "labeling/textTransp", textTransp );
1129  layer->setCustomProperty( "labeling/blendMode", QgsMapRenderer::getBlendModeEnum( blendMode ) );
1130  layer->setCustomProperty( "labeling/previewBkgrdColor", previewBkgrdColor.name() );
1131 
1132  // text formatting
1133  layer->setCustomProperty( "labeling/wrapChar", wrapChar );
1134  layer->setCustomProperty( "labeling/multilineHeight", multilineHeight );
1135  layer->setCustomProperty( "labeling/multilineAlign", static_cast< unsigned int >( multilineAlign ) );
1136  layer->setCustomProperty( "labeling/addDirectionSymbol", addDirectionSymbol );
1137  layer->setCustomProperty( "labeling/leftDirectionSymbol", leftDirectionSymbol );
1138  layer->setCustomProperty( "labeling/rightDirectionSymbol", rightDirectionSymbol );
1139  layer->setCustomProperty( "labeling/reverseDirectionSymbol", reverseDirectionSymbol );
1140  layer->setCustomProperty( "labeling/placeDirectionSymbol", static_cast< unsigned int >( placeDirectionSymbol ) );
1141  layer->setCustomProperty( "labeling/formatNumbers", formatNumbers );
1142  layer->setCustomProperty( "labeling/decimals", decimals );
1143  layer->setCustomProperty( "labeling/plussign", plusSign );
1144 
1145  // text buffer
1146  layer->setCustomProperty( "labeling/bufferDraw", bufferDraw );
1147  layer->setCustomProperty( "labeling/bufferSize", bufferSize );
1148  layer->setCustomProperty( "labeling/bufferSizeInMapUnits", bufferSizeInMapUnits );
1149  layer->setCustomProperty( "labeling/bufferSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( bufferSizeMapUnitScale ) );
1150  _writeColor( layer, "labeling/bufferColor", bufferColor );
1151  layer->setCustomProperty( "labeling/bufferNoFill", bufferNoFill );
1152  layer->setCustomProperty( "labeling/bufferTransp", bufferTransp );
1153  layer->setCustomProperty( "labeling/bufferJoinStyle", static_cast< unsigned int >( bufferJoinStyle ) );
1154  layer->setCustomProperty( "labeling/bufferBlendMode", QgsMapRenderer::getBlendModeEnum( bufferBlendMode ) );
1155 
1156  // background
1157  layer->setCustomProperty( "labeling/shapeDraw", shapeDraw );
1158  layer->setCustomProperty( "labeling/shapeType", static_cast< unsigned int >( shapeType ) );
1159  layer->setCustomProperty( "labeling/shapeSVGFile", shapeSVGFile );
1160  layer->setCustomProperty( "labeling/shapeSizeType", static_cast< unsigned int >( shapeSizeType ) );
1161  layer->setCustomProperty( "labeling/shapeSizeX", shapeSize.x() );
1162  layer->setCustomProperty( "labeling/shapeSizeY", shapeSize.y() );
1163  layer->setCustomProperty( "labeling/shapeSizeUnits", static_cast< unsigned int >( shapeSizeUnits ) );
1164  layer->setCustomProperty( "labeling/shapeSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeSizeMapUnitScale ) );
1165  layer->setCustomProperty( "labeling/shapeRotationType", static_cast< unsigned int >( shapeRotationType ) );
1166  layer->setCustomProperty( "labeling/shapeRotation", shapeRotation );
1167  layer->setCustomProperty( "labeling/shapeOffsetX", shapeOffset.x() );
1168  layer->setCustomProperty( "labeling/shapeOffsetY", shapeOffset.y() );
1169  layer->setCustomProperty( "labeling/shapeOffsetUnits", static_cast< unsigned int >( shapeOffsetUnits ) );
1170  layer->setCustomProperty( "labeling/shapeOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeOffsetMapUnitScale ) );
1171  layer->setCustomProperty( "labeling/shapeRadiiX", shapeRadii.x() );
1172  layer->setCustomProperty( "labeling/shapeRadiiY", shapeRadii.y() );
1173  layer->setCustomProperty( "labeling/shapeRadiiUnits", static_cast< unsigned int >( shapeRadiiUnits ) );
1174  layer->setCustomProperty( "labeling/shapeRadiiMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeRadiiMapUnitScale ) );
1175  _writeColor( layer, "labeling/shapeFillColor", shapeFillColor, true );
1176  _writeColor( layer, "labeling/shapeBorderColor", shapeBorderColor, true );
1177  layer->setCustomProperty( "labeling/shapeBorderWidth", shapeBorderWidth );
1178  layer->setCustomProperty( "labeling/shapeBorderWidthUnits", static_cast< unsigned int >( shapeBorderWidthUnits ) );
1179  layer->setCustomProperty( "labeling/shapeBorderWidthMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeBorderWidthMapUnitScale ) );
1180  layer->setCustomProperty( "labeling/shapeJoinStyle", static_cast< unsigned int >( shapeJoinStyle ) );
1181  layer->setCustomProperty( "labeling/shapeTransparency", shapeTransparency );
1182  layer->setCustomProperty( "labeling/shapeBlendMode", QgsMapRenderer::getBlendModeEnum( shapeBlendMode ) );
1183 
1184  // drop shadow
1185  layer->setCustomProperty( "labeling/shadowDraw", shadowDraw );
1186  layer->setCustomProperty( "labeling/shadowUnder", static_cast< unsigned int >( shadowUnder ) );
1187  layer->setCustomProperty( "labeling/shadowOffsetAngle", shadowOffsetAngle );
1188  layer->setCustomProperty( "labeling/shadowOffsetDist", shadowOffsetDist );
1189  layer->setCustomProperty( "labeling/shadowOffsetUnits", static_cast< unsigned int >( shadowOffsetUnits ) );
1190  layer->setCustomProperty( "labeling/shadowOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowOffsetMapUnitScale ) );
1191  layer->setCustomProperty( "labeling/shadowOffsetGlobal", shadowOffsetGlobal );
1192  layer->setCustomProperty( "labeling/shadowRadius", shadowRadius );
1193  layer->setCustomProperty( "labeling/shadowRadiusUnits", static_cast< unsigned int >( shadowRadiusUnits ) );
1194  layer->setCustomProperty( "labeling/shadowRadiusMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowRadiusMapUnitScale ) );
1195  layer->setCustomProperty( "labeling/shadowRadiusAlphaOnly", shadowRadiusAlphaOnly );
1196  layer->setCustomProperty( "labeling/shadowTransparency", shadowTransparency );
1197  layer->setCustomProperty( "labeling/shadowScale", shadowScale );
1198  _writeColor( layer, "labeling/shadowColor", shadowColor, false );
1199  layer->setCustomProperty( "labeling/shadowBlendMode", QgsMapRenderer::getBlendModeEnum( shadowBlendMode ) );
1200 
1201  // placement
1202  layer->setCustomProperty( "labeling/placement", placement );
1203  layer->setCustomProperty( "labeling/placementFlags", static_cast< unsigned int >( placementFlags ) );
1204  layer->setCustomProperty( "labeling/centroidWhole", centroidWhole );
1205  layer->setCustomProperty( "labeling/centroidInside", centroidInside );
1206  layer->setCustomProperty( "labeling/predefinedPositionOrder", QgsLabelingUtils::encodePredefinedPositionOrder( predefinedPositionOrder ) );
1207  layer->setCustomProperty( "labeling/fitInPolygonOnly", fitInPolygonOnly );
1208  layer->setCustomProperty( "labeling/dist", dist );
1209  layer->setCustomProperty( "labeling/distInMapUnits", distInMapUnits );
1210  layer->setCustomProperty( "labeling/distMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( distMapUnitScale ) );
1211  layer->setCustomProperty( "labeling/offsetType", static_cast< unsigned int >( offsetType ) );
1212  layer->setCustomProperty( "labeling/quadOffset", static_cast< unsigned int >( quadOffset ) );
1213  layer->setCustomProperty( "labeling/xOffset", xOffset );
1214  layer->setCustomProperty( "labeling/yOffset", yOffset );
1215  layer->setCustomProperty( "labeling/labelOffsetInMapUnits", labelOffsetInMapUnits );
1216  layer->setCustomProperty( "labeling/labelOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( labelOffsetMapUnitScale ) );
1217  layer->setCustomProperty( "labeling/angleOffset", angleOffset );
1218  layer->setCustomProperty( "labeling/preserveRotation", preserveRotation );
1219  layer->setCustomProperty( "labeling/maxCurvedCharAngleIn", maxCurvedCharAngleIn );
1220  layer->setCustomProperty( "labeling/maxCurvedCharAngleOut", maxCurvedCharAngleOut );
1221  layer->setCustomProperty( "labeling/priority", priority );
1222  layer->setCustomProperty( "labeling/repeatDistance", repeatDistance );
1223  layer->setCustomProperty( "labeling/repeatDistanceUnit", repeatDistanceUnit );
1224  layer->setCustomProperty( "labeling/repeatDistanceMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( repeatDistanceMapUnitScale ) );
1225 
1226  // rendering
1227  layer->setCustomProperty( "labeling/scaleVisibility", scaleVisibility );
1228  layer->setCustomProperty( "labeling/scaleMin", scaleMin );
1229  layer->setCustomProperty( "labeling/scaleMax", scaleMax );
1230  layer->setCustomProperty( "labeling/fontLimitPixelSize", fontLimitPixelSize );
1231  layer->setCustomProperty( "labeling/fontMinPixelSize", fontMinPixelSize );
1232  layer->setCustomProperty( "labeling/fontMaxPixelSize", fontMaxPixelSize );
1233  layer->setCustomProperty( "labeling/displayAll", displayAll );
1234  layer->setCustomProperty( "labeling/upsidedownLabels", static_cast< unsigned int >( upsidedownLabels ) );
1235 
1236  layer->setCustomProperty( "labeling/labelPerPart", labelPerPart );
1237  layer->setCustomProperty( "labeling/mergeLines", mergeLines );
1238  layer->setCustomProperty( "labeling/minFeatureSize", minFeatureSize );
1239  layer->setCustomProperty( "labeling/limitNumLabels", limitNumLabels );
1240  layer->setCustomProperty( "labeling/maxNumLabels", maxNumLabels );
1241  layer->setCustomProperty( "labeling/obstacle", obstacle );
1242  layer->setCustomProperty( "labeling/obstacleFactor", obstacleFactor );
1243  layer->setCustomProperty( "labeling/obstacleType", static_cast< unsigned int >( obstacleType ) );
1244  layer->setCustomProperty( "labeling/zIndex", zIndex );
1245 
1246  writeDataDefinedPropertyMap( layer, nullptr, dataDefinedProperties );
1247  layer->emitStyleChanged();
1248 }
1249 
1251 {
1252  enabled = true;
1253  drawLabels = true;
1254 
1255  // text style
1256  QDomElement textStyleElem = elem.firstChildElement( "text-style" );
1257  fieldName = textStyleElem.attribute( "fieldName" );
1258  isExpression = textStyleElem.attribute( "isExpression" ).toInt();
1259  QFont appFont = QApplication::font();
1260  mTextFontFamily = textStyleElem.attribute( "fontFamily", appFont.family() );
1261  QString fontFamily = mTextFontFamily;
1263  {
1264  // trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
1265  mTextFontFound = false;
1266 
1267  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
1268  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
1269 
1270  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
1271  fontFamily = appFont.family();
1272  }
1273 
1274  double fontSize = textStyleElem.attribute( "fontSize" ).toDouble();
1275  fontSizeInMapUnits = textStyleElem.attribute( "fontSizeInMapUnits" ).toInt();
1276  if ( !textStyleElem.hasAttribute( "fontSizeMapUnitScale" ) )
1277  {
1278  //fallback to older property
1279  fontSizeMapUnitScale.minScale = textStyleElem.attribute( "fontSizeMapUnitMinScale", "0" ).toDouble();
1280  fontSizeMapUnitScale.maxScale = textStyleElem.attribute( "fontSizeMapUnitMaxScale", "0" ).toDouble();
1281  }
1282  else
1283  {
1284  fontSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( textStyleElem.attribute( "fontSizeMapUnitScale" ) );
1285  }
1286  int fontWeight = textStyleElem.attribute( "fontWeight" ).toInt();
1287  bool fontItalic = textStyleElem.attribute( "fontItalic" ).toInt();
1288  textFont = QFont( fontFamily, fontSize, fontWeight, fontItalic );
1289  textFont.setPointSizeF( fontSize ); //double precision needed because of map units
1290  textNamedStyle = QgsFontUtils::translateNamedStyle( textStyleElem.attribute( "namedStyle" ) );
1291  QgsFontUtils::updateFontViaStyle( textFont, textNamedStyle ); // must come after textFont.setPointSizeF()
1292  textFont.setCapitalization( static_cast< QFont::Capitalization >( textStyleElem.attribute( "fontCapitals", "0" ).toUInt() ) );
1293  textFont.setUnderline( textStyleElem.attribute( "fontUnderline" ).toInt() );
1294  textFont.setStrikeOut( textStyleElem.attribute( "fontStrikeout" ).toInt() );
1295  textFont.setLetterSpacing( QFont::AbsoluteSpacing, textStyleElem.attribute( "fontLetterSpacing", "0" ).toDouble() );
1296  textFont.setWordSpacing( textStyleElem.attribute( "fontWordSpacing", "0" ).toDouble() );
1297  textColor = QgsSymbolLayerV2Utils::decodeColor( textStyleElem.attribute( "textColor", QgsSymbolLayerV2Utils::encodeColor( Qt::black ) ) );
1298  textTransp = textStyleElem.attribute( "textTransp" ).toInt();
1300  static_cast< QgsMapRenderer::BlendMode >( textStyleElem.attribute( "blendMode", QString::number( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
1301  previewBkgrdColor = QColor( textStyleElem.attribute( "previewBkgrdColor", "#ffffff" ) );
1302 
1303 
1304  // text formatting
1305  QDomElement textFormatElem = elem.firstChildElement( "text-format" );
1306  wrapChar = textFormatElem.attribute( "wrapChar" );
1307  multilineHeight = textFormatElem.attribute( "multilineHeight", "1" ).toDouble();
1308  multilineAlign = static_cast< MultiLineAlign >( textFormatElem.attribute( "multilineAlign", QString::number( MultiLeft ) ).toUInt() );
1309  addDirectionSymbol = textFormatElem.attribute( "addDirectionSymbol" ).toInt();
1310  leftDirectionSymbol = textFormatElem.attribute( "leftDirectionSymbol", "<" );
1311  rightDirectionSymbol = textFormatElem.attribute( "rightDirectionSymbol", ">" );
1312  reverseDirectionSymbol = textFormatElem.attribute( "reverseDirectionSymbol" ).toInt();
1313  placeDirectionSymbol = static_cast< DirectionSymbols >( textFormatElem.attribute( "placeDirectionSymbol", QString::number( SymbolLeftRight ) ).toUInt() );
1314  formatNumbers = textFormatElem.attribute( "formatNumbers" ).toInt();
1315  decimals = textFormatElem.attribute( "decimals" ).toInt();
1316  plusSign = textFormatElem.attribute( "plussign" ).toInt();
1317 
1318  // text buffer
1319  QDomElement textBufferElem = elem.firstChildElement( "text-buffer" );
1320  double bufSize = textBufferElem.attribute( "bufferSize", "0" ).toDouble();
1321 
1322  // fix for buffer being keyed off of just its size in the past (<2.0)
1323  QVariant drawBuffer = textBufferElem.attribute( "bufferDraw" );
1324  if ( drawBuffer.isValid() )
1325  {
1326  bufferDraw = drawBuffer.toBool();
1327  bufferSize = bufSize;
1328  }
1329  else if ( bufSize != 0.0 )
1330  {
1331  bufferDraw = true;
1332  bufferSize = bufSize;
1333  }
1334  else
1335  {
1336  // keep bufferSize at new 1.0 default
1337  bufferDraw = false;
1338  }
1339 
1340  bufferSizeInMapUnits = textBufferElem.attribute( "bufferSizeInMapUnits" ).toInt();
1341  if ( !textBufferElem.hasAttribute( "bufferSizeMapUnitScale" ) )
1342  {
1343  //fallback to older property
1344  bufferSizeMapUnitScale.minScale = textBufferElem.attribute( "bufferSizeMapUnitMinScale", "0" ).toDouble();
1345  bufferSizeMapUnitScale.maxScale = textBufferElem.attribute( "bufferSizeMapUnitMaxScale", "0" ).toDouble();
1346  }
1347  else
1348  {
1349  bufferSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( textBufferElem.attribute( "bufferSizeMapUnitScale" ) );
1350  }
1351  bufferColor = QgsSymbolLayerV2Utils::decodeColor( textBufferElem.attribute( "bufferColor", QgsSymbolLayerV2Utils::encodeColor( Qt::white ) ) );
1352  bufferTransp = textBufferElem.attribute( "bufferTransp" ).toInt();
1354  static_cast< QgsMapRenderer::BlendMode >( textBufferElem.attribute( "bufferBlendMode", QString::number( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
1355  bufferJoinStyle = static_cast< Qt::PenJoinStyle >( textBufferElem.attribute( "bufferJoinStyle", QString::number( Qt::BevelJoin ) ).toUInt() );
1356  bufferNoFill = textBufferElem.attribute( "bufferNoFill", "0" ).toInt();
1357 
1358  // background
1359  QDomElement backgroundElem = elem.firstChildElement( "background" );
1360  shapeDraw = backgroundElem.attribute( "shapeDraw", "0" ).toInt();
1361  shapeType = static_cast< ShapeType >( backgroundElem.attribute( "shapeType", QString::number( ShapeRectangle ) ).toUInt() );
1362  shapeSVGFile = backgroundElem.attribute( "shapeSVGFile" );
1363  shapeSizeType = static_cast< SizeType >( backgroundElem.attribute( "shapeSizeType", QString::number( SizeBuffer ) ).toUInt() );
1364  shapeSize = QPointF( backgroundElem.attribute( "shapeSizeX", "0" ).toDouble(),
1365  backgroundElem.attribute( "shapeSizeY", "0" ).toDouble() );
1366  shapeSizeUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeSizeUnits", QString::number( MM ) ).toUInt() );
1367  if ( !backgroundElem.hasAttribute( "shapeSizeMapUnitScale" ) )
1368  {
1369  //fallback to older property
1370  shapeSizeMapUnitScale.minScale = backgroundElem.attribute( "shapeSizeMapUnitMinScale", "0" ).toDouble();
1371  shapeSizeMapUnitScale.maxScale = backgroundElem.attribute( "shapeSizeMapUnitMaxScale", "0" ).toDouble();
1372  }
1373  else
1374  {
1375  shapeSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeSizeMapUnitScale" ) );
1376  }
1377  shapeRotationType = static_cast< RotationType >( backgroundElem.attribute( "shapeRotationType", QString::number( RotationSync ) ).toUInt() );
1378  shapeRotation = backgroundElem.attribute( "shapeRotation", "0" ).toDouble();
1379  shapeOffset = QPointF( backgroundElem.attribute( "shapeOffsetX", "0" ).toDouble(),
1380  backgroundElem.attribute( "shapeOffsetY", "0" ).toDouble() );
1381  shapeOffsetUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeOffsetUnits", QString::number( MM ) ).toUInt() );
1382  if ( !backgroundElem.hasAttribute( "shapeOffsetMapUnitScale" ) )
1383  {
1384  //fallback to older property
1385  shapeOffsetMapUnitScale.minScale = backgroundElem.attribute( "shapeOffsetMapUnitMinScale", "0" ).toDouble();
1386  shapeOffsetMapUnitScale.maxScale = backgroundElem.attribute( "shapeOffsetMapUnitMaxScale", "0" ).toDouble();
1387  }
1388  else
1389  {
1390  shapeOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeOffsetMapUnitScale" ) );
1391  }
1392  shapeRadii = QPointF( backgroundElem.attribute( "shapeRadiiX", "0" ).toDouble(),
1393  backgroundElem.attribute( "shapeRadiiY", "0" ).toDouble() );
1394  shapeRadiiUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeRadiiUnits", QString::number( MM ) ).toUInt() );
1395  if ( !backgroundElem.hasAttribute( "shapeRadiiMapUnitScale" ) )
1396  {
1397  //fallback to older property
1398  shapeRadiiMapUnitScale.minScale = backgroundElem.attribute( "shapeRadiiMapUnitMinScale", "0" ).toDouble();
1399  shapeRadiiMapUnitScale.maxScale = backgroundElem.attribute( "shapeRadiiMapUnitMaxScale", "0" ).toDouble();
1400  }
1401  else
1402  {
1403  shapeRadiiMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeRadiiMapUnitScale" ) );
1404  }
1405  shapeFillColor = QgsSymbolLayerV2Utils::decodeColor( backgroundElem.attribute( "shapeFillColor", QgsSymbolLayerV2Utils::encodeColor( Qt::white ) ) );
1406  shapeBorderColor = QgsSymbolLayerV2Utils::decodeColor( backgroundElem.attribute( "shapeBorderColor", QgsSymbolLayerV2Utils::encodeColor( Qt::darkGray ) ) );
1407  shapeBorderWidth = backgroundElem.attribute( "shapeBorderWidth", "0" ).toDouble();
1408  shapeBorderWidthUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeBorderWidthUnits", QString::number( MM ) ).toUInt() );
1409  if ( !backgroundElem.hasAttribute( "shapeBorderWidthMapUnitScale" ) )
1410  {
1411  //fallback to older property
1412  shapeBorderWidthMapUnitScale.minScale = backgroundElem.attribute( "shapeBorderWidthMapUnitMinScale", "0" ).toDouble();
1413  shapeBorderWidthMapUnitScale.maxScale = backgroundElem.attribute( "shapeBorderWidthMapUnitMaxScale", "0" ).toDouble();
1414  }
1415  else
1416  {
1417  shapeBorderWidthMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeBorderWidthMapUnitScale" ) );
1418  }
1419  shapeJoinStyle = static_cast< Qt::PenJoinStyle >( backgroundElem.attribute( "shapeJoinStyle", QString::number( Qt::BevelJoin ) ).toUInt() );
1420  shapeTransparency = backgroundElem.attribute( "shapeTransparency", "0" ).toInt();
1422  static_cast< QgsMapRenderer::BlendMode >( backgroundElem.attribute( "shapeBlendMode", QString::number( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
1423 
1424  // drop shadow
1425  QDomElement shadowElem = elem.firstChildElement( "shadow" );
1426  shadowDraw = shadowElem.attribute( "shadowDraw", "0" ).toInt();
1427  shadowUnder = static_cast< ShadowType >( shadowElem.attribute( "shadowUnder", QString::number( ShadowLowest ) ).toUInt() );//ShadowLowest ;
1428  shadowOffsetAngle = shadowElem.attribute( "shadowOffsetAngle", "135" ).toInt();
1429  shadowOffsetDist = shadowElem.attribute( "shadowOffsetDist", "1" ).toDouble();
1430  shadowOffsetUnits = static_cast< SizeUnit >( shadowElem.attribute( "shadowOffsetUnits", QString::number( MM ) ).toUInt() );
1431  if ( !shadowElem.hasAttribute( "shadowOffsetMapUnitScale" ) )
1432  {
1433  //fallback to older property
1434  shadowOffsetMapUnitScale.minScale = shadowElem.attribute( "shadowOffsetMapUnitMinScale", "0" ).toDouble();
1435  shadowOffsetMapUnitScale.maxScale = shadowElem.attribute( "shadowOffsetMapUnitMaxScale", "0" ).toDouble();
1436  }
1437  else
1438  {
1439  shadowOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( shadowElem.attribute( "shadowOffsetMapUnitScale" ) );
1440  }
1441  shadowOffsetGlobal = shadowElem.attribute( "shadowOffsetGlobal", "1" ).toInt();
1442  shadowRadius = shadowElem.attribute( "shadowRadius", "1.5" ).toDouble();
1443  shadowRadiusUnits = static_cast< SizeUnit >( shadowElem.attribute( "shadowRadiusUnits", QString::number( MM ) ).toUInt() );
1444  if ( !shadowElem.hasAttribute( "shadowRadiusMapUnitScale" ) )
1445  {
1446  //fallback to older property
1447  shadowRadiusMapUnitScale.minScale = shadowElem.attribute( "shadowRadiusMapUnitMinScale", "0" ).toDouble();
1448  shadowRadiusMapUnitScale.maxScale = shadowElem.attribute( "shadowRadiusMapUnitMaxScale", "0" ).toDouble();
1449  }
1450  else
1451  {
1452  shadowRadiusMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( shadowElem.attribute( "shadowRadiusMapUnitScale" ) );
1453  }
1454  shadowRadiusAlphaOnly = shadowElem.attribute( "shadowRadiusAlphaOnly", "0" ).toInt();
1455  shadowTransparency = shadowElem.attribute( "shadowTransparency", "30" ).toInt();
1456  shadowScale = shadowElem.attribute( "shadowScale", "100" ).toInt();
1457  shadowColor = QgsSymbolLayerV2Utils::decodeColor( shadowElem.attribute( "shadowColor", QgsSymbolLayerV2Utils::encodeColor( Qt::black ) ) );
1459  static_cast< QgsMapRenderer::BlendMode >( shadowElem.attribute( "shadowBlendMode", QString::number( QgsMapRenderer::BlendMultiply ) ).toUInt() ) );
1460 
1461  // placement
1462  QDomElement placementElem = elem.firstChildElement( "placement" );
1463  placement = static_cast< Placement >( placementElem.attribute( "placement" ).toInt() );
1464  placementFlags = placementElem.attribute( "placementFlags" ).toUInt();
1465  centroidWhole = placementElem.attribute( "centroidWhole", "0" ).toInt();
1466  centroidInside = placementElem.attribute( "centroidInside", "0" ).toInt();
1467  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( placementElem.attribute( "predefinedPositionOrder" ) );
1469  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
1470  fitInPolygonOnly = placementElem.attribute( "fitInPolygonOnly", "0" ).toInt();
1471  dist = placementElem.attribute( "dist" ).toDouble();
1472  distInMapUnits = placementElem.attribute( "distInMapUnits" ).toInt();
1473  if ( !placementElem.hasAttribute( "distMapUnitScale" ) )
1474  {
1475  //fallback to older property
1476  distMapUnitScale.minScale = placementElem.attribute( "distMapUnitMinScale", "0" ).toDouble();
1477  distMapUnitScale.maxScale = placementElem.attribute( "distMapUnitMaxScale", "0" ).toDouble();
1478  }
1479  else
1480  {
1481  distMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( placementElem.attribute( "distMapUnitScale" ) );
1482  }
1483  offsetType = static_cast< OffsetType >( placementElem.attribute( "offsetType", QString::number( FromPoint ) ).toUInt() );
1484  quadOffset = static_cast< QuadrantPosition >( placementElem.attribute( "quadOffset", QString::number( QuadrantOver ) ).toUInt() );
1485  xOffset = placementElem.attribute( "xOffset", "0" ).toDouble();
1486  yOffset = placementElem.attribute( "yOffset", "0" ).toDouble();
1487  labelOffsetInMapUnits = placementElem.attribute( "labelOffsetInMapUnits", "1" ).toInt();
1488  if ( !placementElem.hasAttribute( "labelOffsetMapUnitScale" ) )
1489  {
1490  //fallback to older property
1491  labelOffsetMapUnitScale.minScale = placementElem.attribute( "labelOffsetMapUnitMinScale", "0" ).toDouble();
1492  labelOffsetMapUnitScale.maxScale = placementElem.attribute( "labelOffsetMapUnitMaxScale", "0" ).toDouble();
1493  }
1494  else
1495  {
1496  labelOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( placementElem.attribute( "labelOffsetMapUnitScale" ) );
1497  }
1498  angleOffset = placementElem.attribute( "angleOffset", "0" ).toDouble();
1499  preserveRotation = placementElem.attribute( "preserveRotation", "1" ).toInt();
1500  maxCurvedCharAngleIn = placementElem.attribute( "maxCurvedCharAngleIn", "20" ).toDouble();
1501  maxCurvedCharAngleOut = placementElem.attribute( "maxCurvedCharAngleOut", "-20" ).toDouble();
1502  priority = placementElem.attribute( "priority" ).toInt();
1503  repeatDistance = placementElem.attribute( "repeatDistance", "0" ).toDouble();
1504  repeatDistanceUnit = static_cast< SizeUnit >( placementElem.attribute( "repeatDistanceUnit", QString::number( MM ) ).toUInt() );
1505  if ( !placementElem.hasAttribute( "repeatDistanceMapUnitScale" ) )
1506  {
1507  //fallback to older property
1508  repeatDistanceMapUnitScale.minScale = placementElem.attribute( "repeatDistanceMapUnitMinScale", "0" ).toDouble();
1509  repeatDistanceMapUnitScale.maxScale = placementElem.attribute( "repeatDistanceMapUnitMaxScale", "0" ).toDouble();
1510  }
1511  else
1512  {
1513  repeatDistanceMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( placementElem.attribute( "repeatDistanceMapUnitScale" ) );
1514  }
1515 
1516  // rendering
1517  QDomElement renderingElem = elem.firstChildElement( "rendering" );
1518  scaleMin = renderingElem.attribute( "scaleMin", "0" ).toInt();
1519  scaleMax = renderingElem.attribute( "scaleMax", "0" ).toInt();
1520  scaleVisibility = renderingElem.attribute( "scaleVisibility" ).toInt();
1521 
1522  fontLimitPixelSize = renderingElem.attribute( "fontLimitPixelSize", "0" ).toInt();
1523  fontMinPixelSize = renderingElem.attribute( "fontMinPixelSize", "0" ).toInt();
1524  fontMaxPixelSize = renderingElem.attribute( "fontMaxPixelSize", "10000" ).toInt();
1525  displayAll = renderingElem.attribute( "displayAll", "0" ).toInt();
1526  upsidedownLabels = static_cast< UpsideDownLabels >( renderingElem.attribute( "upsidedownLabels", QString::number( Upright ) ).toUInt() );
1527 
1528  labelPerPart = renderingElem.attribute( "labelPerPart" ).toInt();
1529  mergeLines = renderingElem.attribute( "mergeLines" ).toInt();
1530  minFeatureSize = renderingElem.attribute( "minFeatureSize" ).toDouble();
1531  limitNumLabels = renderingElem.attribute( "limitNumLabels", "0" ).toInt();
1532  maxNumLabels = renderingElem.attribute( "maxNumLabels", "2000" ).toInt();
1533  obstacle = renderingElem.attribute( "obstacle", "1" ).toInt();
1534  obstacleFactor = renderingElem.attribute( "obstacleFactor", "1" ).toDouble();
1535  obstacleType = static_cast< ObstacleType >( renderingElem.attribute( "obstacleType", QString::number( PolygonInterior ) ).toUInt() );
1536  zIndex = renderingElem.attribute( "zIndex", "0.0" ).toDouble();
1537 
1538  QDomElement ddElem = elem.firstChildElement( "data-defined" );
1539  readDataDefinedPropertyMap( nullptr, &ddElem, dataDefinedProperties );
1540 }
1541 
1542 
1543 
1545 {
1546  // we assume (enabled == true && drawLabels == true) so those are not saved
1547 
1548  // text style
1549  QDomElement textStyleElem = doc.createElement( "text-style" );
1550  textStyleElem.setAttribute( "fieldName", fieldName );
1551  textStyleElem.setAttribute( "isExpression", isExpression );
1552  textStyleElem.setAttribute( "fontFamily", textFont.family() );
1553  textStyleElem.setAttribute( "namedStyle", QgsFontUtils::untranslateNamedStyle( textNamedStyle ) );
1554  textStyleElem.setAttribute( "fontSize", textFont.pointSizeF() );
1555  textStyleElem.setAttribute( "fontSizeInMapUnits", fontSizeInMapUnits );
1556  textStyleElem.setAttribute( "fontSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( fontSizeMapUnitScale ) );
1557  textStyleElem.setAttribute( "fontWeight", textFont.weight() );
1558  textStyleElem.setAttribute( "fontItalic", textFont.italic() );
1559  textStyleElem.setAttribute( "fontStrikeout", textFont.strikeOut() );
1560  textStyleElem.setAttribute( "fontUnderline", textFont.underline() );
1561  textStyleElem.setAttribute( "textColor", QgsSymbolLayerV2Utils::encodeColor( textColor ) );
1562  textStyleElem.setAttribute( "fontCapitals", static_cast< unsigned int >( textFont.capitalization() ) );
1563  textStyleElem.setAttribute( "fontLetterSpacing", textFont.letterSpacing() );
1564  textStyleElem.setAttribute( "fontWordSpacing", textFont.wordSpacing() );
1565  textStyleElem.setAttribute( "textTransp", textTransp );
1566  textStyleElem.setAttribute( "blendMode", QgsMapRenderer::getBlendModeEnum( blendMode ) );
1567  textStyleElem.setAttribute( "previewBkgrdColor", previewBkgrdColor.name() );
1568 
1569  // text formatting
1570  QDomElement textFormatElem = doc.createElement( "text-format" );
1571  textFormatElem.setAttribute( "wrapChar", wrapChar );
1572  textFormatElem.setAttribute( "multilineHeight", multilineHeight );
1573  textFormatElem.setAttribute( "multilineAlign", static_cast< unsigned int >( multilineAlign ) );
1574  textFormatElem.setAttribute( "addDirectionSymbol", addDirectionSymbol );
1575  textFormatElem.setAttribute( "leftDirectionSymbol", leftDirectionSymbol );
1576  textFormatElem.setAttribute( "rightDirectionSymbol", rightDirectionSymbol );
1577  textFormatElem.setAttribute( "reverseDirectionSymbol", reverseDirectionSymbol );
1578  textFormatElem.setAttribute( "placeDirectionSymbol", static_cast< unsigned int >( placeDirectionSymbol ) );
1579  textFormatElem.setAttribute( "formatNumbers", formatNumbers );
1580  textFormatElem.setAttribute( "decimals", decimals );
1581  textFormatElem.setAttribute( "plussign", plusSign );
1582 
1583  // text buffer
1584  QDomElement textBufferElem = doc.createElement( "text-buffer" );
1585  textBufferElem.setAttribute( "bufferDraw", bufferDraw );
1586  textBufferElem.setAttribute( "bufferSize", bufferSize );
1587  textBufferElem.setAttribute( "bufferSizeInMapUnits", bufferSizeInMapUnits );
1588  textBufferElem.setAttribute( "bufferSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( bufferSizeMapUnitScale ) );
1589  textBufferElem.setAttribute( "bufferColor", QgsSymbolLayerV2Utils::encodeColor( bufferColor ) );
1590  textBufferElem.setAttribute( "bufferNoFill", bufferNoFill );
1591  textBufferElem.setAttribute( "bufferTransp", bufferTransp );
1592  textBufferElem.setAttribute( "bufferJoinStyle", static_cast< unsigned int >( bufferJoinStyle ) );
1593  textBufferElem.setAttribute( "bufferBlendMode", QgsMapRenderer::getBlendModeEnum( bufferBlendMode ) );
1594 
1595  // background
1596  QDomElement backgroundElem = doc.createElement( "background" );
1597  backgroundElem.setAttribute( "shapeDraw", shapeDraw );
1598  backgroundElem.setAttribute( "shapeType", static_cast< unsigned int >( shapeType ) );
1599  backgroundElem.setAttribute( "shapeSVGFile", shapeSVGFile );
1600  backgroundElem.setAttribute( "shapeSizeType", static_cast< unsigned int >( shapeSizeType ) );
1601  backgroundElem.setAttribute( "shapeSizeX", shapeSize.x() );
1602  backgroundElem.setAttribute( "shapeSizeY", shapeSize.y() );
1603  backgroundElem.setAttribute( "shapeSizeUnits", static_cast< unsigned int >( shapeSizeUnits ) );
1604  backgroundElem.setAttribute( "shapeSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeSizeMapUnitScale ) );
1605  backgroundElem.setAttribute( "shapeRotationType", static_cast< unsigned int >( shapeRotationType ) );
1606  backgroundElem.setAttribute( "shapeRotation", shapeRotation );
1607  backgroundElem.setAttribute( "shapeOffsetX", shapeOffset.x() );
1608  backgroundElem.setAttribute( "shapeOffsetY", shapeOffset.y() );
1609  backgroundElem.setAttribute( "shapeOffsetUnits", static_cast< unsigned int >( shapeOffsetUnits ) );
1610  backgroundElem.setAttribute( "shapeOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeOffsetMapUnitScale ) );
1611  backgroundElem.setAttribute( "shapeRadiiX", shapeRadii.x() );
1612  backgroundElem.setAttribute( "shapeRadiiY", shapeRadii.y() );
1613  backgroundElem.setAttribute( "shapeRadiiUnits", static_cast< unsigned int >( shapeRadiiUnits ) );
1614  backgroundElem.setAttribute( "shapeRadiiMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeRadiiMapUnitScale ) );
1615  backgroundElem.setAttribute( "shapeFillColor", QgsSymbolLayerV2Utils::encodeColor( shapeFillColor ) );
1616  backgroundElem.setAttribute( "shapeBorderColor", QgsSymbolLayerV2Utils::encodeColor( shapeBorderColor ) );
1617  backgroundElem.setAttribute( "shapeBorderWidth", shapeBorderWidth );
1618  backgroundElem.setAttribute( "shapeBorderWidthUnits", static_cast< unsigned int >( shapeBorderWidthUnits ) );
1619  backgroundElem.setAttribute( "shapeBorderWidthMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeBorderWidthMapUnitScale ) );
1620  backgroundElem.setAttribute( "shapeJoinStyle", static_cast< unsigned int >( shapeJoinStyle ) );
1621  backgroundElem.setAttribute( "shapeTransparency", shapeTransparency );
1622  backgroundElem.setAttribute( "shapeBlendMode", QgsMapRenderer::getBlendModeEnum( shapeBlendMode ) );
1623 
1624  // drop shadow
1625  QDomElement shadowElem = doc.createElement( "shadow" );
1626  shadowElem.setAttribute( "shadowDraw", shadowDraw );
1627  shadowElem.setAttribute( "shadowUnder", static_cast< unsigned int >( shadowUnder ) );
1628  shadowElem.setAttribute( "shadowOffsetAngle", shadowOffsetAngle );
1629  shadowElem.setAttribute( "shadowOffsetDist", shadowOffsetDist );
1630  shadowElem.setAttribute( "shadowOffsetUnits", static_cast< unsigned int >( shadowOffsetUnits ) );
1631  shadowElem.setAttribute( "shadowOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowOffsetMapUnitScale ) );
1632  shadowElem.setAttribute( "shadowOffsetGlobal", shadowOffsetGlobal );
1633  shadowElem.setAttribute( "shadowRadius", shadowRadius );
1634  shadowElem.setAttribute( "shadowRadiusUnits", static_cast< unsigned int >( shadowRadiusUnits ) );
1635  shadowElem.setAttribute( "shadowRadiusMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowRadiusMapUnitScale ) );
1636  shadowElem.setAttribute( "shadowRadiusAlphaOnly", shadowRadiusAlphaOnly );
1637  shadowElem.setAttribute( "shadowTransparency", shadowTransparency );
1638  shadowElem.setAttribute( "shadowScale", shadowScale );
1639  shadowElem.setAttribute( "shadowColor", QgsSymbolLayerV2Utils::encodeColor( shadowColor ) );
1640  shadowElem.setAttribute( "shadowBlendMode", QgsMapRenderer::getBlendModeEnum( shadowBlendMode ) );
1641 
1642  // placement
1643  QDomElement placementElem = doc.createElement( "placement" );
1644  placementElem.setAttribute( "placement", placement );
1645  placementElem.setAttribute( "placementFlags", static_cast< unsigned int >( placementFlags ) );
1646  placementElem.setAttribute( "centroidWhole", centroidWhole );
1647  placementElem.setAttribute( "centroidInside", centroidInside );
1648  placementElem.setAttribute( "predefinedPositionOrder", QgsLabelingUtils::encodePredefinedPositionOrder( predefinedPositionOrder ) );
1649  placementElem.setAttribute( "fitInPolygonOnly", fitInPolygonOnly );
1650  placementElem.setAttribute( "dist", dist );
1651  placementElem.setAttribute( "distInMapUnits", distInMapUnits );
1652  placementElem.setAttribute( "distMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( distMapUnitScale ) );
1653  placementElem.setAttribute( "offsetType", static_cast< unsigned int >( offsetType ) );
1654  placementElem.setAttribute( "quadOffset", static_cast< unsigned int >( quadOffset ) );
1655  placementElem.setAttribute( "xOffset", xOffset );
1656  placementElem.setAttribute( "yOffset", yOffset );
1657  placementElem.setAttribute( "labelOffsetInMapUnits", labelOffsetInMapUnits );
1658  placementElem.setAttribute( "labelOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( labelOffsetMapUnitScale ) );
1659  placementElem.setAttribute( "angleOffset", angleOffset );
1660  placementElem.setAttribute( "preserveRotation", preserveRotation );
1661  placementElem.setAttribute( "maxCurvedCharAngleIn", maxCurvedCharAngleIn );
1662  placementElem.setAttribute( "maxCurvedCharAngleOut", maxCurvedCharAngleOut );
1663  placementElem.setAttribute( "priority", priority );
1664  placementElem.setAttribute( "repeatDistance", repeatDistance );
1665  placementElem.setAttribute( "repeatDistanceUnit", repeatDistanceUnit );
1666  placementElem.setAttribute( "repeatDistanceMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( repeatDistanceMapUnitScale ) );
1667 
1668  // rendering
1669  QDomElement renderingElem = doc.createElement( "rendering" );
1670  renderingElem.setAttribute( "scaleVisibility", scaleVisibility );
1671  renderingElem.setAttribute( "scaleMin", scaleMin );
1672  renderingElem.setAttribute( "scaleMax", scaleMax );
1673  renderingElem.setAttribute( "fontLimitPixelSize", fontLimitPixelSize );
1674  renderingElem.setAttribute( "fontMinPixelSize", fontMinPixelSize );
1675  renderingElem.setAttribute( "fontMaxPixelSize", fontMaxPixelSize );
1676  renderingElem.setAttribute( "displayAll", displayAll );
1677  renderingElem.setAttribute( "upsidedownLabels", static_cast< unsigned int >( upsidedownLabels ) );
1678 
1679  renderingElem.setAttribute( "labelPerPart", labelPerPart );
1680  renderingElem.setAttribute( "mergeLines", mergeLines );
1681  renderingElem.setAttribute( "minFeatureSize", minFeatureSize );
1682  renderingElem.setAttribute( "limitNumLabels", limitNumLabels );
1683  renderingElem.setAttribute( "maxNumLabels", maxNumLabels );
1684  renderingElem.setAttribute( "obstacle", obstacle );
1685  renderingElem.setAttribute( "obstacleFactor", obstacleFactor );
1686  renderingElem.setAttribute( "obstacleType", static_cast< unsigned int >( obstacleType ) );
1687  renderingElem.setAttribute( "zIndex", zIndex );
1688 
1689  QDomElement ddElem = doc.createElement( "data-defined" );
1690  writeDataDefinedPropertyMap( nullptr, &ddElem, dataDefinedProperties );
1691 
1692  QDomElement elem = doc.createElement( "settings" );
1693  elem.appendChild( textStyleElem );
1694  elem.appendChild( textFormatElem );
1695  elem.appendChild( textBufferElem );
1696  elem.appendChild( backgroundElem );
1697  elem.appendChild( shadowElem );
1698  elem.appendChild( placementElem );
1699  elem.appendChild( renderingElem );
1700  elem.appendChild( ddElem );
1701  return elem;
1702 }
1703 
1705  bool active, bool useExpr, const QString& expr, const QString& field )
1706 {
1707  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
1708 
1709  if ( dataDefinedProperties.contains( p ) )
1710  {
1712  if ( it != dataDefinedProperties.constEnd() )
1713  {
1714  QgsDataDefined* dd = it.value();
1715  dd->setActive( active );
1716  dd->setExpressionString( expr );
1717  dd->setField( field );
1718  dd->setUseExpression( useExpr );
1719  }
1720  }
1721  else if ( !defaultVals )
1722  {
1723  QgsDataDefined* dd = new QgsDataDefined( active, useExpr, expr, field );
1724  dataDefinedProperties.insert( p, dd );
1725  }
1726 }
1727 
1729 {
1731  if ( it != dataDefinedProperties.end() )
1732  {
1733  delete( it.value() );
1735  }
1736 }
1737 
1739 {
1740  qDeleteAll( dataDefinedProperties );
1742 }
1743 
1745 {
1746  // TODO: update or remove this when project settings for labeling are migrated to better XML layout
1747  QString newValue = value;
1748  if ( !value.isEmpty() && !value.contains( "~~" ) )
1749  {
1750  QStringList values;
1751  values << "1"; // all old-style values are active if not empty
1752  values << "0";
1753  values << "";
1754  values << value; // all old-style values are only field names
1755  newValue = values.join( "~~" );
1756  }
1757 
1758  return newValue;
1759 }
1760 
1762 {
1764  return nullptr;
1765 
1767  if ( it != dataDefinedProperties.constEnd() )
1768  {
1769  return it.value();
1770  }
1771  return nullptr;
1772 }
1773 
1775 {
1778  if ( it != dataDefinedProperties.constEnd() )
1779  {
1780  return it.value()->toMap();
1781  }
1782  return map;
1783 }
1784 
1786 {
1788  {
1789  return QVariant();
1790  }
1791 
1792  //try to keep < 2.12 API - handle no passed expression context
1794  if ( !context )
1795  {
1796  scopedEc.reset( new QgsExpressionContext() );
1797  scopedEc->setFeature( f );
1798  scopedEc->setFields( fields );
1799  }
1800  const QgsExpressionContext* ec = context ? context : scopedEc.data();
1801 
1802  QgsDataDefined* dd = nullptr;
1804  if ( it != dataDefinedProperties.constEnd() )
1805  {
1806  dd = it.value();
1807  }
1808 
1809  if ( !dd )
1810  {
1811  return QVariant();
1812  }
1813 
1814  if ( !dd->isActive() )
1815  {
1816  return QVariant();
1817  }
1818 
1819  QVariant result = QVariant();
1820  bool useExpression = dd->useExpression();
1821  QString field = dd->field();
1822 
1823  //QgsDebugMsgLevel( QString( "isActive:" ) + isActive ? "1" : "0", 4 );
1824  //QgsDebugMsgLevel( QString( "useExpression:" ) + useExpression ? "1" : "0", 4 );
1825  //QgsDebugMsgLevel( QString( "expression:" ) + dd->expressionString(), 4 );
1826  //QgsDebugMsgLevel( QString( "field:" ) + field, 4 );
1827 
1828  if ( useExpression && dd->expressionIsPrepared() )
1829  {
1830  QgsExpression* expr = dd->expression();
1831  //QgsDebugMsgLevel( QString( "expr columns:" ) + expr->referencedColumns().join( "," ), 4 );
1832 
1833  result = expr->evaluate( ec );
1834  if ( expr->hasEvalError() )
1835  {
1836  QgsDebugMsgLevel( QString( "Evaluate error:" ) + expr->evalErrorString(), 4 );
1837  return QVariant();
1838  }
1839  }
1840  else if ( !useExpression && !field.isEmpty() )
1841  {
1842  // use direct attribute access instead of evaluating "field" expression (much faster)
1843  int indx = fields.indexFromName( field );
1844  if ( indx != -1 )
1845  {
1846  result = f.attribute( indx );
1847  }
1848  }
1849  return result;
1850 }
1851 
1853 {
1854  // null passed-around QVariant
1855  exprVal.clear();
1857  return false;
1858 
1859  //try to keep < 2.12 API - handle no passed expression context
1861  if ( !context )
1862  {
1863  scopedEc.reset( new QgsExpressionContext() );
1864  scopedEc->setFeature( *mCurFeat );
1865  scopedEc->setFields( mCurFields );
1866  }
1867  QgsExpressionContext* ec = context ? context : scopedEc.data();
1868 
1869  ec->setOriginalValueVariable( originalValue );
1870  QVariant result = dataDefinedValue( p, *mCurFeat, mCurFields, ec );
1871 
1872  if ( result.isValid() && !result.isNull() )
1873  {
1874  //QgsDebugMsgLevel( QString( "result type:" ) + QString( result.typeName() ), 4 );
1875  //QgsDebugMsgLevel( QString( "result string:" ) + result.toString(), 4 );
1876  exprVal = result;
1877  return true;
1878  }
1879 
1880  return false;
1881 }
1882 
1884 {
1886  return false;
1887 
1888  bool isActive = false;
1889 
1891  if ( it != dataDefinedProperties.constEnd() )
1892  {
1893  isActive = it.value()->isActive();
1894  }
1895 
1896  return isActive;
1897 }
1898 
1900 {
1902  return false;
1903 
1904  bool useExpression = false;
1906  if ( it != dataDefinedProperties.constEnd() )
1907  {
1908  useExpression = it.value()->useExpression();
1909  }
1910 
1911  return useExpression;
1912 }
1913 
1914 bool QgsPalLayerSettings::checkMinimumSizeMM( const QgsRenderContext& ct, const QgsGeometry* geom, double minSize ) const
1915 {
1916  return QgsPalLabeling::checkMinimumSizeMM( ct, geom, minSize );
1917 }
1918 
1919 void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString text, double& labelX, double& labelY, QgsFeature* f, QgsRenderContext *context )
1920 {
1921  if ( !fm || !f )
1922  {
1923  return;
1924  }
1925 
1926  //try to keep < 2.12 API - handle no passed render context
1928  if ( !context )
1929  {
1930  scopedRc.reset( new QgsRenderContext() );
1931  if ( f )
1932  scopedRc->expressionContext().setFeature( *f );
1933  }
1934  QgsRenderContext* rc = context ? context : scopedRc.data();
1935 
1936  QString wrapchr = wrapChar;
1937  double multilineH = multilineHeight;
1938 
1939  bool addDirSymb = addDirectionSymbol;
1940  QString leftDirSymb = leftDirectionSymbol;
1941  QString rightDirSymb = rightDirectionSymbol;
1943 
1944  if ( f == mCurFeat ) // called internally, use any stored data defined values
1945  {
1946  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
1947  {
1948  wrapchr = dataDefinedValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
1949  }
1950 
1951  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
1952  {
1953  multilineH = dataDefinedValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
1954  }
1955 
1956  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
1957  {
1958  addDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
1959  }
1960 
1961  if ( addDirSymb )
1962  {
1963 
1964  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
1965  {
1966  leftDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
1967  }
1968  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbRight ) )
1969  {
1970  rightDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
1971  }
1972 
1973  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
1974  {
1975  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( dataDefinedValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt() );
1976  }
1977 
1978  }
1979 
1980  }
1981  else // called externally with passed-in feature, evaluate data defined
1982  {
1985  if ( exprVal.isValid() )
1986  {
1987  wrapchr = exprVal.toString();
1988  }
1989  exprVal.clear();
1990  rc->expressionContext().setOriginalValueVariable( multilineH );
1992  if ( exprVal.isValid() )
1993  {
1994  bool ok;
1995  double size = exprVal.toDouble( &ok );
1996  if ( ok )
1997  {
1998  multilineH = size;
1999  }
2000  }
2001 
2002  exprVal.clear();
2003  rc->expressionContext().setOriginalValueVariable( addDirSymb );
2005  if ( exprVal.isValid() )
2006  {
2007  addDirSymb = exprVal.toBool();
2008  }
2009 
2010  if ( addDirSymb ) // don't do extra evaluations if not adding a direction symbol
2011  {
2012  exprVal.clear();
2013  rc->expressionContext().setOriginalValueVariable( leftDirSymb );
2015  if ( exprVal.isValid() )
2016  {
2017  leftDirSymb = exprVal.toString();
2018  }
2019  exprVal.clear();
2020  rc->expressionContext().setOriginalValueVariable( rightDirSymb );
2022  if ( exprVal.isValid() )
2023  {
2024  rightDirSymb = exprVal.toString();
2025  }
2026  exprVal.clear();
2027  rc->expressionContext().setOriginalValueVariable( static_cast< int >( placeDirSymb ) );
2029  if ( exprVal.isValid() )
2030  {
2031  bool ok;
2032  int enmint = exprVal.toInt( &ok );
2033  if ( ok )
2034  {
2035  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( enmint );
2036  }
2037  }
2038  }
2039 
2040  }
2041 
2042  if ( wrapchr.isEmpty() )
2043  {
2044  wrapchr = QLatin1String( "\n" ); // default to new line delimiter
2045  }
2046 
2047  //consider the space needed for the direction symbol
2048  if ( addDirSymb && placement == QgsPalLayerSettings::Line
2049  && ( !leftDirSymb.isEmpty() || !rightDirSymb.isEmpty() ) )
2050  {
2051  QString dirSym = leftDirSymb;
2052 
2053  if ( fm->width( rightDirSymb ) > fm->width( dirSym ) )
2054  dirSym = rightDirSymb;
2055 
2056  if ( placeDirSymb == QgsPalLayerSettings::SymbolLeftRight )
2057  {
2058  text.append( dirSym );
2059  }
2060  else
2061  {
2062  text.prepend( dirSym + QLatin1String( "\n" ) ); // SymbolAbove or SymbolBelow
2063  }
2064  }
2065 
2066  double w = 0.0, h = 0.0;
2067  QStringList multiLineSplit = QgsPalLabeling::splitToLines( text, wrapchr );
2068  int lines = multiLineSplit.size();
2069 
2070  double labelHeight = fm->ascent() + fm->descent(); // ignore +1 for baseline
2071 
2072  h += fm->height() + static_cast< double >(( lines - 1 ) * labelHeight * multilineH );
2073  h /= rasterCompressFactor;
2074 
2075  for ( int i = 0; i < lines; ++i )
2076  {
2077  double width = fm->width( multiLineSplit.at( i ) );
2078  if ( width > w )
2079  {
2080  w = width;
2081  }
2082  }
2083  w /= rasterCompressFactor;
2084 
2085 #if 0 // XXX strk
2086  QgsPoint ptSize = xform->toMapCoordinatesF( w, h );
2087  labelX = qAbs( ptSize.x() - ptZero.x() );
2088  labelY = qAbs( ptSize.y() - ptZero.y() );
2089 #else
2090  double uPP = xform->mapUnitsPerPixel();
2091  labelX = w * uPP;
2092  labelY = h * uPP;
2093 #endif
2094 }
2095 
2096 void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &context, QgsLabelFeature** labelFeature , QgsGeometry* obstacleGeometry )
2097 {
2098  // either used in QgsPalLabeling (palLayer is set) or in QgsLabelingEngineV2 (labelFeature is set)
2099  Q_ASSERT( labelFeature );
2100 
2101  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2102  mCurFeat = &f;
2103 
2104  // data defined is obstacle? calculate this first, to avoid wasting time working with obstacles we don't require
2105  bool isObstacle = obstacle; // start with layer default
2107  {
2108  isObstacle = exprVal.toBool();
2109  }
2110 
2111  if ( !drawLabels )
2112  {
2113  if ( isObstacle )
2114  {
2115  registerObstacleFeature( f, context, labelFeature, obstacleGeometry );
2116  }
2117  return;
2118  }
2119 
2120 // mCurFields = &layer->pendingFields();
2121 
2122  // store data defined-derived values for later adding to label feature for use during rendering
2123  dataDefinedValues.clear();
2124 
2125  // data defined show label? defaults to show label if not 0
2127  {
2128  bool showLabel = dataDefinedEvaluate( QgsPalLayerSettings::Show, exprVal, &context.expressionContext(), true );
2129  showLabel &= exprVal.toBool();
2130  QgsDebugMsgLevel( QString( "exprVal Show:%1" ).arg( showLabel ? "true" : "false" ), 4 );
2131  if ( !showLabel )
2132  {
2133  return;
2134  }
2135  }
2136 
2137  // data defined scale visibility?
2138  bool useScaleVisibility = scaleVisibility;
2140  {
2141  QgsDebugMsgLevel( QString( "exprVal ScaleVisibility:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
2142  useScaleVisibility = exprVal.toBool();
2143  }
2144 
2145  if ( useScaleVisibility )
2146  {
2147  // data defined min scale?
2148  double minScale = scaleMin;
2150  {
2151  QgsDebugMsgLevel( QString( "exprVal MinScale:%1" ).arg( exprVal.toDouble() ), 4 );
2152  bool conversionOk;
2153  double mins = exprVal.toDouble( &conversionOk );
2154  if ( conversionOk )
2155  {
2156  minScale = mins;
2157  }
2158  }
2159 
2160  // scales closer than 1:1
2161  if ( minScale < 0 )
2162  {
2163  minScale = 1 / qAbs( minScale );
2164  }
2165 
2166  if ( !qgsDoubleNear( minScale, 0.0 ) && context.rendererScale() < minScale )
2167  {
2168  return;
2169  }
2170 
2171  // data defined max scale?
2172  double maxScale = scaleMax;
2174  {
2175  QgsDebugMsgLevel( QString( "exprVal MaxScale:%1" ).arg( exprVal.toDouble() ), 4 );
2176  bool conversionOk;
2177  double maxs = exprVal.toDouble( &conversionOk );
2178  if ( conversionOk )
2179  {
2180  maxScale = maxs;
2181  }
2182  }
2183 
2184  // scales closer than 1:1
2185  if ( maxScale < 0 )
2186  {
2187  maxScale = 1 / qAbs( maxScale );
2188  }
2189 
2190  if ( !qgsDoubleNear( maxScale, 0.0 ) && context.rendererScale() > maxScale )
2191  {
2192  return;
2193  }
2194  }
2195 
2196  QFont labelFont = textFont;
2197  // labelFont will be added to label feature for use during label painting
2198 
2199  // data defined font units?
2202  {
2203  QString units = exprVal.toString().trimmed();
2204  QgsDebugMsgLevel( QString( "exprVal Font units:%1" ).arg( units ), 4 );
2205  if ( !units.isEmpty() )
2206  {
2207  fontunits = _decodeUnits( units );
2208  }
2209  }
2210 
2211  //data defined label size?
2212  double fontSize = labelFont.pointSizeF(); // font size doesn't have its own class data member
2213  if ( dataDefinedEvaluate( QgsPalLayerSettings::Size, exprVal, &context.expressionContext(), fontSize ) )
2214  {
2215  QgsDebugMsgLevel( QString( "exprVal Size:%1" ).arg( exprVal.toDouble() ), 4 );
2216  bool ok;
2217  double size = exprVal.toDouble( &ok );
2218  if ( ok )
2219  {
2220  fontSize = size;
2221  }
2222  }
2223  if ( fontSize <= 0.0 )
2224  {
2225  return;
2226  }
2227 
2228  int fontPixelSize = sizeToPixel( fontSize, context, fontunits, true, fontSizeMapUnitScale );
2229  // don't try to show font sizes less than 1 pixel (Qt complains)
2230  if ( fontPixelSize < 1 )
2231  {
2232  return;
2233  }
2234  labelFont.setPixelSize( fontPixelSize );
2235 
2236  // NOTE: labelFont now always has pixelSize set, so pointSize or pointSizeF might return -1
2237 
2238  // defined 'minimum/maximum pixel font size'?
2239  if ( fontunits == QgsPalLayerSettings::MapUnits )
2240  {
2241  bool useFontLimitPixelSize = fontLimitPixelSize;
2243  {
2244  QgsDebugMsgLevel( QString( "exprVal FontLimitPixel:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
2245  useFontLimitPixelSize = exprVal.toBool();
2246  }
2247 
2248  if ( useFontLimitPixelSize )
2249  {
2250  int fontMinPixel = fontMinPixelSize;
2252  {
2253  bool ok;
2254  int sizeInt = exprVal.toInt( &ok );
2255  QgsDebugMsgLevel( QString( "exprVal FontMinPixel:%1" ).arg( sizeInt ), 4 );
2256  if ( ok )
2257  {
2258  fontMinPixel = sizeInt;
2259  }
2260  }
2261 
2262  int fontMaxPixel = fontMaxPixelSize;
2264  {
2265  bool ok;
2266  int sizeInt = exprVal.toInt( &ok );
2267  QgsDebugMsgLevel( QString( "exprVal FontMaxPixel:%1" ).arg( sizeInt ), 4 );
2268  if ( ok )
2269  {
2270  fontMaxPixel = sizeInt;
2271  }
2272  }
2273 
2274  if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
2275  {
2276  return;
2277  }
2278  }
2279  }
2280 
2281  // NOTE: the following parsing functions calculate and store any data defined values for later use in QgsPalLabeling::drawLabeling
2282  // this is done to provide clarity, and because such parsing is not directly related to PAL feature registration calculations
2283 
2284  // calculate rest of font attributes and store any data defined values
2285  // this is done here for later use in making label backgrounds part of collision management (when implemented)
2286  parseTextStyle( labelFont, fontunits, context );
2287  parseTextFormatting( context );
2288  parseTextBuffer( context );
2289  parseShapeBackground( context );
2290  parseDropShadow( context );
2291 
2292  QString labelText;
2293 
2294  // Check to see if we are a expression string.
2295  if ( isExpression )
2296  {
2298  if ( exp->hasParserError() )
2299  {
2300  QgsDebugMsgLevel( QString( "Expression parser error:%1" ).arg( exp->parserErrorString() ), 4 );
2301  return;
2302  }
2303  exp->setScale( context.rendererScale() );
2304 // QVariant result = exp->evaluate( &f, layer->pendingFields() );
2305  QVariant result = exp->evaluate( &context.expressionContext() ); // expression prepared in QgsPalLabeling::prepareLayer()
2306  if ( exp->hasEvalError() )
2307  {
2308  QgsDebugMsgLevel( QString( "Expression parser eval error:%1" ).arg( exp->evalErrorString() ), 4 );
2309  return;
2310  }
2311  labelText = result.isNull() ? "" : result.toString();
2312  }
2313  else
2314  {
2315  const QVariant &v = f.attribute( fieldIndex );
2316  labelText = v.isNull() ? "" : v.toString();
2317  }
2318 
2319  // data defined format numbers?
2320  bool formatnum = formatNumbers;
2322  {
2323  formatnum = exprVal.toBool();
2324  QgsDebugMsgLevel( QString( "exprVal NumFormat:%1" ).arg( formatnum ? "true" : "false" ), 4 );
2325  }
2326 
2327  // format number if label text is coercible to a number
2328  if ( formatnum )
2329  {
2330  // data defined decimal places?
2331  int decimalPlaces = decimals;
2333  {
2334  bool ok;
2335  int dInt = exprVal.toInt( &ok );
2336  QgsDebugMsgLevel( QString( "exprVal NumDecimals:%1" ).arg( dInt ), 4 );
2337  if ( ok && dInt > 0 ) // needs to be positive
2338  {
2339  decimalPlaces = dInt;
2340  }
2341  }
2342 
2343  // data defined plus sign?
2344  bool signPlus = plusSign;
2346  {
2347  signPlus = exprVal.toBool();
2348  QgsDebugMsgLevel( QString( "exprVal NumPlusSign:%1" ).arg( signPlus ? "true" : "false" ), 4 );
2349  }
2350 
2351  QVariant textV( labelText );
2352  bool ok;
2353  double d = textV.toDouble( &ok );
2354  if ( ok )
2355  {
2356  QString numberFormat;
2357  if ( d > 0 && signPlus )
2358  {
2359  numberFormat.append( '+' );
2360  }
2361  numberFormat.append( "%1" );
2362  labelText = numberFormat.arg( d, 0, 'f', decimalPlaces );
2363  }
2364  }
2365 
2366  // NOTE: this should come AFTER any option that affects font metrics
2367  QScopedPointer<QFontMetricsF> labelFontMetrics( new QFontMetricsF( labelFont ) );
2368  double labelX, labelY; // will receive label size
2369  calculateLabelSize( labelFontMetrics.data(), labelText, labelX, labelY, mCurFeat, &context );
2370 
2371 
2372  // maximum angle between curved label characters (hardcoded defaults used in QGIS <2.0)
2373  //
2374  double maxcharanglein = 20.0; // range 20.0-60.0
2375  double maxcharangleout = -20.0; // range 20.0-95.0
2376 
2378  {
2379  maxcharanglein = maxCurvedCharAngleIn;
2380  maxcharangleout = maxCurvedCharAngleOut;
2381 
2382  //data defined maximum angle between curved label characters?
2384  {
2385  QString ptstr = exprVal.toString().trimmed();
2386  QgsDebugMsgLevel( QString( "exprVal CurvedCharAngleInOut:%1" ).arg( ptstr ), 4 );
2387 
2388  if ( !ptstr.isEmpty() )
2389  {
2390  QPointF maxcharanglePt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
2391  maxcharanglein = qBound( 20.0, static_cast< double >( maxcharanglePt.x() ), 60.0 );
2392  maxcharangleout = qBound( 20.0, static_cast< double >( maxcharanglePt.y() ), 95.0 );
2393  }
2394  }
2395  // make sure maxcharangleout is always negative
2396  maxcharangleout = -( qAbs( maxcharangleout ) );
2397  }
2398 
2399  // data defined centroid whole or clipped?
2400  bool wholeCentroid = centroidWhole;
2402  {
2403  QString str = exprVal.toString().trimmed();
2404  QgsDebugMsgLevel( QString( "exprVal CentroidWhole:%1" ).arg( str ), 4 );
2405 
2406  if ( !str.isEmpty() )
2407  {
2408  if ( str.compare( "Visible", Qt::CaseInsensitive ) == 0 )
2409  {
2410  wholeCentroid = false;
2411  }
2412  else if ( str.compare( "Whole", Qt::CaseInsensitive ) == 0 )
2413  {
2414  wholeCentroid = true;
2415  }
2416  }
2417  }
2418 
2419  const QgsGeometry* geom = f.constGeometry();
2420  if ( !geom )
2421  {
2422  return;
2423  }
2424 
2425  // simplify?
2426  const QgsVectorSimplifyMethod &simplifyMethod = context.vectorSimplifyMethod();
2427  QScopedPointer<QgsGeometry> scopedClonedGeom;
2428  if ( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && simplifyMethod.forceLocalOptimization() )
2429  {
2430  int simplifyHints = simplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
2432  QgsGeometry* g = new QgsGeometry( *geom );
2433 
2434  if ( QgsMapToPixelSimplifier::simplifyGeometry( g, simplifyHints, simplifyMethod.tolerance(), simplifyAlgorithm ) )
2435  {
2436  geom = g;
2437  scopedClonedGeom.reset( g );
2438  }
2439  else
2440  {
2441  delete g;
2442  }
2443  }
2444 
2445  // whether we're going to create a centroid for polygon
2446  bool centroidPoly = (( placement == QgsPalLayerSettings::AroundPoint
2448  && geom->type() == QGis::Polygon );
2449 
2450  // CLIP the geometry if it is bigger than the extent
2451  // don't clip if centroid is requested for whole feature
2452  bool doClip = false;
2453  if ( !centroidPoly || !wholeCentroid )
2454  {
2455  doClip = true;
2456  }
2457 
2458  const GEOSGeometry* geos_geom = nullptr;
2459  const QgsGeometry* preparedGeom = geom;
2460  QScopedPointer<QgsGeometry> scopedPreparedGeom;
2461  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, doClip ? extentGeom : nullptr ) )
2462  {
2463  scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, doClip ? extentGeom : nullptr ) );
2464  if ( !scopedPreparedGeom.data() )
2465  return;
2466  preparedGeom = scopedPreparedGeom.data();
2467  geos_geom = scopedPreparedGeom.data()->asGeos();
2468  }
2469  else
2470  {
2471  geos_geom = geom->asGeos();
2472  }
2473  const GEOSGeometry* geosObstacleGeom = nullptr;
2474  QScopedPointer<QgsGeometry> scopedObstacleGeom;
2475  if ( isObstacle )
2476  {
2477  if ( obstacleGeometry && QgsPalLabeling::geometryRequiresPreparation( obstacleGeometry, context, ct, doClip ? extentGeom : nullptr ) )
2478  {
2479  scopedObstacleGeom.reset( QgsPalLabeling::prepareGeometry( obstacleGeometry, context, ct, doClip ? extentGeom : nullptr ) );
2480  obstacleGeometry = scopedObstacleGeom.data();
2481  }
2482  if ( obstacleGeometry )
2483  {
2484  geosObstacleGeom = obstacleGeometry->asGeos();
2485  }
2486  }
2487 
2488  if ( minFeatureSize > 0 && !checkMinimumSizeMM( context, preparedGeom, minFeatureSize ) )
2489  return;
2490 
2491  if ( !geos_geom )
2492  return; // invalid geometry
2493 
2494  // likelihood exists label will be registered with PAL and may be drawn
2495  // check if max number of features to label (already registered with PAL) has been reached
2496  // Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
2497  if ( limitNumLabels )
2498  {
2499  if ( !maxNumLabels )
2500  {
2501  return;
2502  }
2503  if ( mFeatsRegPal >= maxNumLabels )
2504  {
2505  return;
2506  }
2507 
2508  int divNum = static_cast< int >(( static_cast< double >( mFeaturesToLabel ) / maxNumLabels ) + 0.5 );
2509  if ( divNum && ( mFeatsRegPal == static_cast< int >( mFeatsSendingToPal / divNum ) ) )
2510  {
2511  mFeatsSendingToPal += 1;
2512  if ( divNum && mFeatsSendingToPal % divNum )
2513  {
2514  return;
2515  }
2516  }
2517  }
2518 
2519  GEOSGeometry* geos_geom_clone;
2520  if ( GEOSGeomTypeId_r( QgsGeometry::getGEOSHandler(), geos_geom ) == GEOS_POLYGON && repeatDistance > 0 && placement == Line )
2521  {
2522  geos_geom_clone = GEOSBoundary_r( QgsGeometry::getGEOSHandler(), geos_geom );
2523  }
2524  else
2525  {
2526  geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
2527  }
2528  GEOSGeometry* geosObstacleGeomClone = nullptr;
2529  if ( geosObstacleGeom )
2530  {
2531  geosObstacleGeomClone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geosObstacleGeom );
2532  }
2533 
2534 
2535  //data defined position / alignment / rotation?
2536  bool dataDefinedPosition = false;
2537  bool layerDefinedRotation = false;
2538  bool dataDefinedRotation = false;
2539  double xPos = 0.0, yPos = 0.0, angle = 0.0;
2540  bool ddXPos = false, ddYPos = false;
2541  double quadOffsetX = 0.0, quadOffsetY = 0.0;
2542  double offsetX = 0.0, offsetY = 0.0;
2543 
2544  //data defined quadrant offset?
2545  bool ddFixedQuad = false;
2546  QuadrantPosition quadOff = quadOffset;
2547  if ( dataDefinedEvaluate( QgsPalLayerSettings::OffsetQuad, exprVal, &context.expressionContext(), static_cast< int >( quadOff ) ) )
2548  {
2549  bool ok;
2550  int quadInt = exprVal.toInt( &ok );
2551  QgsDebugMsgLevel( QString( "exprVal OffsetQuad:%1" ).arg( quadInt ), 4 );
2552  if ( ok && 0 <= quadInt && quadInt <= 8 )
2553  {
2554  quadOff = static_cast< QuadrantPosition >( quadInt );
2555  ddFixedQuad = true;
2556  }
2557  }
2558 
2559  // adjust quadrant offset of labels
2560  switch ( quadOff )
2561  {
2562  case QuadrantAboveLeft:
2563  quadOffsetX = -1.0;
2564  quadOffsetY = 1.0;
2565  break;
2566  case QuadrantAbove:
2567  quadOffsetX = 0.0;
2568  quadOffsetY = 1.0;
2569  break;
2570  case QuadrantAboveRight:
2571  quadOffsetX = 1.0;
2572  quadOffsetY = 1.0;
2573  break;
2574  case QuadrantLeft:
2575  quadOffsetX = -1.0;
2576  quadOffsetY = 0.0;
2577  break;
2578  case QuadrantRight:
2579  quadOffsetX = 1.0;
2580  quadOffsetY = 0.0;
2581  break;
2582  case QuadrantBelowLeft:
2583  quadOffsetX = -1.0;
2584  quadOffsetY = -1.0;
2585  break;
2586  case QuadrantBelow:
2587  quadOffsetX = 0.0;
2588  quadOffsetY = -1.0;
2589  break;
2590  case QuadrantBelowRight:
2591  quadOffsetX = 1.0;
2592  quadOffsetY = -1.0;
2593  break;
2594  case QuadrantOver:
2595  default:
2596  break;
2597  }
2598 
2599  //data defined label offset?
2600  double xOff = xOffset;
2601  double yOff = yOffset;
2603  {
2604  QString ptstr = exprVal.toString().trimmed();
2605  QgsDebugMsgLevel( QString( "exprVal OffsetXY:%1" ).arg( ptstr ), 4 );
2606 
2607  if ( !ptstr.isEmpty() )
2608  {
2609  QPointF ddOffPt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
2610  xOff = ddOffPt.x();
2611  yOff = ddOffPt.y();
2612  }
2613  }
2614 
2615  // data defined label offset units?
2616  bool offinmapunits = labelOffsetInMapUnits;
2618  {
2619  QString units = exprVal.toString().trimmed();
2620  QgsDebugMsgLevel( QString( "exprVal OffsetUnits:%1" ).arg( units ), 4 );
2621  if ( !units.isEmpty() )
2622  {
2623  offinmapunits = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2624  }
2625  }
2626 
2627  // adjust offset of labels to match chosen unit and map scale
2628  // offsets match those of symbology: -x = left, -y = up
2629  double mapUntsPerMM = labelOffsetMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor();
2630  if ( !qgsDoubleNear( xOff, 0.0 ) )
2631  {
2632  offsetX = xOff; // must be positive to match symbology offset direction
2633  if ( !offinmapunits )
2634  {
2635  offsetX *= mapUntsPerMM; //convert offset from mm to map units
2636  }
2637  }
2638  if ( !qgsDoubleNear( yOff, 0.0 ) )
2639  {
2640  offsetY = -yOff; // must be negative to match symbology offset direction
2641  if ( !offinmapunits )
2642  {
2643  offsetY *= mapUntsPerMM; //convert offset from mm to map units
2644  }
2645  }
2646 
2647  // layer defined rotation?
2648  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2650  {
2651  layerDefinedRotation = true;
2652  angle = angleOffset * M_PI / 180; // convert to radians
2653  }
2654 
2655  const QgsMapToPixel& m2p = context.mapToPixel();
2656  //data defined rotation?
2658  {
2659  bool ok;
2660  double rotD = exprVal.toDouble( &ok );
2661  QgsDebugMsgLevel( QString( "exprVal Rotation:%1" ).arg( rotD ), 4 );
2662  if ( ok )
2663  {
2664  dataDefinedRotation = true;
2665  // TODO: add setting to disable having data defined rotation follow
2666  // map rotation ?
2667  rotD -= m2p.mapRotation();
2668  angle = rotD * M_PI / 180.0;
2669  }
2670  }
2671 
2673  {
2674  if ( !exprVal.isNull() )
2675  xPos = exprVal.toDouble( &ddXPos );
2676  QgsDebugMsgLevel( QString( "exprVal PositionX:%1" ).arg( xPos ), 4 );
2677 
2679  {
2680  //data defined position. But field values could be NULL -> positions will be generated by PAL
2681  if ( !exprVal.isNull() )
2682  yPos = exprVal.toDouble( &ddYPos );
2683  QgsDebugMsgLevel( QString( "exprVal PositionY:%1" ).arg( yPos ), 4 );
2684 
2685  if ( ddXPos && ddYPos )
2686  {
2687  dataDefinedPosition = true;
2688  // layer rotation set, but don't rotate pinned labels unless data defined
2689  if ( layerDefinedRotation && !dataDefinedRotation )
2690  {
2691  angle = 0.0;
2692  }
2693 
2694  //x/y shift in case of alignment
2695  double xdiff = 0.0;
2696  double ydiff = 0.0;
2697 
2698  //horizontal alignment
2699  if ( dataDefinedEvaluate( QgsPalLayerSettings::Hali, exprVal, &context.expressionContext() ) )
2700  {
2701  QString haliString = exprVal.toString();
2702  QgsDebugMsgLevel( QString( "exprVal Hali:%1" ).arg( haliString ), 4 );
2703  if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
2704  {
2705  xdiff -= labelX / 2.0;
2706  }
2707  else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
2708  {
2709  xdiff -= labelX;
2710  }
2711  }
2712 
2713  //vertical alignment
2714  if ( dataDefinedEvaluate( QgsPalLayerSettings::Vali, exprVal, &context.expressionContext() ) )
2715  {
2716  QString valiString = exprVal.toString();
2717  QgsDebugMsgLevel( QString( "exprVal Vali:%1" ).arg( valiString ), 4 );
2718 
2719  if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
2720  {
2721  if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 )
2722  {
2723  ydiff -= labelY;
2724  }
2725  else
2726  {
2727  double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
2728  if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
2729  {
2730  ydiff -= labelY * descentRatio;
2731  }
2732  else //'Cap' or 'Half'
2733  {
2734  double capHeightRatio = ( labelFontMetrics->boundingRect( 'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
2735  ydiff -= labelY * capHeightRatio;
2736  if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
2737  {
2738  ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
2739  }
2740  }
2741  }
2742  }
2743  }
2744 
2745  if ( dataDefinedRotation )
2746  {
2747  //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
2748  double xd = xdiff * cos( angle ) - ydiff * sin( angle );
2749  double yd = xdiff * sin( angle ) + ydiff * cos( angle );
2750  xdiff = xd;
2751  ydiff = yd;
2752  }
2753 
2754  //project xPos and yPos from layer to map CRS
2755  double z = 0;
2756  if ( ct )
2757  {
2758  try
2759  {
2760  ct->transformInPlace( xPos, yPos, z );
2761  }
2762  catch ( QgsCsException &e )
2763  {
2764  Q_UNUSED( e );
2765  QgsDebugMsgLevel( QString( "Ignoring feature %1 due transformation exception on data-defined position" ).arg( f.id() ), 4 );
2766  return;
2767  }
2768  }
2769 
2770  //rotate position with map if data-defined
2771  if ( dataDefinedPosition && m2p.mapRotation() )
2772  {
2773  const QgsPoint& center = context.extent().center();
2774  QTransform t = QTransform::fromTranslate( center.x(), center.y() );
2775  t.rotate( -m2p.mapRotation() );
2776  t.translate( -center.x(), -center.y() );
2777  qreal xPosR, yPosR;
2778  qreal xPos_qreal = xPos, yPos_qreal = yPos;
2779  t.map( xPos_qreal, yPos_qreal, &xPosR, &yPosR );
2780  xPos = xPosR;
2781  yPos = yPosR;
2782 
2783  }
2784 
2785  xPos += xdiff;
2786  yPos += ydiff;
2787  }
2788  else
2789  {
2790  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2791  if ( dataDefinedRotation && placement != QgsPalLayerSettings::OverPoint )
2792  {
2793  angle = 0.0;
2794  }
2795  }
2796  }
2797  }
2798 
2799  // data defined always show?
2800  bool alwaysShow = false;
2802  {
2803  alwaysShow = exprVal.toBool();
2804  }
2805 
2806  // set repeat distance
2807  // data defined repeat distance?
2808  double repeatDist = repeatDistance;
2810  {
2811  bool ok;
2812  double distD = exprVal.toDouble( &ok );
2813  if ( ok )
2814  {
2815  repeatDist = distD;
2816  }
2817  }
2818 
2819  // data defined label-repeat distance units?
2820  bool repeatdistinmapunit = repeatDistanceUnit == QgsPalLayerSettings::MapUnits;
2822  {
2823  QString units = exprVal.toString().trimmed();
2824  QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 );
2825  if ( !units.isEmpty() )
2826  {
2827  repeatdistinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2828  }
2829  }
2830 
2831  if ( !qgsDoubleNear( repeatDist, 0.0 ) )
2832  {
2833  if ( !repeatdistinmapunit )
2834  {
2835  repeatDist *= mapUntsPerMM; //convert repeat distance from mm to map units
2836  }
2837  }
2838 
2839  // feature to the layer
2840  QgsTextLabelFeature* lf = new QgsTextLabelFeature( f.id(), geos_geom_clone, QSizeF( labelX, labelY ) );
2841  mFeatsRegPal++;
2842 
2843  *labelFeature = lf;
2844  ( *labelFeature )->setHasFixedPosition( dataDefinedPosition );
2845  ( *labelFeature )->setFixedPosition( QgsPoint( xPos, yPos ) );
2846  // use layer-level defined rotation, but not if position fixed
2847  ( *labelFeature )->setHasFixedAngle( dataDefinedRotation || ( !dataDefinedPosition && !qgsDoubleNear( angle, 0.0 ) ) );
2848  ( *labelFeature )->setFixedAngle( angle );
2849  ( *labelFeature )->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
2850  ( *labelFeature )->setPositionOffset( QgsPoint( offsetX, offsetY ) );
2851  ( *labelFeature )->setOffsetType( offsetType );
2852  ( *labelFeature )->setAlwaysShow( alwaysShow );
2853  ( *labelFeature )->setRepeatDistance( repeatDist );
2854  ( *labelFeature )->setLabelText( labelText );
2855  if ( geosObstacleGeomClone )
2856  {
2857  ( *labelFeature )->setObstacleGeometry( geosObstacleGeomClone );
2858 
2859  if ( geom->type() == QGis::Point )
2860  {
2861  //register symbol size
2862  ( *labelFeature )->setSymbolSize( QSizeF( obstacleGeometry->boundingBox().width(),
2863  obstacleGeometry->boundingBox().height() ) );
2864  }
2865  }
2866 
2867  //set label's visual margin so that top visual margin is the leading, and bottom margin is the font's descent
2868  //this makes labels align to the font's baseline or highest character
2869  double topMargin = qMax( 0.25 * labelFontMetrics->ascent(), 0.0 );
2870  double bottomMargin = 1.0 + labelFontMetrics->descent();
2871  QgsLabelFeature::VisualMargin vm( topMargin, 0.0, bottomMargin, 0.0 );
2873  ( *labelFeature )->setVisualMargin( vm );
2874 
2875  // store the label's calculated font for later use during painting
2876  QgsDebugMsgLevel( QString( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString(), labelFont.styleName() ), 4 );
2877  lf->setDefinedFont( labelFont );
2878 
2879  // TODO: only for placement which needs character info
2880  // account for any data defined font metrics adjustments
2881  lf->calculateInfo( placement == QgsPalLayerSettings::Curved, labelFontMetrics.data(), xform, rasterCompressFactor, maxcharanglein, maxcharangleout );
2882  // for labelFeature the LabelInfo is passed to feat when it is registered
2883 
2884  // TODO: allow layer-wide feature dist in PAL...?
2885 
2886  // data defined label-feature distance?
2887  double distance = dist;
2889  {
2890  bool ok;
2891  double distD = exprVal.toDouble( &ok );
2892  if ( ok )
2893  {
2894  distance = distD;
2895  }
2896  }
2897 
2898  // data defined label-feature distance units?
2899  bool distinmapunit = distInMapUnits;
2901  {
2902  QString units = exprVal.toString().trimmed();
2903  QgsDebugMsgLevel( QString( "exprVal DistanceUnits:%1" ).arg( units ), 4 );
2904  if ( !units.isEmpty() )
2905  {
2906  distinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2907  }
2908  }
2909 
2910  if ( !qgsDoubleNear( distance, 0.0 ) )
2911  {
2912  if ( distinmapunit ) //convert distance from mm/map units to pixels
2913  {
2914  distance /= distMapUnitScale.computeMapUnitsPerPixel( context );
2915  }
2916  else //mm
2917  {
2918  distance *= vectorScaleFactor;
2919  }
2920  double d = ptOne.distance( ptZero ) * distance;
2921  ( *labelFeature )->setDistLabel( d );
2922  }
2923 
2924  if ( ddFixedQuad )
2925  {
2926  ( *labelFeature )->setHasFixedQuadrant( true );
2927  }
2928 
2929  // data defined z-index?
2930  double z = zIndex;
2932  {
2933  bool ok;
2934  double zIndexD = exprVal.toDouble( &ok );
2935  if ( ok )
2936  {
2937  z = zIndexD;
2938  }
2939  }
2940  ( *labelFeature )->setZIndex( z );
2941 
2942  // data defined priority?
2944  {
2945  bool ok;
2946  double priorityD = exprVal.toDouble( &ok );
2947  if ( ok )
2948  {
2949  priorityD = qBound( 0.0, priorityD, 10.0 );
2950  priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
2951  ( *labelFeature )->setPriority( priorityD );
2952  }
2953  }
2954 
2955  ( *labelFeature )->setIsObstacle( isObstacle );
2956 
2957  double featObstacleFactor = obstacleFactor;
2959  {
2960  bool ok;
2961  double factorD = exprVal.toDouble( &ok );
2962  if ( ok )
2963  {
2964  factorD = qBound( 0.0, factorD, 10.0 );
2965  factorD = factorD / 5.0 + 0.0001; // convert 0 -> 10 to 0.0001 -> 2.0
2966  featObstacleFactor = factorD;
2967  }
2968  }
2969  ( *labelFeature )->setObstacleFactor( featObstacleFactor );
2970 
2972  if ( positionOrder.isEmpty() )
2973  positionOrder = QgsPalLayerSettings::DEFAULT_PLACEMENT_ORDER;
2974 
2976  {
2977  QString orderD = exprVal.toString();
2978  positionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( orderD );
2979  }
2980  ( *labelFeature )->setPredefinedPositionOrder( positionOrder );
2981 
2982  // add parameters for data defined labeling to label feature
2983  lf->setDataDefinedValues( dataDefinedValues );
2984 }
2985 
2986 void QgsPalLayerSettings::registerObstacleFeature( QgsFeature& f, QgsRenderContext &context, QgsLabelFeature** obstacleFeature, QgsGeometry* obstacleGeometry )
2987 {
2988  mCurFeat = &f;
2989 
2990  const QgsGeometry* geom = nullptr;
2991  if ( obstacleGeometry )
2992  {
2993  geom = obstacleGeometry;
2994  }
2995  else
2996  {
2997  geom = f.constGeometry();
2998  }
2999 
3000  if ( !geom )
3001  {
3002  return;
3003  }
3004 
3005  // simplify?
3006  const QgsVectorSimplifyMethod &simplifyMethod = context.vectorSimplifyMethod();
3007  QScopedPointer<QgsGeometry> scopedClonedGeom;
3008  if ( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && simplifyMethod.forceLocalOptimization() )
3009  {
3010  int simplifyHints = simplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
3012  QgsGeometry* g = new QgsGeometry( *geom );
3013 
3014  if ( QgsMapToPixelSimplifier::simplifyGeometry( g, simplifyHints, simplifyMethod.tolerance(), simplifyAlgorithm ) )
3015  {
3016  geom = g;
3017  scopedClonedGeom.reset( g );
3018  }
3019  else
3020  {
3021  delete g;
3022  }
3023  }
3024 
3025  const GEOSGeometry* geos_geom = nullptr;
3026  QScopedPointer<QgsGeometry> scopedPreparedGeom;
3027 
3028  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, extentGeom ) )
3029  {
3030  scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, extentGeom ) );
3031  if ( !scopedPreparedGeom.data() )
3032  return;
3033  geos_geom = scopedPreparedGeom.data()->asGeos();
3034  }
3035  else
3036  {
3037  geos_geom = geom->asGeos();
3038  }
3039 
3040  if ( !geos_geom )
3041  return; // invalid geometry
3042 
3043  GEOSGeometry* geos_geom_clone;
3044  geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
3045 
3046  // feature to the layer
3047  *obstacleFeature = new QgsLabelFeature( f.id(), geos_geom_clone, QSizeF( 0, 0 ) );
3048  ( *obstacleFeature )->setIsObstacle( true );
3049  mFeatsRegPal++;
3050 }
3051 
3052 bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
3054  QVariant& exprVal, QgsExpressionContext& context, const QVariant& originalValue )
3055 {
3056  if ( dataDefinedEvaluate( p, exprVal, &context, originalValue ) )
3057  {
3058 #ifdef QGISDEBUG
3059  QString dbgStr = QString( "exprVal %1:" ).arg( mDataDefinedNames.value( p ).first ) + "%1"; // clazy:exclude=unused-non-trivial-variable
3060 #endif
3061 
3062  switch ( valType )
3063  {
3064  case DDBool:
3065  {
3066  bool bol = exprVal.toBool();
3067  QgsDebugMsgLevel( dbgStr.arg( bol ? "true" : "false" ), 4 );
3068  dataDefinedValues.insert( p, QVariant( bol ) );
3069  return true;
3070  }
3071  case DDInt:
3072  {
3073  bool ok;
3074  int size = exprVal.toInt( &ok );
3075  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3076 
3077  if ( ok )
3078  {
3079  dataDefinedValues.insert( p, QVariant( size ) );
3080  return true;
3081  }
3082  return false;
3083  }
3084  case DDIntPos:
3085  {
3086  bool ok;
3087  int size = exprVal.toInt( &ok );
3088  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3089 
3090  if ( ok && size > 0 )
3091  {
3092  dataDefinedValues.insert( p, QVariant( size ) );
3093  return true;
3094  }
3095  return false;
3096  }
3097  case DDDouble:
3098  {
3099  bool ok;
3100  double size = exprVal.toDouble( &ok );
3101  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3102 
3103  if ( ok )
3104  {
3105  dataDefinedValues.insert( p, QVariant( size ) );
3106  return true;
3107  }
3108  return false;
3109  }
3110  case DDDoublePos:
3111  {
3112  bool ok;
3113  double size = exprVal.toDouble( &ok );
3114  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3115 
3116  if ( ok && size > 0.0 )
3117  {
3118  dataDefinedValues.insert( p, QVariant( size ) );
3119  return true;
3120  }
3121  return false;
3122  }
3123  case DDRotation180:
3124  {
3125  bool ok;
3126  double rot = exprVal.toDouble( &ok );
3127  QgsDebugMsgLevel( dbgStr.arg( rot ), 4 );
3128  if ( ok )
3129  {
3130  if ( rot < -180.0 && rot >= -360 )
3131  {
3132  rot += 360;
3133  }
3134  if ( rot > 180.0 && rot <= 360 )
3135  {
3136  rot -= 360;
3137  }
3138  if ( rot >= -180 && rot <= 180 )
3139  {
3140  dataDefinedValues.insert( p, QVariant( rot ) );
3141  return true;
3142  }
3143  }
3144  return false;
3145  }
3146  case DDTransparency:
3147  {
3148  bool ok;
3149  int size = exprVal.toInt( &ok );
3150  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3151  if ( ok && size >= 0 && size <= 100 )
3152  {
3153  dataDefinedValues.insert( p, QVariant( size ) );
3154  return true;
3155  }
3156  return false;
3157  }
3158  case DDString:
3159  {
3160  QString str = exprVal.toString(); // don't trim whitespace
3161  QgsDebugMsgLevel( dbgStr.arg( str ), 4 );
3162 
3163  dataDefinedValues.insert( p, QVariant( str ) ); // let it stay empty if it is
3164  return true;
3165  }
3166  case DDUnits:
3167  {
3168  QString unitstr = exprVal.toString().trimmed();
3169  QgsDebugMsgLevel( dbgStr.arg( unitstr ), 4 );
3170 
3171  if ( !unitstr.isEmpty() )
3172  {
3173  dataDefinedValues.insert( p, QVariant( static_cast< int >( _decodeUnits( unitstr ) ) ) );
3174  return true;
3175  }
3176  return false;
3177  }
3178  case DDColor:
3179  {
3180  QString colorstr = exprVal.toString().trimmed();
3181  QgsDebugMsgLevel( dbgStr.arg( colorstr ), 4 );
3182  QColor color = QgsSymbolLayerV2Utils::decodeColor( colorstr );
3183 
3184  if ( color.isValid() )
3185  {
3186  dataDefinedValues.insert( p, QVariant( color ) );
3187  return true;
3188  }
3189  return false;
3190  }
3191  case DDJoinStyle:
3192  {
3193  QString joinstr = exprVal.toString().trimmed();
3194  QgsDebugMsgLevel( dbgStr.arg( joinstr ), 4 );
3195 
3196  if ( !joinstr.isEmpty() )
3197  {
3198  dataDefinedValues.insert( p, QVariant( static_cast< int >( _decodePenJoinStyle( joinstr ) ) ) );
3199  return true;
3200  }
3201  return false;
3202  }
3203  case DDBlendMode:
3204  {
3205  QString blendstr = exprVal.toString().trimmed();
3206  QgsDebugMsgLevel( dbgStr.arg( blendstr ), 4 );
3207 
3208  if ( !blendstr.isEmpty() )
3209  {
3210  dataDefinedValues.insert( p, QVariant( static_cast< int >( QgsSymbolLayerV2Utils::decodeBlendMode( blendstr ) ) ) );
3211  return true;
3212  }
3213  return false;
3214  }
3215  case DDPointF:
3216  {
3217  QString ptstr = exprVal.toString().trimmed();
3218  QgsDebugMsgLevel( dbgStr.arg( ptstr ), 4 );
3219 
3220  if ( !ptstr.isEmpty() )
3221  {
3222  dataDefinedValues.insert( p, QVariant( QgsSymbolLayerV2Utils::decodePoint( ptstr ) ) );
3223  return true;
3224  }
3225  return false;
3226  }
3227  }
3228  }
3229  return false;
3230 }
3231 
3232 void QgsPalLayerSettings::parseTextStyle( QFont& labelFont,
3234  QgsRenderContext &context )
3235 {
3236  // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
3237 
3238  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3239 
3240  // Two ways to generate new data defined font:
3241  // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
3242  // 2) Family + named style (bold or italic is ignored)
3243 
3244  // data defined font family?
3245  QString ddFontFamily( "" );
3246  if ( dataDefinedEvaluate( QgsPalLayerSettings::Family, exprVal, &context.expressionContext(), labelFont.family() ) )
3247  {
3248  QString family = exprVal.toString().trimmed();
3249  QgsDebugMsgLevel( QString( "exprVal Font family:%1" ).arg( family ), 4 );
3250 
3251  if ( labelFont.family() != family )
3252  {
3253  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
3254  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
3255  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
3256  {
3257  ddFontFamily = family;
3258  }
3259  }
3260  }
3261 
3262  // data defined named font style?
3263  QString ddFontStyle( "" );
3265  {
3266  QString fontstyle = exprVal.toString().trimmed();
3267  QgsDebugMsgLevel( QString( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
3268  ddFontStyle = fontstyle;
3269  }
3270 
3271  // data defined bold font style?
3272  bool ddBold = false;
3273  if ( dataDefinedEvaluate( QgsPalLayerSettings::Bold, exprVal, &context.expressionContext(), labelFont.bold() ) )
3274  {
3275  bool bold = exprVal.toBool();
3276  QgsDebugMsgLevel( QString( "exprVal Font bold:%1" ).arg( bold ? "true" : "false" ), 4 );
3277  ddBold = bold;
3278  }
3279 
3280  // data defined italic font style?
3281  bool ddItalic = false;
3282  if ( dataDefinedEvaluate( QgsPalLayerSettings::Italic, exprVal, &context.expressionContext(), labelFont.italic() ) )
3283  {
3284  bool italic = exprVal.toBool();
3285  QgsDebugMsgLevel( QString( "exprVal Font italic:%1" ).arg( italic ? "true" : "false" ), 4 );
3286  ddItalic = italic;
3287  }
3288 
3289  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
3290  // (currently defaults to what has been read in from layer settings)
3291  QFont newFont;
3292  QFont appFont = QApplication::font();
3293  bool newFontBuilt = false;
3294  if ( ddBold || ddItalic )
3295  {
3296  // new font needs built, since existing style needs removed
3297  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
3298  newFontBuilt = true;
3299  newFont.setBold( ddBold );
3300  newFont.setItalic( ddItalic );
3301  }
3302  else if ( !ddFontStyle.isEmpty()
3303  && ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
3304  {
3305  if ( !ddFontFamily.isEmpty() )
3306  {
3307  // both family and style are different, build font from database
3308  QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
3309  if ( appFont != styledfont )
3310  {
3311  newFont = styledfont;
3312  newFontBuilt = true;
3313  }
3314  }
3315 
3316  // update the font face style
3317  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
3318  }
3319  else if ( !ddFontFamily.isEmpty() )
3320  {
3321  if ( ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
3322  {
3323  // just family is different, build font from database
3324  QFont styledfont = mFontDB.font( ddFontFamily, textNamedStyle, appFont.pointSize() );
3325  if ( appFont != styledfont )
3326  {
3327  newFont = styledfont;
3328  newFontBuilt = true;
3329  }
3330  }
3331  else
3332  {
3333  newFont = QFont( ddFontFamily );
3334  newFontBuilt = true;
3335  }
3336  }
3337 
3338  if ( newFontBuilt )
3339  {
3340  // copy over existing font settings
3341  //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
3342  newFont.setPixelSize( labelFont.pixelSize() );
3343  newFont.setCapitalization( labelFont.capitalization() );
3344  newFont.setUnderline( labelFont.underline() );
3345  newFont.setStrikeOut( labelFont.strikeOut() );
3346  newFont.setWordSpacing( labelFont.wordSpacing() );
3347  newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
3348 
3349  labelFont = newFont;
3350  }
3351 
3352  // data defined word spacing?
3353  double wordspace = labelFont.wordSpacing();
3354  if ( dataDefinedEvaluate( QgsPalLayerSettings::FontWordSpacing, exprVal, &context.expressionContext(), wordspace ) )
3355  {
3356  bool ok;
3357  double wspacing = exprVal.toDouble( &ok );
3358  QgsDebugMsgLevel( QString( "exprVal FontWordSpacing:%1" ).arg( wspacing ), 4 );
3359  if ( ok )
3360  {
3361  wordspace = wspacing;
3362  }
3363  }
3364  labelFont.setWordSpacing( sizeToPixel( wordspace, context, fontunits, false, fontSizeMapUnitScale ) );
3365 
3366  // data defined letter spacing?
3367  double letterspace = labelFont.letterSpacing();
3368  if ( dataDefinedEvaluate( QgsPalLayerSettings::FontLetterSpacing, exprVal, &context.expressionContext(), letterspace ) )
3369  {
3370  bool ok;
3371  double lspacing = exprVal.toDouble( &ok );
3372  QgsDebugMsgLevel( QString( "exprVal FontLetterSpacing:%1" ).arg( lspacing ), 4 );
3373  if ( ok )
3374  {
3375  letterspace = lspacing;
3376  }
3377  }
3378  labelFont.setLetterSpacing( QFont::AbsoluteSpacing, sizeToPixel( letterspace, context, fontunits, false, fontSizeMapUnitScale ) );
3379 
3380  // data defined font capitalization?
3381  QFont::Capitalization fontcaps = labelFont.capitalization();
3383  {
3384  QString fcase = exprVal.toString().trimmed();
3385  QgsDebugMsgLevel( QString( "exprVal FontCase:%1" ).arg( fcase ), 4 );
3386 
3387  if ( !fcase.isEmpty() )
3388  {
3389  if ( fcase.compare( "NoChange", Qt::CaseInsensitive ) == 0 )
3390  {
3391  fontcaps = QFont::MixedCase;
3392  }
3393  else if ( fcase.compare( "Upper", Qt::CaseInsensitive ) == 0 )
3394  {
3395  fontcaps = QFont::AllUppercase;
3396  }
3397  else if ( fcase.compare( "Lower", Qt::CaseInsensitive ) == 0 )
3398  {
3399  fontcaps = QFont::AllLowercase;
3400  }
3401  else if ( fcase.compare( "Capitalize", Qt::CaseInsensitive ) == 0 )
3402  {
3403  fontcaps = QFont::Capitalize;
3404  }
3405 
3406  if ( fontcaps != labelFont.capitalization() )
3407  {
3408  labelFont.setCapitalization( fontcaps );
3409  }
3410  }
3411  }
3412 
3413  // data defined strikeout font style?
3414  if ( dataDefinedEvaluate( QgsPalLayerSettings::Strikeout, exprVal, &context.expressionContext(), labelFont.strikeOut() ) )
3415  {
3416  bool strikeout = exprVal.toBool();
3417  QgsDebugMsgLevel( QString( "exprVal Font strikeout:%1" ).arg( strikeout ? "true" : "false" ), 4 );
3418  labelFont.setStrikeOut( strikeout );
3419  }
3420 
3421  // data defined underline font style?
3422  if ( dataDefinedEvaluate( QgsPalLayerSettings::Underline, exprVal, &context.expressionContext(), labelFont.underline() ) )
3423  {
3424  bool underline = exprVal.toBool();
3425  QgsDebugMsgLevel( QString( "exprVal Font underline:%1" ).arg( underline ? "true" : "false" ), 4 );
3426  labelFont.setUnderline( underline );
3427  }
3428 
3429  // pass the rest on to QgsPalLabeling::drawLabeling
3430 
3431  // data defined font color?
3432  dataDefinedValEval( DDColor, QgsPalLayerSettings::Color, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( textColor ) );
3433 
3434  // data defined font transparency?
3435  dataDefinedValEval( DDTransparency, QgsPalLayerSettings::FontTransp, exprVal, context.expressionContext(), textTransp );
3436 
3437  // data defined font blend mode?
3438  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::FontBlendMode, exprVal, context.expressionContext() );
3439 
3440 }
3441 
3442 void QgsPalLayerSettings::parseTextBuffer( QgsRenderContext &context )
3443 {
3444  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3445 
3446  // data defined draw buffer?
3447  bool drawBuffer = bufferDraw;
3448  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::BufferDraw, exprVal, context.expressionContext(), bufferDraw ) )
3449  {
3450  drawBuffer = exprVal.toBool();
3451  }
3452 
3453  if ( !drawBuffer )
3454  {
3455  return;
3456  }
3457 
3458  // data defined buffer size?
3459  double bufrSize = bufferSize;
3460  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::BufferSize, exprVal, context.expressionContext(), bufferSize ) )
3461  {
3462  bufrSize = exprVal.toDouble();
3463  }
3464 
3465  // data defined buffer transparency?
3466  int bufTransp = bufferTransp;
3467  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::BufferTransp, exprVal, context.expressionContext(), bufferTransp ) )
3468  {
3469  bufTransp = exprVal.toInt();
3470  }
3471 
3472  drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufTransp < 100 );
3473 
3474  if ( !drawBuffer )
3475  {
3476  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
3477  dataDefinedValues.remove( QgsPalLayerSettings::BufferSize );
3478  dataDefinedValues.remove( QgsPalLayerSettings::BufferTransp );
3479  return; // don't bother evaluating values that won't be used
3480  }
3481 
3482  // data defined buffer units?
3483  dataDefinedValEval( DDUnits, QgsPalLayerSettings::BufferUnit, exprVal, context.expressionContext() );
3484 
3485  // data defined buffer color?
3486  dataDefinedValEval( DDColor, QgsPalLayerSettings::BufferColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( bufferColor ) );
3487 
3488  // data defined buffer pen join style?
3490 
3491  // data defined buffer blend mode?
3492  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::BufferBlendMode, exprVal, context.expressionContext() );
3493 }
3494 
3495 void QgsPalLayerSettings::parseTextFormatting( QgsRenderContext &context )
3496 {
3497  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3498 
3499  // data defined multiline wrap character?
3500  QString wrapchr = wrapChar;
3501  if ( dataDefinedValEval( DDString, QgsPalLayerSettings::MultiLineWrapChar, exprVal, context.expressionContext(), wrapChar ) )
3502  {
3503  wrapchr = exprVal.toString();
3504  }
3505 
3506  // data defined multiline height?
3507  dataDefinedValEval( DDDouble, QgsPalLayerSettings::MultiLineHeight, exprVal, context.expressionContext() );
3508 
3509  // data defined multiline text align?
3511  {
3512  QString str = exprVal.toString().trimmed();
3513  QgsDebugMsgLevel( QString( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
3514 
3515  if ( !str.isEmpty() )
3516  {
3517  // "Left"
3519 
3520  if ( str.compare( "Center", Qt::CaseInsensitive ) == 0 )
3521  {
3523  }
3524  else if ( str.compare( "Right", Qt::CaseInsensitive ) == 0 )
3525  {
3526  aligntype = QgsPalLayerSettings::MultiRight;
3527  }
3528  else if ( str.compare( "Follow", Qt::CaseInsensitive ) == 0 )
3529  {
3531  }
3532  dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant( static_cast< int >( aligntype ) ) );
3533  }
3534  }
3535 
3536  // data defined direction symbol?
3537  bool drawDirSymb = addDirectionSymbol;
3538  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbDraw, exprVal, context.expressionContext(), addDirectionSymbol ) )
3539  {
3540  drawDirSymb = exprVal.toBool();
3541  }
3542 
3543  if ( drawDirSymb )
3544  {
3545  // data defined direction left symbol?
3546  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbLeft, exprVal, context.expressionContext(), leftDirectionSymbol );
3547 
3548  // data defined direction right symbol?
3549  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbRight, exprVal, context.expressionContext(), rightDirectionSymbol );
3550 
3551  // data defined direction symbol placement?
3553  {
3554  QString str = exprVal.toString().trimmed();
3555  QgsDebugMsgLevel( QString( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
3556 
3557  if ( !str.isEmpty() )
3558  {
3559  // "LeftRight"
3561 
3562  if ( str.compare( "Above", Qt::CaseInsensitive ) == 0 )
3563  {
3565  }
3566  else if ( str.compare( "Below", Qt::CaseInsensitive ) == 0 )
3567  {
3569  }
3570  dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant( static_cast< int >( placetype ) ) );
3571  }
3572  }
3573 
3574  // data defined direction symbol reversed?
3575  dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbReverse, exprVal, context.expressionContext(), reverseDirectionSymbol );
3576  }
3577 
3578  // formatting for numbers is inline with generation of base label text and not passed to label painting
3579 }
3580 
3581 void QgsPalLayerSettings::parseShapeBackground( QgsRenderContext &context )
3582 {
3583  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3584 
3585  // data defined draw shape?
3586  bool drawShape = shapeDraw;
3587  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShapeDraw, exprVal, context.expressionContext(), shapeDraw ) )
3588  {
3589  drawShape = exprVal.toBool();
3590  }
3591 
3592  if ( !drawShape )
3593  {
3594  return;
3595  }
3596 
3597  // data defined shape transparency?
3598  int shapeTransp = shapeTransparency;
3599  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::ShapeTransparency, exprVal, context.expressionContext(), shapeTransparency ) )
3600  {
3601  shapeTransp = exprVal.toInt();
3602  }
3603 
3604  drawShape = ( drawShape && shapeTransp < 100 ); // size is not taken into account (could be)
3605 
3606  if ( !drawShape )
3607  {
3608  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
3609  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
3610  return; // don't bother evaluating values that won't be used
3611  }
3612 
3613  // data defined shape kind?
3616  {
3617  QString skind = exprVal.toString().trimmed();
3618  QgsDebugMsgLevel( QString( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
3619 
3620  if ( !skind.isEmpty() )
3621  {
3622  // "Rectangle"
3624 
3625  if ( skind.compare( "Square", Qt::CaseInsensitive ) == 0 )
3626  {
3628  }
3629  else if ( skind.compare( "Ellipse", Qt::CaseInsensitive ) == 0 )
3630  {
3632  }
3633  else if ( skind.compare( "Circle", Qt::CaseInsensitive ) == 0 )
3634  {
3636  }
3637  else if ( skind.compare( "SVG", Qt::CaseInsensitive ) == 0 )
3638  {
3640  }
3641  shapeKind = shpkind;
3642  dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant( static_cast< int >( shpkind ) ) );
3643  }
3644  }
3645 
3646  // data defined shape SVG path?
3647  QString svgPath = shapeSVGFile;
3649  {
3650  QString svgfile = exprVal.toString().trimmed();
3651  QgsDebugMsgLevel( QString( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
3652 
3653  // '' empty paths are allowed
3654  svgPath = svgfile;
3655  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgfile ) );
3656  }
3657 
3658  // data defined shape size type?
3661  {
3662  QString stype = exprVal.toString().trimmed();
3663  QgsDebugMsgLevel( QString( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
3664 
3665  if ( !stype.isEmpty() )
3666  {
3667  // "Buffer"
3669 
3670  if ( stype.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
3671  {
3673  }
3674  shpSizeType = sizType;
3675  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant( static_cast< int >( sizType ) ) );
3676  }
3677  }
3678 
3679  // data defined shape size X? (SVGs only use X for sizing)
3680  double ddShpSizeX = shapeSize.x();
3681  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeX, exprVal, context.expressionContext(), ddShpSizeX ) )
3682  {
3683  ddShpSizeX = exprVal.toDouble();
3684  }
3685 
3686  // data defined shape size Y?
3687  double ddShpSizeY = shapeSize.y();
3688  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeY, exprVal, context.expressionContext(), ddShpSizeY ) )
3689  {
3690  ddShpSizeY = exprVal.toDouble();
3691  }
3692 
3693  // don't continue under certain circumstances (e.g. size is fixed)
3694  bool skip = false;
3695  if ( shapeKind == QgsPalLayerSettings::ShapeSVG
3696  && ( svgPath.isEmpty()
3697  || ( !svgPath.isEmpty()
3698  && shpSizeType == QgsPalLayerSettings::SizeFixed
3699  && ddShpSizeX == 0.0 ) ) )
3700  {
3701  skip = true;
3702  }
3703  if ( shapeKind != QgsPalLayerSettings::ShapeSVG
3704  && shpSizeType == QgsPalLayerSettings::SizeFixed
3705  && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
3706  {
3707  skip = true;
3708  }
3709 
3710  if ( skip )
3711  {
3712  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
3713  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
3714  dataDefinedValues.remove( QgsPalLayerSettings::ShapeKind );
3715  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSVGFile );
3716  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeX );
3717  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeY );
3718  return; // don't bother evaluating values that won't be used
3719  }
3720 
3721  // data defined shape size units?
3722  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeSizeUnits, exprVal, context.expressionContext() );
3723 
3724  // data defined shape rotation type?
3726  {
3727  QString rotstr = exprVal.toString().trimmed();
3728  QgsDebugMsgLevel( QString( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
3729 
3730  if ( !rotstr.isEmpty() )
3731  {
3732  // "Sync"
3734 
3735  if ( rotstr.compare( "Offset", Qt::CaseInsensitive ) == 0 )
3736  {
3738  }
3739  else if ( rotstr.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
3740  {
3742  }
3743  dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant( static_cast< int >( rottype ) ) );
3744  }
3745  }
3746 
3747  // data defined shape rotation?
3748  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShapeRotation, exprVal, context.expressionContext(), shapeRotation );
3749 
3750  // data defined shape offset?
3751  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeOffset, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodePoint( shapeOffset ) );
3752 
3753  // data defined shape offset units?
3754  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeOffsetUnits, exprVal, context.expressionContext() );
3755 
3756  // data defined shape radii?
3757  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeRadii, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodePoint( shapeRadii ) );
3758 
3759  // data defined shape radii units?
3760  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeRadiiUnits, exprVal, context.expressionContext() );
3761 
3762  // data defined shape blend mode?
3763  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShapeBlendMode, exprVal, context.expressionContext() );
3764 
3765  // data defined shape fill color?
3766  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShapeFillColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( shapeFillColor ) );
3767 
3768  // data defined shape border color?
3770 
3771  // data defined shape border width?
3772  dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShapeBorderWidth, exprVal, context.expressionContext(), shapeBorderWidth );
3773 
3774  // data defined shape border width units?
3775  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeBorderWidthUnits, exprVal, context.expressionContext() );
3776 
3777  // data defined shape join style?
3779 
3780 }
3781 
3782 void QgsPalLayerSettings::parseDropShadow( QgsRenderContext &context )
3783 {
3784  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3785 
3786  // data defined draw shadow?
3787  bool drawShadow = shadowDraw;
3788  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShadowDraw, exprVal, context.expressionContext(), shadowDraw ) )
3789  {
3790  drawShadow = exprVal.toBool();
3791  }
3792 
3793  if ( !drawShadow )
3794  {
3795  return;
3796  }
3797 
3798  // data defined shadow transparency?
3799  int shadowTransp = shadowTransparency;
3800  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::ShadowTransparency, exprVal, context.expressionContext(), shadowTransparency ) )
3801  {
3802  shadowTransp = exprVal.toInt();
3803  }
3804 
3805  // data defined shadow offset distance?
3806  double shadowOffDist = shadowOffsetDist;
3807  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowOffsetDist, exprVal, context.expressionContext(), shadowOffsetDist ) )
3808  {
3809  shadowOffDist = exprVal.toDouble();
3810  }
3811 
3812  // data defined shadow offset distance?
3813  double shadowRad = shadowRadius;
3814  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRadius ) )
3815  {
3816  shadowRad = exprVal.toDouble();
3817  }
3818 
3819  drawShadow = ( drawShadow && shadowTransp < 100 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
3820 
3821  if ( !drawShadow )
3822  {
3823  dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
3824  dataDefinedValues.remove( QgsPalLayerSettings::ShadowTransparency );
3825  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOffsetDist );
3826  dataDefinedValues.remove( QgsPalLayerSettings::ShadowRadius );
3827  return; // don't bother evaluating values that won't be used
3828  }
3829 
3830  // data defined shadow under type?
3832  {
3833  QString str = exprVal.toString().trimmed();
3834  QgsDebugMsgLevel( QString( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
3835 
3836  if ( !str.isEmpty() )
3837  {
3838  // "Lowest"
3840 
3841  if ( str.compare( "Text", Qt::CaseInsensitive ) == 0 )
3842  {
3844  }
3845  else if ( str.compare( "Buffer", Qt::CaseInsensitive ) == 0 )
3846  {
3848  }
3849  else if ( str.compare( "Background", Qt::CaseInsensitive ) == 0 )
3850  {
3852  }
3853  dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant( static_cast< int >( shdwtype ) ) );
3854  }
3855  }
3856 
3857  // data defined shadow offset angle?
3858  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShadowOffsetAngle, exprVal, context.expressionContext(), shadowOffsetAngle );
3859 
3860  // data defined shadow offset units?
3861  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowOffsetUnits, exprVal, context.expressionContext() );
3862 
3863  // data defined shadow radius?
3864  dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRadius );
3865 
3866  // data defined shadow radius units?
3867  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowRadiusUnits, exprVal, context.expressionContext() );
3868 
3869  // data defined shadow scale? ( gui bounds to 0-2000, no upper bound here )
3870  dataDefinedValEval( DDIntPos, QgsPalLayerSettings::ShadowScale, exprVal, context.expressionContext(), shadowScale );
3871 
3872  // data defined shadow color?
3873  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShadowColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( shadowColor ) );
3874 
3875  // data defined shadow blend mode?
3876  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShadowBlendMode, exprVal, context.expressionContext() );
3877 }
3878 
3879 int QgsPalLayerSettings::sizeToPixel( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor, const QgsMapUnitScale& mapUnitScale ) const
3880 {
3881  return static_cast< int >( scaleToPixelContext( size, c, unit, rasterfactor, mapUnitScale ) + 0.5 );
3882 }
3883 
3884 double QgsPalLayerSettings::scaleToPixelContext( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor, const QgsMapUnitScale& mapUnitScale ) const
3885 {
3886  // if render context is that of device (i.e. not a scaled map), just return size
3887  double mapUnitsPerPixel = mapUnitScale.computeMapUnitsPerPixel( c );
3888 
3889  if ( unit == MapUnits && mapUnitsPerPixel > 0.0 )
3890  {
3891  size = size / mapUnitsPerPixel * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3892  }
3893  else // e.g. in points or mm
3894  {
3895  double ptsTomm = ( unit == Points ? 0.352778 : 1 );
3896  size *= ptsTomm * c.scaleFactor() * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3897  }
3898  return size;
3899 }
3900 
3901 // -------------
3902 
3904  : mEngine( new QgsLabelingEngineV2() )
3905 {
3906 }
3907 
3909 {
3910  delete mEngine;
3911  mEngine = nullptr;
3912 }
3913 
3915 {
3916  return staticWillUseLayer( layer );
3917 }
3918 
3920 {
3921  QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( layerID ) );
3922  if ( !layer )
3923  return false;
3924  return staticWillUseLayer( layer );
3925 }
3926 
3927 
3929 {
3930  // don't do QgsPalLayerSettings::readFromLayer( layer ) if not needed
3931  bool enabled = false;
3932  if ( layer->customProperty( "labeling" ).toString() == "pal" )
3933  enabled = layer->labelsEnabled() || layer->diagramsEnabled();
3934  else if ( layer->labeling()->type() == "rule-based" )
3935  return true;
3936 
3937  return enabled;
3938 }
3939 
3940 
3942 {
3943 }
3944 
3946 {
3947  Q_UNUSED( layerID );
3948 }
3949 
3950 
3952 {
3953  if ( !willUseLayer( layer ) )
3954  {
3955  return 0;
3956  }
3957 
3958  if ( !layer->labeling() )
3959  return 0;
3960 
3961  QgsVectorLayerLabelProvider* lp = layer->labeling()->provider( layer );
3962  if ( !lp )
3963  return 0;
3964 
3965  //QgsVectorLayerLabelProvider* lp = new QgsVectorLayerLabelProvider( layer, false );
3966  // need to be added before calling prepare() - uses map settings from engine
3967  mEngine->addProvider( lp );
3968  mLabelProviders[layer->id()] = lp; // fast lookup table by layer ID
3969 
3970  if ( !lp->