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