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