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