QGIS API Documentation  2.14.11-Essen
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 #include "qgsstringutils.h"
22 
23 #include <list>
24 
25 #include <pal/pal.h>
26 #include <pal/feature.h>
27 #include <pal/layer.h>
28 #include <pal/palexception.h>
29 #include <pal/problem.h>
30 #include <pal/labelposition.h>
31 
32 #include <cmath>
33 
34 #include <QApplication>
35 #include <QByteArray>
36 #include <QString>
37 #include <QFontMetrics>
38 #include <QTime>
39 #include <QPainter>
40 
41 #include "diagram/qgsdiagram.h"
42 #include "qgsdiagramrendererv2.h"
43 #include "qgsfontutils.h"
44 #include "qgslabelsearchtree.h"
45 #include "qgsexpression.h"
46 #include "qgsdatadefined.h"
47 #include "qgslabelingenginev2.h"
48 #include "qgsvectorlayerlabeling.h"
49 
50 #include <qgslogger.h>
51 #include <qgsvectorlayer.h>
52 #include <qgsmaplayerregistry.h>
53 #include <qgsvectordataprovider.h>
56 #include <qgsgeometry.h>
57 #include <qgsmaprenderer.h>
58 #include <qgsmarkersymbollayerv2.h>
59 #include <qgsproject.h>
60 #include "qgssymbolv2.h"
61 #include "qgssymbollayerv2utils.h"
62 #include <QMessageBox>
63 
64 
65 Q_GUI_EXPORT extern int qt_defaultDpiX();
66 Q_GUI_EXPORT extern int qt_defaultDpiY();
67 
68 static void _fixQPictureDPI( QPainter* p )
69 {
70  // QPicture makes an assumption that we drawing to it with system DPI.
71  // Then when being drawn, it scales the painter. The following call
72  // negates the effect. There is no way of setting QPicture's DPI.
73  // See QTBUG-20361
74  p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
75  static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
76 }
77 
78 
79 using namespace pal;
80 
81 // -------------
82 
83 /* ND: Default point label position priority. These are set to match variants of the ideal placement priority described
84  in "Making Maps", Krygier & Wood (2011) (p216),
85  "Elements of Cartography", Robinson et al (1995)
86  and "Designing Better Maps", Brewer (2005) (p76)
87  Note that while they agree on positions 1-4, 5-8 are more contentious so I've selected these placements
88  based on my preferences, and to follow Krygier and Wood's placements more closer. (I'm not going to disagree
89  with Denis Wood on anything cartography related...!)
90 */
100 //debugging only - don't use these placements by default
101 /* << QgsPalLayerSettings::TopSlightlyLeft
102 << QgsPalLayerSettings::BottomSlightlyLeft;
103 << QgsPalLayerSettings::TopMiddle
104 << QgsPalLayerSettings::BottomMiddle;*/
105 
107  : upsidedownLabels( Upright )
108  , mCurFeat( nullptr )
109  , xform( nullptr )
110  , ct( nullptr )
111  , extentGeom( nullptr )
112  , mFeaturesToLabel( 0 )
113  , mFeatsSendingToPal( 0 )
114  , mFeatsRegPal( 0 )
115  , expression( nullptr )
116 {
117  enabled = false;
118  drawLabels = true;
119  isExpression = false;
120  fieldIndex = 0;
121 
122  // text style
124  fontSizeInMapUnits = false;
125  textColor = Qt::black;
126  textTransp = 0;
127  blendMode = QPainter::CompositionMode_SourceOver;
128  previewBkgrdColor = Qt::white;
129  // font processing info
130  mTextFontFound = true;
132 
133  // text formatting
134  wrapChar = "";
135  multilineHeight = 1.0;
137  addDirectionSymbol = false;
138  leftDirectionSymbol = QString( "<" );
139  rightDirectionSymbol = QString( ">" );
140  reverseDirectionSymbol = false;
142  formatNumbers = false;
143  decimals = 3;
144  plusSign = false;
145 
146  // text buffer
147  bufferDraw = false;
148  bufferSize = 1.0;
149  bufferSizeInMapUnits = false;
150  bufferColor = Qt::white;
151  bufferTransp = 0;
152  bufferNoFill = false;
153  bufferJoinStyle = Qt::BevelJoin;
154  bufferBlendMode = QPainter::CompositionMode_SourceOver;
155 
156  // shape background
157  shapeDraw = false;
159  shapeSVGFile = QString();
161  shapeSize = QPointF( 0.0, 0.0 );
162  shapeSizeUnits = MM;
164  shapeRotation = 0.0;
165  shapeOffset = QPointF( 0.0, 0.0 );
167  shapeRadii = QPointF( 0.0, 0.0 );
169  shapeFillColor = Qt::white;
170  shapeBorderColor = Qt::darkGray;
171  shapeBorderWidth = 0.0;
173  shapeJoinStyle = Qt::BevelJoin;
174  shapeTransparency = 0;
175  shapeBlendMode = QPainter::CompositionMode_SourceOver;
176 
177  // drop shadow
178  shadowDraw = false;
180  shadowOffsetAngle = 135;
181  shadowOffsetDist = 1.0;
183  shadowOffsetGlobal = true;
184  shadowRadius = 1.5;
186  shadowRadiusAlphaOnly = false;
187  shadowTransparency = 30;
188  shadowScale = 100;
189  shadowColor = Qt::black;
190  shadowBlendMode = QPainter::CompositionMode_Multiply;
191 
192  // placement
195  centroidWhole = false;
196  centroidInside = false;
197  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
198  fitInPolygonOnly = false;
200  xOffset = 0;
201  yOffset = 0;
202  labelOffsetInMapUnits = true;
203  dist = 0;
204  distInMapUnits = false;
206  angleOffset = 0;
207  preserveRotation = true;
208  maxCurvedCharAngleIn = 20.0;
209  maxCurvedCharAngleOut = -20.0;
210  priority = 5;
211  repeatDistance = 0;
213 
214  // rendering
215  scaleVisibility = false;
216  scaleMin = 1;
217  scaleMax = 10000000;
218  fontLimitPixelSize = false;
219  fontMinPixelSize = 0; //trigger to turn it on by default for map unit labels
220  fontMaxPixelSize = 10000;
221  displayAll = false;
223 
224  labelPerPart = false;
225  mergeLines = false;
226  minFeatureSize = 0.0;
227  limitNumLabels = false;
228  maxNumLabels = 2000;
229  obstacle = true;
230  obstacleFactor = 1.0;
232  zIndex = 0.0;
233 
234  // scale factors
235  vectorScaleFactor = 1.0;
236  rasterCompressFactor = 1.0;
237 
238  // data defined string and old-style index values
239  // NOTE: in QPair use -1 for second value (other values are for old-style layer properties migration)
240 
241  // text style
242  mDataDefinedNames.insert( Size, QPair<QString, int>( "Size", 0 ) );
243  mDataDefinedNames.insert( Bold, QPair<QString, int>( "Bold", 1 ) );
244  mDataDefinedNames.insert( Italic, QPair<QString, int>( "Italic", 2 ) );
245  mDataDefinedNames.insert( Underline, QPair<QString, int>( "Underline", 3 ) );
246  mDataDefinedNames.insert( Color, QPair<QString, int>( "Color", 4 ) );
247  mDataDefinedNames.insert( Strikeout, QPair<QString, int>( "Strikeout", 5 ) );
248  mDataDefinedNames.insert( Family, QPair<QString, int>( "Family", 6 ) );
249  mDataDefinedNames.insert( FontStyle, QPair<QString, int>( "FontStyle", -1 ) );
250  mDataDefinedNames.insert( FontSizeUnit, QPair<QString, int>( "FontSizeUnit", -1 ) );
251  mDataDefinedNames.insert( FontTransp, QPair<QString, int>( "FontTransp", 18 ) );
252  mDataDefinedNames.insert( FontCase, QPair<QString, int>( "FontCase", -1 ) );
253  mDataDefinedNames.insert( FontLetterSpacing, QPair<QString, int>( "FontLetterSpacing", -1 ) );
254  mDataDefinedNames.insert( FontWordSpacing, QPair<QString, int>( "FontWordSpacing", -1 ) );
255  mDataDefinedNames.insert( FontBlendMode, QPair<QString, int>( "FontBlendMode", -1 ) );
256 
257  // text formatting
258  mDataDefinedNames.insert( MultiLineWrapChar, QPair<QString, int>( "MultiLineWrapChar", -1 ) );
259  mDataDefinedNames.insert( MultiLineHeight, QPair<QString, int>( "MultiLineHeight", -1 ) );
260  mDataDefinedNames.insert( MultiLineAlignment, QPair<QString, int>( "MultiLineAlignment", -1 ) );
261  mDataDefinedNames.insert( DirSymbDraw, QPair<QString, int>( "DirSymbDraw", -1 ) );
262  mDataDefinedNames.insert( DirSymbLeft, QPair<QString, int>( "DirSymbLeft", -1 ) );
263  mDataDefinedNames.insert( DirSymbRight, QPair<QString, int>( "DirSymbRight", -1 ) );
264  mDataDefinedNames.insert( DirSymbPlacement, QPair<QString, int>( "DirSymbPlacement", -1 ) );
265  mDataDefinedNames.insert( DirSymbReverse, QPair<QString, int>( "DirSymbReverse", -1 ) );
266  mDataDefinedNames.insert( NumFormat, QPair<QString, int>( "NumFormat", -1 ) );
267  mDataDefinedNames.insert( NumDecimals, QPair<QString, int>( "NumDecimals", -1 ) );
268  mDataDefinedNames.insert( NumPlusSign, QPair<QString, int>( "NumPlusSign", -1 ) );
269 
270  // text buffer
271  mDataDefinedNames.insert( BufferDraw, QPair<QString, int>( "BufferDraw", -1 ) );
272  mDataDefinedNames.insert( BufferSize, QPair<QString, int>( "BufferSize", 7 ) );
273  mDataDefinedNames.insert( BufferUnit, QPair<QString, int>( "BufferUnit", -1 ) );
274  mDataDefinedNames.insert( BufferColor, QPair<QString, int>( "BufferColor", 8 ) );
275  mDataDefinedNames.insert( BufferTransp, QPair<QString, int>( "BufferTransp", 19 ) );
276  mDataDefinedNames.insert( BufferJoinStyle, QPair<QString, int>( "BufferJoinStyle", -1 ) );
277  mDataDefinedNames.insert( BufferBlendMode, QPair<QString, int>( "BufferBlendMode", -1 ) );
278 
279  // background
280  mDataDefinedNames.insert( ShapeDraw, QPair<QString, int>( "ShapeDraw", -1 ) );
281  mDataDefinedNames.insert( ShapeKind, QPair<QString, int>( "ShapeKind", -1 ) );
282  mDataDefinedNames.insert( ShapeSVGFile, QPair<QString, int>( "ShapeSVGFile", -1 ) );
283  mDataDefinedNames.insert( ShapeSizeType, QPair<QString, int>( "ShapeSizeType", -1 ) );
284  mDataDefinedNames.insert( ShapeSizeX, QPair<QString, int>( "ShapeSizeX", -1 ) );
285  mDataDefinedNames.insert( ShapeSizeY, QPair<QString, int>( "ShapeSizeY", -1 ) );
286  mDataDefinedNames.insert( ShapeSizeUnits, QPair<QString, int>( "ShapeSizeUnits", -1 ) );
287  mDataDefinedNames.insert( ShapeRotationType, QPair<QString, int>( "ShapeRotationType", -1 ) );
288  mDataDefinedNames.insert( ShapeRotation, QPair<QString, int>( "ShapeRotation", -1 ) );
289  mDataDefinedNames.insert( ShapeOffset, QPair<QString, int>( "ShapeOffset", -1 ) );
290  mDataDefinedNames.insert( ShapeOffsetUnits, QPair<QString, int>( "ShapeOffsetUnits", -1 ) );
291  mDataDefinedNames.insert( ShapeRadii, QPair<QString, int>( "ShapeRadii", -1 ) );
292  mDataDefinedNames.insert( ShapeRadiiUnits, QPair<QString, int>( "ShapeRadiiUnits", -1 ) );
293  mDataDefinedNames.insert( ShapeTransparency, QPair<QString, int>( "ShapeTransparency", -1 ) );
294  mDataDefinedNames.insert( ShapeBlendMode, QPair<QString, int>( "ShapeBlendMode", -1 ) );
295  mDataDefinedNames.insert( ShapeFillColor, QPair<QString, int>( "ShapeFillColor", -1 ) );
296  mDataDefinedNames.insert( ShapeBorderColor, QPair<QString, int>( "ShapeBorderColor", -1 ) );
297  mDataDefinedNames.insert( ShapeBorderWidth, QPair<QString, int>( "ShapeBorderWidth", -1 ) );
298  mDataDefinedNames.insert( ShapeBorderWidthUnits, QPair<QString, int>( "ShapeBorderWidthUnits", -1 ) );
299  mDataDefinedNames.insert( ShapeJoinStyle, QPair<QString, int>( "ShapeJoinStyle", -1 ) );
300 
301  // drop shadow
302  mDataDefinedNames.insert( ShadowDraw, QPair<QString, int>( "ShadowDraw", -1 ) );
303  mDataDefinedNames.insert( ShadowUnder, QPair<QString, int>( "ShadowUnder", -1 ) );
304  mDataDefinedNames.insert( ShadowOffsetAngle, QPair<QString, int>( "ShadowOffsetAngle", -1 ) );
305  mDataDefinedNames.insert( ShadowOffsetDist, QPair<QString, int>( "ShadowOffsetDist", -1 ) );
306  mDataDefinedNames.insert( ShadowOffsetUnits, QPair<QString, int>( "ShadowOffsetUnits", -1 ) );
307  mDataDefinedNames.insert( ShadowRadius, QPair<QString, int>( "ShadowRadius", -1 ) );
308  mDataDefinedNames.insert( ShadowRadiusUnits, QPair<QString, int>( "ShadowRadiusUnits", -1 ) );
309  mDataDefinedNames.insert( ShadowTransparency, QPair<QString, int>( "ShadowTransparency", -1 ) );
310  mDataDefinedNames.insert( ShadowScale, QPair<QString, int>( "ShadowScale", -1 ) );
311  mDataDefinedNames.insert( ShadowColor, QPair<QString, int>( "ShadowColor", -1 ) );
312  mDataDefinedNames.insert( ShadowBlendMode, QPair<QString, int>( "ShadowBlendMode", -1 ) );
313 
314  // placement
315  mDataDefinedNames.insert( CentroidWhole, QPair<QString, int>( "CentroidWhole", -1 ) );
316  mDataDefinedNames.insert( OffsetQuad, QPair<QString, int>( "OffsetQuad", -1 ) );
317  mDataDefinedNames.insert( OffsetXY, QPair<QString, int>( "OffsetXY", -1 ) );
318  mDataDefinedNames.insert( OffsetUnits, QPair<QString, int>( "OffsetUnits", -1 ) );
319  mDataDefinedNames.insert( LabelDistance, QPair<QString, int>( "LabelDistance", 13 ) );
320  mDataDefinedNames.insert( DistanceUnits, QPair<QString, int>( "DistanceUnits", -1 ) );
321  mDataDefinedNames.insert( OffsetRotation, QPair<QString, int>( "OffsetRotation", -1 ) );
322  mDataDefinedNames.insert( CurvedCharAngleInOut, QPair<QString, int>( "CurvedCharAngleInOut", -1 ) );
323  mDataDefinedNames.insert( RepeatDistance, QPair<QString, int>( "RepeatDistance", -1 ) );
324  mDataDefinedNames.insert( RepeatDistanceUnit, QPair<QString, int>( "RepeatDistanceUnit", -1 ) );
325  mDataDefinedNames.insert( Priority, QPair<QString, int>( "Priority", -1 ) );
326  mDataDefinedNames.insert( IsObstacle, QPair<QString, int>( "IsObstacle", -1 ) );
327  mDataDefinedNames.insert( ObstacleFactor, QPair<QString, int>( "ObstacleFactor", -1 ) );
328  mDataDefinedNames.insert( PredefinedPositionOrder, QPair<QString, int>( "PredefinedPositionOrder", -1 ) );
329 
330  // (data defined only)
331  mDataDefinedNames.insert( PositionX, QPair<QString, int>( "PositionX", 9 ) );
332  mDataDefinedNames.insert( PositionY, QPair<QString, int>( "PositionY", 10 ) );
333  mDataDefinedNames.insert( Hali, QPair<QString, int>( "Hali", 11 ) );
334  mDataDefinedNames.insert( Vali, QPair<QString, int>( "Vali", 12 ) );
335  mDataDefinedNames.insert( Rotation, QPair<QString, int>( "Rotation", 14 ) );
336 
337  //rendering
338  mDataDefinedNames.insert( ScaleVisibility, QPair<QString, int>( "ScaleVisibility", -1 ) );
339  mDataDefinedNames.insert( MinScale, QPair<QString, int>( "MinScale", 16 ) );
340  mDataDefinedNames.insert( MaxScale, QPair<QString, int>( "MaxScale", 17 ) );
341  mDataDefinedNames.insert( FontLimitPixel, QPair<QString, int>( "FontLimitPixel", -1 ) );
342  mDataDefinedNames.insert( FontMinPixel, QPair<QString, int>( "FontMinPixel", -1 ) );
343  mDataDefinedNames.insert( FontMaxPixel, QPair<QString, int>( "FontMaxPixel", -1 ) );
344  mDataDefinedNames.insert( ZIndex, QPair<QString, int>( "ZIndex", -1 ) );
345  // (data defined only)
346  mDataDefinedNames.insert( Show, QPair<QString, int>( "Show", 15 ) );
347  mDataDefinedNames.insert( AlwaysShow, QPair<QString, int>( "AlwaysShow", 20 ) );
348 
349  // temp stuff for when drawing label components (don't copy)
350  showingShadowRects = false;
351 }
352 
354  : mCurFeat( nullptr )
355  , fieldIndex( 0 )
356  , xform( nullptr )
357  , ct( nullptr )
358  , extentGeom( nullptr )
359  , mFeaturesToLabel( 0 )
360  , mFeatsSendingToPal( 0 )
361  , mFeatsRegPal( 0 )
362  , showingShadowRects( false )
363  , expression( nullptr )
364 {
365  *this = s;
366 }
367 
369 {
370  if ( this == &s )
371  return *this;
372 
373  // copy only permanent stuff
374 
375  enabled = s.enabled;
377 
378  // text style
379  fieldName = s.fieldName;
381  textFont = s.textFont;
385  textColor = s.textColor;
387  blendMode = s.blendMode;
389  // font processing info
392 
393  // text formatting
394  wrapChar = s.wrapChar;
403  decimals = s.decimals;
404  plusSign = s.plusSign;
405 
406  // text buffer
416 
417  // placement
418  placement = s.placement;
425  xOffset = s.xOffset;
426  yOffset = s.yOffset;
429  dist = s.dist;
437  priority = s.priority;
441 
442  // rendering
444  scaleMin = s.scaleMin;
445  scaleMax = s.scaleMax;
451 
457  obstacle = s.obstacle;
460  zIndex = s.zIndex;
461 
462  // shape background
463  shapeDraw = s.shapeDraw;
464  shapeType = s.shapeType;
467  shapeSize = s.shapeSize;
486 
487  // drop shadow
503 
504  // data defined
507  for ( ; it != s.dataDefinedProperties.constEnd(); ++it )
508  {
509  dataDefinedProperties.insert( it.key(), it.value() ? new QgsDataDefined( *it.value() ) : nullptr );
510  }
511  mDataDefinedNames = s.mDataDefinedNames;
512 
513  // scale factors
516  return *this;
517 }
518 
519 
521 {
522  // pal layer is deleted internally in PAL
523 
524  delete ct;
525  delete expression;
526  delete extentGeom;
527 
528  // delete all QgsDataDefined objects (which also deletes their QgsExpression object)
530 }
531 
532 
534 {
535  QgsPalLayerSettings settings;
536  settings.readFromLayer( layer );
537  return settings;
538 }
539 
540 
542 {
543  if ( !expression )
544  {
545  expression = new QgsExpression( fieldName );
546  }
547  return expression;
548 }
549 
550 static QColor _readColor( QgsVectorLayer* layer, const QString& property, const QColor& defaultColor = Qt::black, bool withAlpha = true )
551 {
552  int r = layer->customProperty( property + 'R', QVariant( defaultColor.red() ) ).toInt();
553  int g = layer->customProperty( property + 'G', QVariant( defaultColor.green() ) ).toInt();
554  int b = layer->customProperty( property + 'B', QVariant( defaultColor.blue() ) ).toInt();
555  int a = withAlpha ? layer->customProperty( property + 'A', QVariant( defaultColor.alpha() ) ).toInt() : 255;
556  return QColor( r, g, b, a );
557 }
558 
559 static void _writeColor( QgsVectorLayer* layer, const QString& property, const QColor& color, bool withAlpha = true )
560 {
561  layer->setCustomProperty( property + 'R', color.red() );
562  layer->setCustomProperty( property + 'G', color.green() );
563  layer->setCustomProperty( property + 'B', color.blue() );
564  if ( withAlpha )
565  layer->setCustomProperty( property + 'A', color.alpha() );
566 }
567 
569 {
570  if ( str.compare( "Point", Qt::CaseInsensitive ) == 0
571  || str.compare( "Points", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Points;
572  if ( str.compare( "MapUnit", Qt::CaseInsensitive ) == 0
573  || str.compare( "MapUnits", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::MapUnits;
574  if ( str.compare( "Percent", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Percent;
575  return QgsPalLayerSettings::MM; // "MM"
576 }
577 
578 static Qt::PenJoinStyle _decodePenJoinStyle( const QString& str )
579 {
580  if ( str.compare( "Miter", Qt::CaseInsensitive ) == 0 ) return Qt::MiterJoin;
581  if ( str.compare( "Round", Qt::CaseInsensitive ) == 0 ) return Qt::RoundJoin;
582  return Qt::BevelJoin; // "Bevel"
583 }
584 
585 void QgsPalLayerSettings::readDataDefinedPropertyMap( QgsVectorLayer* layer, QDomElement* parentElem,
587 {
588  if ( !layer && !parentElem )
589  {
590  return;
591  }
592 
594  while ( i.hasNext() )
595  {
596  i.next();
597  if ( layer )
598  {
599  // reading from layer's custom properties (old way)
600  readDataDefinedProperty( layer, i.key(), propertyMap );
601  }
602  else if ( parentElem )
603  {
604  // reading from XML (new way)
605  QDomElement e = parentElem->firstChildElement( i.value().first );
606  if ( !e.isNull() )
607  {
608  QgsDataDefined* dd = new QgsDataDefined();
609  if ( dd->setFromXmlElement( e ) )
610  propertyMap.insert( i.key(), dd );
611  else
612  delete dd;
613  }
614  }
615  }
616 }
617 
618 void QgsPalLayerSettings::writeDataDefinedPropertyMap( QgsVectorLayer* layer, QDomElement* parentElem,
620 {
621  if ( !layer && !parentElem )
622  {
623  return;
624  }
625 
627  while ( i.hasNext() )
628  {
629  i.next();
630  QString newPropertyName = "labeling/dataDefined/" + i.value().first;
631  QVariant propertyValue = QVariant();
632 
634  if ( it != propertyMap.constEnd() )
635  {
636  QgsDataDefined* dd = it.value();
637  if ( dd )
638  {
639  bool active = dd->isActive();
640  bool useExpr = dd->useExpression();
641  QString expr = dd->expressionString();
642  QString field = dd->field();
643 
644  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
645 
646  if ( !defaultVals )
647  {
648  // TODO: update this when project settings for labeling are migrated to better XML layout
649  QStringList values;
650  values << ( active ? "1" : "0" );
651  values << ( useExpr ? "1" : "0" );
652  values << expr;
653  values << field;
654  if ( !values.isEmpty() )
655  {
656  propertyValue = QVariant( values.join( "~~" ) );
657  }
658  }
659 
660  if ( parentElem )
661  {
662  // writing to XML document (instead of writing to layer)
663  QDomDocument doc = parentElem->ownerDocument();
664  QDomElement e = dd->toXmlElement( doc, i.value().first );
665  parentElem->appendChild( e );
666  }
667  }
668  }
669 
670  if ( layer )
671  {
672  // writing to layer's custom properties (old method)
673 
674  if ( propertyValue.isValid() )
675  {
676  layer->setCustomProperty( newPropertyName, propertyValue );
677  }
678  else
679  {
680  // remove unused properties
681  layer->removeCustomProperty( newPropertyName );
682  }
683 
684  if ( layer->customProperty( newPropertyName, QVariant() ).isValid() && i.value().second > -1 )
685  {
686  // remove old-style field index-based property, if still present
687  layer->removeCustomProperty( QString( "labeling/dataDefinedProperty" ) + QString::number( i.value().second ) );
688  }
689  }
690  }
691 }
692 
693 void QgsPalLayerSettings::readDataDefinedProperty( QgsVectorLayer* layer,
696 {
697  QString newPropertyName = "labeling/dataDefined/" + mDataDefinedNames.value( p ).first;
698  QVariant newPropertyField = layer->customProperty( newPropertyName, QVariant() );
699 
700  QString ddString = QString();
701  if ( newPropertyField.isValid() )
702  {
703  ddString = newPropertyField.toString();
704  }
705  else // maybe working with old-style field index-based property (< QGIS 2.0)
706  {
707  int oldIndx = mDataDefinedNames.value( p ).second;
708 
709  if ( oldIndx < 0 ) // something went wrong and we are working with new-style
710  {
711  return;
712  }
713 
714  QString oldPropertyName = "labeling/dataDefinedProperty" + QString::number( oldIndx );
715  QVariant oldPropertyField = layer->customProperty( oldPropertyName, QVariant() );
716 
717  if ( !oldPropertyField.isValid() )
718  {
719  return;
720  }
721 
722  // switch from old-style field index- to name-based properties
723  bool conversionOk;
724  int indx = oldPropertyField.toInt( &conversionOk );
725 
726  if ( conversionOk )
727  {
728  // Fix to migrate from old-style vector api, where returned QMap keys possibly
729  // had 'holes' in sequence of field indices, e.g. 0,2,3
730  // QgsAttrPalIndexNameHash provides a means of access field name in sequences from
731  // providers that procuded holes (e.g. PostGIS skipped geom column), otherwise it is empty
732  QgsAttrPalIndexNameHash oldIndicesToNames = layer->dataProvider()->palAttributeIndexNames();
733 
734  if ( !oldIndicesToNames.isEmpty() )
735  {
736  ddString = oldIndicesToNames.value( indx );
737  }
738  else
739  {
740  QgsFields fields = layer->dataProvider()->fields();
741  if ( indx < fields.size() ) // in case field count has changed
742  {
743  ddString = fields.at( indx ).name();
744  }
745  }
746  }
747 
748  if ( !ddString.isEmpty() )
749  {
750  //upgrade any existing property to field name-based
751  layer->setCustomProperty( newPropertyName, QVariant( updateDataDefinedString( ddString ) ) );
752 
753  // fix for buffer drawing triggered off of just its data defined size in the past (<2.0)
754  if ( oldIndx == 7 ) // old bufferSize enum
755  {
756  bufferDraw = true;
757  layer->setCustomProperty( "labeling/bufferDraw", true );
758  }
759 
760  // fix for scale visibility limits triggered off of just its data defined values in the past (<2.0)
761  if ( oldIndx == 16 || oldIndx == 17 ) // old minScale and maxScale enums
762  {
763  scaleVisibility = true;
764  layer->setCustomProperty( "labeling/scaleVisibility", true );
765  }
766  }
767 
768  // remove old-style field index-based property
769  layer->removeCustomProperty( oldPropertyName );
770  }
771 
772  if ( !ddString.isEmpty() && ddString != QLatin1String( "0~~0~~~~" ) )
773  {
774  // TODO: update this when project settings for labeling are migrated to better XML layout
775  QString newStyleString = updateDataDefinedString( ddString );
776  QStringList ddv = newStyleString.split( "~~" );
777 
778  QgsDataDefined* dd = new QgsDataDefined( ddv.at( 0 ).toInt(), ddv.at( 1 ).toInt(), ddv.at( 2 ), ddv.at( 3 ) );
779  propertyMap.insert( p, dd );
780  }
781  else
782  {
783  // remove unused properties
784  layer->removeCustomProperty( newPropertyName );
785  }
786 }
787 
789 {
790  if ( layer->customProperty( "labeling" ).toString() != QLatin1String( "pal" ) )
791  {
792  // for polygons the "over point" (over centroid) placement is better than the default
793  // "around point" (around centroid) which is more suitable for points
794  if ( layer->geometryType() == QGis::Polygon )
796 
797  return; // there's no information available
798  }
799 
800  // NOTE: set defaults for newly added properties, for backwards compatibility
801 
802  enabled = layer->labelsEnabled();
803  drawLabels = layer->customProperty( "labeling/drawLabels", true ).toBool();
804 
805  // text style
806  fieldName = layer->customProperty( "labeling/fieldName" ).toString();
807  isExpression = layer->customProperty( "labeling/isExpression" ).toBool();
808  QFont appFont = QApplication::font();
809  mTextFontFamily = layer->customProperty( "labeling/fontFamily", QVariant( appFont.family() ) ).toString();
810  QString fontFamily = mTextFontFamily;
812  {
813  // trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
814  mTextFontFound = false;
815 
816  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
817  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
818 
819  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
820  fontFamily = appFont.family();
821  }
822 
823  double fontSize = layer->customProperty( "labeling/fontSize" ).toDouble();
824  fontSizeInMapUnits = layer->customProperty( "labeling/fontSizeInMapUnits" ).toBool();
825  if ( layer->customProperty( "labeling/fontSizeMapUnitScale" ).toString().isEmpty() )
826  {
827  //fallback to older property
828  fontSizeMapUnitScale.minScale = layer->customProperty( "labeling/fontSizeMapUnitMinScale", 0.0 ).toDouble();
829  fontSizeMapUnitScale.maxScale = layer->customProperty( "labeling/fontSizeMapUnitMaxScale", 0.0 ).toDouble();
830  }
831  else
832  {
833  fontSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/fontSizeMapUnitScale" ).toString() );
834  }
835  int fontWeight = layer->customProperty( "labeling/fontWeight" ).toInt();
836  bool fontItalic = layer->customProperty( "labeling/fontItalic" ).toBool();
837  textFont = QFont( fontFamily, fontSize, fontWeight, fontItalic );
838  textFont.setPointSizeF( fontSize ); //double precision needed because of map units
839  textNamedStyle = QgsFontUtils::translateNamedStyle( layer->customProperty( "labeling/namedStyle", QVariant( "" ) ).toString() );
840  QgsFontUtils::updateFontViaStyle( textFont, textNamedStyle ); // must come after textFont.setPointSizeF()
841  textFont.setCapitalization( static_cast< QFont::Capitalization >( layer->customProperty( "labeling/fontCapitals", QVariant( 0 ) ).toUInt() ) );
842  textFont.setUnderline( layer->customProperty( "labeling/fontUnderline" ).toBool() );
843  textFont.setStrikeOut( layer->customProperty( "labeling/fontStrikeout" ).toBool() );
844  textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( "labeling/fontLetterSpacing", QVariant( 0.0 ) ).toDouble() );
845  textFont.setWordSpacing( layer->customProperty( "labeling/fontWordSpacing", QVariant( 0.0 ) ).toDouble() );
846  textColor = _readColor( layer, "labeling/textColor", Qt::black, false );
847  textTransp = layer->customProperty( "labeling/textTransp" ).toInt();
849  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/blendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
850  previewBkgrdColor = QColor( layer->customProperty( "labeling/previewBkgrdColor", QVariant( "#ffffff" ) ).toString() );
851 
852 
853  // text formatting
854  wrapChar = layer->customProperty( "labeling/wrapChar" ).toString();
855  multilineHeight = layer->customProperty( "labeling/multilineHeight", QVariant( 1.0 ) ).toDouble();
856  multilineAlign = static_cast< MultiLineAlign >( layer->customProperty( "labeling/multilineAlign", QVariant( MultiLeft ) ).toUInt() );
857  addDirectionSymbol = layer->customProperty( "labeling/addDirectionSymbol" ).toBool();
858  leftDirectionSymbol = layer->customProperty( "labeling/leftDirectionSymbol", QVariant( "<" ) ).toString();
859  rightDirectionSymbol = layer->customProperty( "labeling/rightDirectionSymbol", QVariant( ">" ) ).toString();
860  reverseDirectionSymbol = layer->customProperty( "labeling/reverseDirectionSymbol" ).toBool();
861  placeDirectionSymbol = static_cast< DirectionSymbols >( layer->customProperty( "labeling/placeDirectionSymbol", QVariant( SymbolLeftRight ) ).toUInt() );
862  formatNumbers = layer->customProperty( "labeling/formatNumbers" ).toBool();
863  decimals = layer->customProperty( "labeling/decimals" ).toInt();
864  plusSign = layer->customProperty( "labeling/plussign" ).toBool();
865 
866  // text buffer
867  double bufSize = layer->customProperty( "labeling/bufferSize", QVariant( 0.0 ) ).toDouble();
868 
869  // fix for buffer being keyed off of just its size in the past (<2.0)
870  QVariant drawBuffer = layer->customProperty( "labeling/bufferDraw", QVariant() );
871  if ( drawBuffer.isValid() )
872  {
873  bufferDraw = drawBuffer.toBool();
874  bufferSize = bufSize;
875  }
876  else if ( bufSize != 0.0 )
877  {
878  bufferDraw = true;
879  bufferSize = bufSize;
880  }
881  else
882  {
883  // keep bufferSize at new 1.0 default
884  bufferDraw = false;
885  }
886 
887  bufferSizeInMapUnits = layer->customProperty( "labeling/bufferSizeInMapUnits" ).toBool();
888  if ( layer->customProperty( "labeling/bufferSizeMapUnitScale" ).toString().isEmpty() )
889  {
890  //fallback to older property
891  bufferSizeMapUnitScale.minScale = layer->customProperty( "labeling/bufferSizeMapUnitMinScale", 0.0 ).toDouble();
892  bufferSizeMapUnitScale.maxScale = layer->customProperty( "labeling/bufferSizeMapUnitMaxScale", 0.0 ).toDouble();
893  }
894  else
895  {
896  bufferSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/bufferSizeMapUnitScale" ).toString() );
897  }
898  bufferColor = _readColor( layer, "labeling/bufferColor", Qt::white, false );
899  bufferTransp = layer->customProperty( "labeling/bufferTransp" ).toInt();
901  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/bufferBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
902  bufferJoinStyle = static_cast< Qt::PenJoinStyle >( layer->customProperty( "labeling/bufferJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt() );
903  bufferNoFill = layer->customProperty( "labeling/bufferNoFill", QVariant( false ) ).toBool();
904 
905  // background
906  shapeDraw = layer->customProperty( "labeling/shapeDraw", QVariant( false ) ).toBool();
907  shapeType = static_cast< ShapeType >( layer->customProperty( "labeling/shapeType", QVariant( ShapeRectangle ) ).toUInt() );
908  shapeSVGFile = layer->customProperty( "labeling/shapeSVGFile", QVariant( "" ) ).toString();
909  shapeSizeType = static_cast< SizeType >( layer->customProperty( "labeling/shapeSizeType", QVariant( SizeBuffer ) ).toUInt() );
910  shapeSize = QPointF( layer->customProperty( "labeling/shapeSizeX", QVariant( 0.0 ) ).toDouble(),
911  layer->customProperty( "labeling/shapeSizeY", QVariant( 0.0 ) ).toDouble() );
912  shapeSizeUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeSizeUnits", QVariant( MM ) ).toUInt() );
913  if ( layer->customProperty( "labeling/shapeSizeMapUnitScale" ).toString().isEmpty() )
914  {
915  //fallback to older property
916  shapeSizeMapUnitScale.minScale = layer->customProperty( "labeling/shapeSizeMapUnitMinScale", 0.0 ).toDouble();
917  shapeSizeMapUnitScale.maxScale = layer->customProperty( "labeling/shapeSizeMapUnitMaxScale", 0.0 ).toDouble();
918  }
919  else
920  {
921  shapeSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeSizeMapUnitScale" ).toString() );
922  }
923  shapeRotationType = static_cast< RotationType >( layer->customProperty( "labeling/shapeRotationType", QVariant( RotationSync ) ).toUInt() );
924  shapeRotation = layer->customProperty( "labeling/shapeRotation", QVariant( 0.0 ) ).toDouble();
925  shapeOffset = QPointF( layer->customProperty( "labeling/shapeOffsetX", QVariant( 0.0 ) ).toDouble(),
926  layer->customProperty( "labeling/shapeOffsetY", QVariant( 0.0 ) ).toDouble() );
927  shapeOffsetUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeOffsetUnits", QVariant( MM ) ).toUInt() );
928  if ( layer->customProperty( "labeling/shapeOffsetMapUnitScale" ).toString().isEmpty() )
929  {
930  //fallback to older property
931  shapeOffsetMapUnitScale.minScale = layer->customProperty( "labeling/shapeOffsetMapUnitMinScale", 0.0 ).toDouble();
932  shapeOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/shapeOffsetMapUnitMaxScale", 0.0 ).toDouble();
933  }
934  else
935  {
936  shapeOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeOffsetMapUnitScale" ).toString() );
937  }
938  shapeRadii = QPointF( layer->customProperty( "labeling/shapeRadiiX", QVariant( 0.0 ) ).toDouble(),
939  layer->customProperty( "labeling/shapeRadiiY", QVariant( 0.0 ) ).toDouble() );
940  shapeRadiiUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeRadiiUnits", QVariant( MM ) ).toUInt() );
941  if ( layer->customProperty( "labeling/shapeRadiiMapUnitScale" ).toString().isEmpty() )
942  {
943  //fallback to older property
944  shapeRadiiMapUnitScale.minScale = layer->customProperty( "labeling/shapeRadiiMapUnitMinScale", 0.0 ).toDouble();
945  shapeRadiiMapUnitScale.maxScale = layer->customProperty( "labeling/shapeRadiiMapUnitMaxScale", 0.0 ).toDouble();
946  }
947  else
948  {
949  shapeRadiiMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeRadiiMapUnitScale" ).toString() );
950  }
951  shapeFillColor = _readColor( layer, "labeling/shapeFillColor", Qt::white, true );
952  shapeBorderColor = _readColor( layer, "labeling/shapeBorderColor", Qt::darkGray, true );
953  shapeBorderWidth = layer->customProperty( "labeling/shapeBorderWidth", QVariant( .0 ) ).toDouble();
954  shapeBorderWidthUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shapeBorderWidthUnits", QVariant( MM ) ).toUInt() );
955  if ( layer->customProperty( "labeling/shapeBorderWidthMapUnitScale" ).toString().isEmpty() )
956  {
957  //fallback to older property
958  shapeBorderWidthMapUnitScale.minScale = layer->customProperty( "labeling/shapeBorderWidthMapUnitMinScale", 0.0 ).toDouble();
959  shapeBorderWidthMapUnitScale.maxScale = layer->customProperty( "labeling/shapeBorderWidthMapUnitMaxScale", 0.0 ).toDouble();
960  }
961  else
962  {
963  shapeBorderWidthMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shapeBorderWidthMapUnitScale" ).toString() );
964  }
965  shapeJoinStyle = static_cast< Qt::PenJoinStyle >( layer->customProperty( "labeling/shapeJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt() );
966  shapeTransparency = layer->customProperty( "labeling/shapeTransparency", QVariant( 0 ) ).toInt();
968  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/shapeBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
969 
970  // drop shadow
971  shadowDraw = layer->customProperty( "labeling/shadowDraw", QVariant( false ) ).toBool();
972  shadowUnder = static_cast< ShadowType >( layer->customProperty( "labeling/shadowUnder", QVariant( ShadowLowest ) ).toUInt() );//ShadowLowest;
973  shadowOffsetAngle = layer->customProperty( "labeling/shadowOffsetAngle", QVariant( 135 ) ).toInt();
974  shadowOffsetDist = layer->customProperty( "labeling/shadowOffsetDist", QVariant( 1.0 ) ).toDouble();
975  shadowOffsetUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shadowOffsetUnits", QVariant( MM ) ).toUInt() );
976  if ( layer->customProperty( "labeling/shadowOffsetMapUnitScale" ).toString().isEmpty() )
977  {
978  //fallback to older property
979  shadowOffsetMapUnitScale.minScale = layer->customProperty( "labeling/shadowOffsetMapUnitMinScale", 0.0 ).toDouble();
980  shadowOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/shadowOffsetMapUnitMaxScale", 0.0 ).toDouble();
981  }
982  else
983  {
984  shadowOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shadowOffsetMapUnitScale" ).toString() );
985  }
986  shadowOffsetGlobal = layer->customProperty( "labeling/shadowOffsetGlobal", QVariant( true ) ).toBool();
987  shadowRadius = layer->customProperty( "labeling/shadowRadius", QVariant( 1.5 ) ).toDouble();
988  shadowRadiusUnits = static_cast< SizeUnit >( layer->customProperty( "labeling/shadowRadiusUnits", QVariant( MM ) ).toUInt() );
989  if ( layer->customProperty( "labeling/shadowRadiusMapUnitScale" ).toString().isEmpty() )
990  {
991  //fallback to older property
992  shadowRadiusMapUnitScale.minScale = layer->customProperty( "labeling/shadowRadiusMapUnitMinScale", 0.0 ).toDouble();
993  shadowRadiusMapUnitScale.maxScale = layer->customProperty( "labeling/shadowRadiusMapUnitMaxScale", 0.0 ).toDouble();
994  }
995  else
996  {
997  shadowRadiusMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/shadowRadiusMapUnitScale" ).toString() );
998  }
999  shadowRadiusAlphaOnly = layer->customProperty( "labeling/shadowRadiusAlphaOnly", QVariant( false ) ).toBool();
1000  shadowTransparency = layer->customProperty( "labeling/shadowTransparency", QVariant( 30 ) ).toInt();
1001  shadowScale = layer->customProperty( "labeling/shadowScale", QVariant( 100 ) ).toInt();
1002  shadowColor = _readColor( layer, "labeling/shadowColor", Qt::black, false );
1004  static_cast< QgsMapRenderer::BlendMode >( layer->customProperty( "labeling/shadowBlendMode", QVariant( QgsMapRenderer::BlendMultiply ) ).toUInt() ) );
1005 
1006  // placement
1007  placement = static_cast< Placement >( layer->customProperty( "labeling/placement" ).toInt() );
1008  placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt();
1009  centroidWhole = layer->customProperty( "labeling/centroidWhole", QVariant( false ) ).toBool();
1010  centroidInside = layer->customProperty( "labeling/centroidInside", QVariant( false ) ).toBool();
1011  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( layer->customProperty( "labeling/predefinedPositionOrder" ).toString() );
1013  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
1014  fitInPolygonOnly = layer->customProperty( "labeling/fitInPolygonOnly", QVariant( false ) ).toBool();
1015  dist = layer->customProperty( "labeling/dist" ).toDouble();
1016  distInMapUnits = layer->customProperty( "labeling/distInMapUnits" ).toBool();
1017  if ( layer->customProperty( "labeling/distMapUnitScale" ).toString().isEmpty() )
1018  {
1019  //fallback to older property
1020  distMapUnitScale.minScale = layer->customProperty( "labeling/distMapUnitMinScale", 0.0 ).toDouble();
1021  distMapUnitScale.maxScale = layer->customProperty( "labeling/distMapUnitMaxScale", 0.0 ).toDouble();
1022  }
1023  else
1024  {
1025  distMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/distMapUnitScale" ).toString() );
1026  }
1027  offsetType = static_cast< OffsetType >( layer->customProperty( "labeling/offsetType", QVariant( FromPoint ) ).toUInt() );
1028  quadOffset = static_cast< QuadrantPosition >( layer->customProperty( "labeling/quadOffset", QVariant( QuadrantOver ) ).toUInt() );
1029  xOffset = layer->customProperty( "labeling/xOffset", QVariant( 0.0 ) ).toDouble();
1030  yOffset = layer->customProperty( "labeling/yOffset", QVariant( 0.0 ) ).toDouble();
1031  labelOffsetInMapUnits = layer->customProperty( "labeling/labelOffsetInMapUnits", QVariant( true ) ).toBool();
1032  if ( layer->customProperty( "labeling/labelOffsetMapUnitScale" ).toString().isEmpty() )
1033  {
1034  //fallback to older property
1035  labelOffsetMapUnitScale.minScale = layer->customProperty( "labeling/labelOffsetMapUnitMinScale", 0.0 ).toDouble();
1036  labelOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/labelOffsetMapUnitMaxScale", 0.0 ).toDouble();
1037  }
1038  else
1039  {
1040  labelOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/labelOffsetMapUnitScale" ).toString() );
1041  }
1042  angleOffset = layer->customProperty( "labeling/angleOffset", QVariant( 0.0 ) ).toDouble();
1043  preserveRotation = layer->customProperty( "labeling/preserveRotation", QVariant( true ) ).toBool();
1044  maxCurvedCharAngleIn = layer->customProperty( "labeling/maxCurvedCharAngleIn", QVariant( 20.0 ) ).toDouble();
1045  maxCurvedCharAngleOut = layer->customProperty( "labeling/maxCurvedCharAngleOut", QVariant( -20.0 ) ).toDouble();
1046  priority = layer->customProperty( "labeling/priority" ).toInt();
1047  repeatDistance = layer->customProperty( "labeling/repeatDistance", 0.0 ).toDouble();
1048  repeatDistanceUnit = static_cast< SizeUnit >( layer->customProperty( "labeling/repeatDistanceUnit", QVariant( MM ) ).toUInt() );
1049  if ( layer->customProperty( "labeling/repeatDistanceMapUnitScale" ).toString().isEmpty() )
1050  {
1051  //fallback to older property
1052  repeatDistanceMapUnitScale.minScale = layer->customProperty( "labeling/repeatDistanceMapUnitMinScale", 0.0 ).toDouble();
1053  repeatDistanceMapUnitScale.maxScale = layer->customProperty( "labeling/repeatDistanceMapUnitMaxScale", 0.0 ).toDouble();
1054  }
1055  else
1056  {
1057  repeatDistanceMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( layer->customProperty( "labeling/repeatDistanceMapUnitScale" ).toString() );
1058  }
1059 
1060  // rendering
1061  int scalemn = layer->customProperty( "labeling/scaleMin", QVariant( 0 ) ).toInt();
1062  int scalemx = layer->customProperty( "labeling/scaleMax", QVariant( 0 ) ).toInt();
1063 
1064  // fix for scale visibility limits being keyed off of just its values in the past (<2.0)
1065  QVariant scalevis = layer->customProperty( "labeling/scaleVisibility", QVariant() );
1066  if ( scalevis.isValid() )
1067  {
1068  scaleVisibility = scalevis.toBool();
1069  scaleMin = scalemn;
1070  scaleMax = scalemx;
1071  }
1072  else if ( scalemn > 0 || scalemx > 0 )
1073  {
1074  scaleVisibility = true;
1075  scaleMin = scalemn;
1076  scaleMax = scalemx;
1077  }
1078  else
1079  {
1080  // keep scaleMin and scaleMax at new 1.0 defaults (1 and 10000000, were 0 and 0)
1081  scaleVisibility = false;
1082  }
1083 
1084 
1085  fontLimitPixelSize = layer->customProperty( "labeling/fontLimitPixelSize", QVariant( false ) ).toBool();
1086  fontMinPixelSize = layer->customProperty( "labeling/fontMinPixelSize", QVariant( 0 ) ).toInt();
1087  fontMaxPixelSize = layer->customProperty( "labeling/fontMaxPixelSize", QVariant( 10000 ) ).toInt();
1088  displayAll = layer->customProperty( "labeling/displayAll", QVariant( false ) ).toBool();
1089  upsidedownLabels = static_cast< UpsideDownLabels >( layer->customProperty( "labeling/upsidedownLabels", QVariant( Upright ) ).toUInt() );
1090 
1091  labelPerPart = layer->customProperty( "labeling/labelPerPart" ).toBool();
1092  mergeLines = layer->customProperty( "labeling/mergeLines" ).toBool();
1093  minFeatureSize = layer->customProperty( "labeling/minFeatureSize" ).toDouble();
1094  limitNumLabels = layer->customProperty( "labeling/limitNumLabels", QVariant( false ) ).toBool();
1095  maxNumLabels = layer->customProperty( "labeling/maxNumLabels", QVariant( 2000 ) ).toInt();
1096  obstacle = layer->customProperty( "labeling/obstacle", QVariant( true ) ).toBool();
1097  obstacleFactor = layer->customProperty( "labeling/obstacleFactor", QVariant( 1.0 ) ).toDouble();
1098  obstacleType = static_cast< ObstacleType >( layer->customProperty( "labeling/obstacleType", QVariant( PolygonInterior ) ).toUInt() );
1099  zIndex = layer->customProperty( "labeling/zIndex", QVariant( 0.0 ) ).toDouble();
1100 
1101  readDataDefinedPropertyMap( layer, nullptr, dataDefinedProperties );
1102 }
1103 
1105 {
1106  // this is a mark that labeling information is present
1107  layer->setCustomProperty( "labeling", "pal" );
1108 
1109  layer->setCustomProperty( "labeling/enabled", enabled );
1110  layer->setCustomProperty( "labeling/drawLabels", drawLabels );
1111 
1112  // text style
1113  layer->setCustomProperty( "labeling/fieldName", fieldName );
1114  layer->setCustomProperty( "labeling/isExpression", isExpression );
1115  layer->setCustomProperty( "labeling/fontFamily", textFont.family() );
1116  layer->setCustomProperty( "labeling/namedStyle", QgsFontUtils::untranslateNamedStyle( textNamedStyle ) );
1117  layer->setCustomProperty( "labeling/fontSize", textFont.pointSizeF() );
1118  layer->setCustomProperty( "labeling/fontSizeInMapUnits", fontSizeInMapUnits );
1119  layer->setCustomProperty( "labeling/fontSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( fontSizeMapUnitScale ) );
1120  layer->setCustomProperty( "labeling/fontWeight", textFont.weight() );
1121  layer->setCustomProperty( "labeling/fontItalic", textFont.italic() );
1122  layer->setCustomProperty( "labeling/fontStrikeout", textFont.strikeOut() );
1123  layer->setCustomProperty( "labeling/fontUnderline", textFont.underline() );
1124  _writeColor( layer, "labeling/textColor", textColor );
1125  layer->setCustomProperty( "labeling/fontCapitals", static_cast< unsigned int >( textFont.capitalization() ) );
1126  layer->setCustomProperty( "labeling/fontLetterSpacing", textFont.letterSpacing() );
1127  layer->setCustomProperty( "labeling/fontWordSpacing", textFont.wordSpacing() );
1128  layer->setCustomProperty( "labeling/textTransp", textTransp );
1129  layer->setCustomProperty( "labeling/blendMode", QgsMapRenderer::getBlendModeEnum( blendMode ) );
1130  layer->setCustomProperty( "labeling/previewBkgrdColor", previewBkgrdColor.name() );
1131 
1132  // text formatting
1133  layer->setCustomProperty( "labeling/wrapChar", wrapChar );
1134  layer->setCustomProperty( "labeling/multilineHeight", multilineHeight );
1135  layer->setCustomProperty( "labeling/multilineAlign", static_cast< unsigned int >( multilineAlign ) );
1136  layer->setCustomProperty( "labeling/addDirectionSymbol", addDirectionSymbol );
1137  layer->setCustomProperty( "labeling/leftDirectionSymbol", leftDirectionSymbol );
1138  layer->setCustomProperty( "labeling/rightDirectionSymbol", rightDirectionSymbol );
1139  layer->setCustomProperty( "labeling/reverseDirectionSymbol", reverseDirectionSymbol );
1140  layer->setCustomProperty( "labeling/placeDirectionSymbol", static_cast< unsigned int >( placeDirectionSymbol ) );
1141  layer->setCustomProperty( "labeling/formatNumbers", formatNumbers );
1142  layer->setCustomProperty( "labeling/decimals", decimals );
1143  layer->setCustomProperty( "labeling/plussign", plusSign );
1144 
1145  // text buffer
1146  layer->setCustomProperty( "labeling/bufferDraw", bufferDraw );
1147  layer->setCustomProperty( "labeling/bufferSize", bufferSize );
1148  layer->setCustomProperty( "labeling/bufferSizeInMapUnits", bufferSizeInMapUnits );
1149  layer->setCustomProperty( "labeling/bufferSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( bufferSizeMapUnitScale ) );
1150  _writeColor( layer, "labeling/bufferColor", bufferColor );
1151  layer->setCustomProperty( "labeling/bufferNoFill", bufferNoFill );
1152  layer->setCustomProperty( "labeling/bufferTransp", bufferTransp );
1153  layer->setCustomProperty( "labeling/bufferJoinStyle", static_cast< unsigned int >( bufferJoinStyle ) );
1154  layer->setCustomProperty( "labeling/bufferBlendMode", QgsMapRenderer::getBlendModeEnum( bufferBlendMode ) );
1155 
1156  // background
1157  layer->setCustomProperty( "labeling/shapeDraw", shapeDraw );
1158  layer->setCustomProperty( "labeling/shapeType", static_cast< unsigned int >( shapeType ) );
1159  layer->setCustomProperty( "labeling/shapeSVGFile", shapeSVGFile );
1160  layer->setCustomProperty( "labeling/shapeSizeType", static_cast< unsigned int >( shapeSizeType ) );
1161  layer->setCustomProperty( "labeling/shapeSizeX", shapeSize.x() );
1162  layer->setCustomProperty( "labeling/shapeSizeY", shapeSize.y() );
1163  layer->setCustomProperty( "labeling/shapeSizeUnits", static_cast< unsigned int >( shapeSizeUnits ) );
1164  layer->setCustomProperty( "labeling/shapeSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeSizeMapUnitScale ) );
1165  layer->setCustomProperty( "labeling/shapeRotationType", static_cast< unsigned int >( shapeRotationType ) );
1166  layer->setCustomProperty( "labeling/shapeRotation", shapeRotation );
1167  layer->setCustomProperty( "labeling/shapeOffsetX", shapeOffset.x() );
1168  layer->setCustomProperty( "labeling/shapeOffsetY", shapeOffset.y() );
1169  layer->setCustomProperty( "labeling/shapeOffsetUnits", static_cast< unsigned int >( shapeOffsetUnits ) );
1170  layer->setCustomProperty( "labeling/shapeOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeOffsetMapUnitScale ) );
1171  layer->setCustomProperty( "labeling/shapeRadiiX", shapeRadii.x() );
1172  layer->setCustomProperty( "labeling/shapeRadiiY", shapeRadii.y() );
1173  layer->setCustomProperty( "labeling/shapeRadiiUnits", static_cast< unsigned int >( shapeRadiiUnits ) );
1174  layer->setCustomProperty( "labeling/shapeRadiiMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeRadiiMapUnitScale ) );
1175  _writeColor( layer, "labeling/shapeFillColor", shapeFillColor, true );
1176  _writeColor( layer, "labeling/shapeBorderColor", shapeBorderColor, true );
1177  layer->setCustomProperty( "labeling/shapeBorderWidth", shapeBorderWidth );
1178  layer->setCustomProperty( "labeling/shapeBorderWidthUnits", static_cast< unsigned int >( shapeBorderWidthUnits ) );
1179  layer->setCustomProperty( "labeling/shapeBorderWidthMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeBorderWidthMapUnitScale ) );
1180  layer->setCustomProperty( "labeling/shapeJoinStyle", static_cast< unsigned int >( shapeJoinStyle ) );
1181  layer->setCustomProperty( "labeling/shapeTransparency", shapeTransparency );
1182  layer->setCustomProperty( "labeling/shapeBlendMode", QgsMapRenderer::getBlendModeEnum( shapeBlendMode ) );
1183 
1184  // drop shadow
1185  layer->setCustomProperty( "labeling/shadowDraw", shadowDraw );
1186  layer->setCustomProperty( "labeling/shadowUnder", static_cast< unsigned int >( shadowUnder ) );
1187  layer->setCustomProperty( "labeling/shadowOffsetAngle", shadowOffsetAngle );
1188  layer->setCustomProperty( "labeling/shadowOffsetDist", shadowOffsetDist );
1189  layer->setCustomProperty( "labeling/shadowOffsetUnits", static_cast< unsigned int >( shadowOffsetUnits ) );
1190  layer->setCustomProperty( "labeling/shadowOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowOffsetMapUnitScale ) );
1191  layer->setCustomProperty( "labeling/shadowOffsetGlobal", shadowOffsetGlobal );
1192  layer->setCustomProperty( "labeling/shadowRadius", shadowRadius );
1193  layer->setCustomProperty( "labeling/shadowRadiusUnits", static_cast< unsigned int >( shadowRadiusUnits ) );
1194  layer->setCustomProperty( "labeling/shadowRadiusMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowRadiusMapUnitScale ) );
1195  layer->setCustomProperty( "labeling/shadowRadiusAlphaOnly", shadowRadiusAlphaOnly );
1196  layer->setCustomProperty( "labeling/shadowTransparency", shadowTransparency );
1197  layer->setCustomProperty( "labeling/shadowScale", shadowScale );
1198  _writeColor( layer, "labeling/shadowColor", shadowColor, false );
1199  layer->setCustomProperty( "labeling/shadowBlendMode", QgsMapRenderer::getBlendModeEnum( shadowBlendMode ) );
1200 
1201  // placement
1202  layer->setCustomProperty( "labeling/placement", placement );
1203  layer->setCustomProperty( "labeling/placementFlags", static_cast< unsigned int >( placementFlags ) );
1204  layer->setCustomProperty( "labeling/centroidWhole", centroidWhole );
1205  layer->setCustomProperty( "labeling/centroidInside", centroidInside );
1206  layer->setCustomProperty( "labeling/predefinedPositionOrder", QgsLabelingUtils::encodePredefinedPositionOrder( predefinedPositionOrder ) );
1207  layer->setCustomProperty( "labeling/fitInPolygonOnly", fitInPolygonOnly );
1208  layer->setCustomProperty( "labeling/dist", dist );
1209  layer->setCustomProperty( "labeling/distInMapUnits", distInMapUnits );
1210  layer->setCustomProperty( "labeling/distMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( distMapUnitScale ) );
1211  layer->setCustomProperty( "labeling/offsetType", static_cast< unsigned int >( offsetType ) );
1212  layer->setCustomProperty( "labeling/quadOffset", static_cast< unsigned int >( quadOffset ) );
1213  layer->setCustomProperty( "labeling/xOffset", xOffset );
1214  layer->setCustomProperty( "labeling/yOffset", yOffset );
1215  layer->setCustomProperty( "labeling/labelOffsetInMapUnits", labelOffsetInMapUnits );
1216  layer->setCustomProperty( "labeling/labelOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( labelOffsetMapUnitScale ) );
1217  layer->setCustomProperty( "labeling/angleOffset", angleOffset );
1218  layer->setCustomProperty( "labeling/preserveRotation", preserveRotation );
1219  layer->setCustomProperty( "labeling/maxCurvedCharAngleIn", maxCurvedCharAngleIn );
1220  layer->setCustomProperty( "labeling/maxCurvedCharAngleOut", maxCurvedCharAngleOut );
1221  layer->setCustomProperty( "labeling/priority", priority );
1222  layer->setCustomProperty( "labeling/repeatDistance", repeatDistance );
1223  layer->setCustomProperty( "labeling/repeatDistanceUnit", repeatDistanceUnit );
1224  layer->setCustomProperty( "labeling/repeatDistanceMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( repeatDistanceMapUnitScale ) );
1225 
1226  // rendering
1227  layer->setCustomProperty( "labeling/scaleVisibility", scaleVisibility );
1228  layer->setCustomProperty( "labeling/scaleMin", scaleMin );
1229  layer->setCustomProperty( "labeling/scaleMax", scaleMax );
1230  layer->setCustomProperty( "labeling/fontLimitPixelSize", fontLimitPixelSize );
1231  layer->setCustomProperty( "labeling/fontMinPixelSize", fontMinPixelSize );
1232  layer->setCustomProperty( "labeling/fontMaxPixelSize", fontMaxPixelSize );
1233  layer->setCustomProperty( "labeling/displayAll", displayAll );
1234  layer->setCustomProperty( "labeling/upsidedownLabels", static_cast< unsigned int >( upsidedownLabels ) );
1235 
1236  layer->setCustomProperty( "labeling/labelPerPart", labelPerPart );
1237  layer->setCustomProperty( "labeling/mergeLines", mergeLines );
1238  layer->setCustomProperty( "labeling/minFeatureSize", minFeatureSize );
1239  layer->setCustomProperty( "labeling/limitNumLabels", limitNumLabels );
1240  layer->setCustomProperty( "labeling/maxNumLabels", maxNumLabels );
1241  layer->setCustomProperty( "labeling/obstacle", obstacle );
1242  layer->setCustomProperty( "labeling/obstacleFactor", obstacleFactor );
1243  layer->setCustomProperty( "labeling/obstacleType", static_cast< unsigned int >( obstacleType ) );
1244  layer->setCustomProperty( "labeling/zIndex", zIndex );
1245 
1246  writeDataDefinedPropertyMap( layer, nullptr, dataDefinedProperties );
1247 }
1248 
1249 
1250 
1252 {
1253  enabled = true;
1254  drawLabels = true;
1255 
1256  // text style
1257  QDomElement textStyleElem = elem.firstChildElement( "text-style" );
1258  fieldName = textStyleElem.attribute( "fieldName" );
1259  isExpression = textStyleElem.attribute( "isExpression" ).toInt();
1260  QFont appFont = QApplication::font();
1261  mTextFontFamily = textStyleElem.attribute( "fontFamily", appFont.family() );
1262  QString fontFamily = mTextFontFamily;
1264  {
1265  // trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
1266  mTextFontFound = false;
1267 
1268  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
1269  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
1270 
1271  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
1272  fontFamily = appFont.family();
1273  }
1274 
1275  double fontSize = textStyleElem.attribute( "fontSize" ).toDouble();
1276  fontSizeInMapUnits = textStyleElem.attribute( "fontSizeInMapUnits" ).toInt();
1277  if ( !textStyleElem.hasAttribute( "fontSizeMapUnitScale" ) )
1278  {
1279  //fallback to older property
1280  fontSizeMapUnitScale.minScale = textStyleElem.attribute( "fontSizeMapUnitMinScale", "0" ).toDouble();
1281  fontSizeMapUnitScale.maxScale = textStyleElem.attribute( "fontSizeMapUnitMaxScale", "0" ).toDouble();
1282  }
1283  else
1284  {
1285  fontSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( textStyleElem.attribute( "fontSizeMapUnitScale" ) );
1286  }
1287  int fontWeight = textStyleElem.attribute( "fontWeight" ).toInt();
1288  bool fontItalic = textStyleElem.attribute( "fontItalic" ).toInt();
1289  textFont = QFont( fontFamily, fontSize, fontWeight, fontItalic );
1290  textFont.setPointSizeF( fontSize ); //double precision needed because of map units
1291  textNamedStyle = QgsFontUtils::translateNamedStyle( textStyleElem.attribute( "namedStyle" ) );
1292  QgsFontUtils::updateFontViaStyle( textFont, textNamedStyle ); // must come after textFont.setPointSizeF()
1293  textFont.setCapitalization( static_cast< QFont::Capitalization >( textStyleElem.attribute( "fontCapitals", "0" ).toUInt() ) );
1294  textFont.setUnderline( textStyleElem.attribute( "fontUnderline" ).toInt() );
1295  textFont.setStrikeOut( textStyleElem.attribute( "fontStrikeout" ).toInt() );
1296  textFont.setLetterSpacing( QFont::AbsoluteSpacing, textStyleElem.attribute( "fontLetterSpacing", "0" ).toDouble() );
1297  textFont.setWordSpacing( textStyleElem.attribute( "fontWordSpacing", "0" ).toDouble() );
1298  textColor = QgsSymbolLayerV2Utils::decodeColor( textStyleElem.attribute( "textColor", QgsSymbolLayerV2Utils::encodeColor( Qt::black ) ) );
1299  textTransp = textStyleElem.attribute( "textTransp" ).toInt();
1301  static_cast< QgsMapRenderer::BlendMode >( textStyleElem.attribute( "blendMode", QString::number( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
1302  previewBkgrdColor = QColor( textStyleElem.attribute( "previewBkgrdColor", "#ffffff" ) );
1303 
1304 
1305  // text formatting
1306  QDomElement textFormatElem = elem.firstChildElement( "text-format" );
1307  wrapChar = textFormatElem.attribute( "wrapChar" );
1308  multilineHeight = textFormatElem.attribute( "multilineHeight", "1" ).toDouble();
1309  multilineAlign = static_cast< MultiLineAlign >( textFormatElem.attribute( "multilineAlign", QString::number( MultiLeft ) ).toUInt() );
1310  addDirectionSymbol = textFormatElem.attribute( "addDirectionSymbol" ).toInt();
1311  leftDirectionSymbol = textFormatElem.attribute( "leftDirectionSymbol", "<" );
1312  rightDirectionSymbol = textFormatElem.attribute( "rightDirectionSymbol", ">" );
1313  reverseDirectionSymbol = textFormatElem.attribute( "reverseDirectionSymbol" ).toInt();
1314  placeDirectionSymbol = static_cast< DirectionSymbols >( textFormatElem.attribute( "placeDirectionSymbol", QString::number( SymbolLeftRight ) ).toUInt() );
1315  formatNumbers = textFormatElem.attribute( "formatNumbers" ).toInt();
1316  decimals = textFormatElem.attribute( "decimals" ).toInt();
1317  plusSign = textFormatElem.attribute( "plussign" ).toInt();
1318 
1319  // text buffer
1320  QDomElement textBufferElem = elem.firstChildElement( "text-buffer" );
1321  double bufSize = textBufferElem.attribute( "bufferSize", "0" ).toDouble();
1322 
1323  // fix for buffer being keyed off of just its size in the past (<2.0)
1324  QVariant drawBuffer = textBufferElem.attribute( "bufferDraw" );
1325  if ( drawBuffer.isValid() )
1326  {
1327  bufferDraw = drawBuffer.toBool();
1328  bufferSize = bufSize;
1329  }
1330  else if ( bufSize != 0.0 )
1331  {
1332  bufferDraw = true;
1333  bufferSize = bufSize;
1334  }
1335  else
1336  {
1337  // keep bufferSize at new 1.0 default
1338  bufferDraw = false;
1339  }
1340 
1341  bufferSizeInMapUnits = textBufferElem.attribute( "bufferSizeInMapUnits" ).toInt();
1342  if ( !textBufferElem.hasAttribute( "bufferSizeMapUnitScale" ) )
1343  {
1344  //fallback to older property
1345  bufferSizeMapUnitScale.minScale = textBufferElem.attribute( "bufferSizeMapUnitMinScale", "0" ).toDouble();
1346  bufferSizeMapUnitScale.maxScale = textBufferElem.attribute( "bufferSizeMapUnitMaxScale", "0" ).toDouble();
1347  }
1348  else
1349  {
1350  bufferSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( textBufferElem.attribute( "bufferSizeMapUnitScale" ) );
1351  }
1352  bufferColor = QgsSymbolLayerV2Utils::decodeColor( textBufferElem.attribute( "bufferColor", QgsSymbolLayerV2Utils::encodeColor( Qt::white ) ) );
1353  bufferTransp = textBufferElem.attribute( "bufferTransp" ).toInt();
1355  static_cast< QgsMapRenderer::BlendMode >( textBufferElem.attribute( "bufferBlendMode", QString::number( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
1356  bufferJoinStyle = static_cast< Qt::PenJoinStyle >( textBufferElem.attribute( "bufferJoinStyle", QString::number( Qt::BevelJoin ) ).toUInt() );
1357  bufferNoFill = textBufferElem.attribute( "bufferNoFill", "0" ).toInt();
1358 
1359  // background
1360  QDomElement backgroundElem = elem.firstChildElement( "background" );
1361  shapeDraw = backgroundElem.attribute( "shapeDraw", "0" ).toInt();
1362  shapeType = static_cast< ShapeType >( backgroundElem.attribute( "shapeType", QString::number( ShapeRectangle ) ).toUInt() );
1363  shapeSVGFile = backgroundElem.attribute( "shapeSVGFile" );
1364  shapeSizeType = static_cast< SizeType >( backgroundElem.attribute( "shapeSizeType", QString::number( SizeBuffer ) ).toUInt() );
1365  shapeSize = QPointF( backgroundElem.attribute( "shapeSizeX", "0" ).toDouble(),
1366  backgroundElem.attribute( "shapeSizeY", "0" ).toDouble() );
1367  shapeSizeUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeSizeUnits", QString::number( MM ) ).toUInt() );
1368  if ( !backgroundElem.hasAttribute( "shapeSizeMapUnitScale" ) )
1369  {
1370  //fallback to older property
1371  shapeSizeMapUnitScale.minScale = backgroundElem.attribute( "shapeSizeMapUnitMinScale", "0" ).toDouble();
1372  shapeSizeMapUnitScale.maxScale = backgroundElem.attribute( "shapeSizeMapUnitMaxScale", "0" ).toDouble();
1373  }
1374  else
1375  {
1376  shapeSizeMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeSizeMapUnitScale" ) );
1377  }
1378  shapeRotationType = static_cast< RotationType >( backgroundElem.attribute( "shapeRotationType", QString::number( RotationSync ) ).toUInt() );
1379  shapeRotation = backgroundElem.attribute( "shapeRotation", "0" ).toDouble();
1380  shapeOffset = QPointF( backgroundElem.attribute( "shapeOffsetX", "0" ).toDouble(),
1381  backgroundElem.attribute( "shapeOffsetY", "0" ).toDouble() );
1382  shapeOffsetUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeOffsetUnits", QString::number( MM ) ).toUInt() );
1383  if ( !backgroundElem.hasAttribute( "shapeOffsetMapUnitScale" ) )
1384  {
1385  //fallback to older property
1386  shapeOffsetMapUnitScale.minScale = backgroundElem.attribute( "shapeOffsetMapUnitMinScale", "0" ).toDouble();
1387  shapeOffsetMapUnitScale.maxScale = backgroundElem.attribute( "shapeOffsetMapUnitMaxScale", "0" ).toDouble();
1388  }
1389  else
1390  {
1391  shapeOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeOffsetMapUnitScale" ) );
1392  }
1393  shapeRadii = QPointF( backgroundElem.attribute( "shapeRadiiX", "0" ).toDouble(),
1394  backgroundElem.attribute( "shapeRadiiY", "0" ).toDouble() );
1395  shapeRadiiUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeRadiiUnits", QString::number( MM ) ).toUInt() );
1396  if ( !backgroundElem.hasAttribute( "shapeRadiiMapUnitScale" ) )
1397  {
1398  //fallback to older property
1399  shapeRadiiMapUnitScale.minScale = backgroundElem.attribute( "shapeRadiiMapUnitMinScale", "0" ).toDouble();
1400  shapeRadiiMapUnitScale.maxScale = backgroundElem.attribute( "shapeRadiiMapUnitMaxScale", "0" ).toDouble();
1401  }
1402  else
1403  {
1404  shapeRadiiMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeRadiiMapUnitScale" ) );
1405  }
1406  shapeFillColor = QgsSymbolLayerV2Utils::decodeColor( backgroundElem.attribute( "shapeFillColor", QgsSymbolLayerV2Utils::encodeColor( Qt::white ) ) );
1407  shapeBorderColor = QgsSymbolLayerV2Utils::decodeColor( backgroundElem.attribute( "shapeBorderColor", QgsSymbolLayerV2Utils::encodeColor( Qt::darkGray ) ) );
1408  shapeBorderWidth = backgroundElem.attribute( "shapeBorderWidth", "0" ).toDouble();
1409  shapeBorderWidthUnits = static_cast< SizeUnit >( backgroundElem.attribute( "shapeBorderWidthUnits", QString::number( MM ) ).toUInt() );
1410  if ( !backgroundElem.hasAttribute( "shapeBorderWidthMapUnitScale" ) )
1411  {
1412  //fallback to older property
1413  shapeBorderWidthMapUnitScale.minScale = backgroundElem.attribute( "shapeBorderWidthMapUnitMinScale", "0" ).toDouble();
1414  shapeBorderWidthMapUnitScale.maxScale = backgroundElem.attribute( "shapeBorderWidthMapUnitMaxScale", "0" ).toDouble();
1415  }
1416  else
1417  {
1418  shapeBorderWidthMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( backgroundElem.attribute( "shapeBorderWidthMapUnitScale" ) );
1419  }
1420  shapeJoinStyle = static_cast< Qt::PenJoinStyle >( backgroundElem.attribute( "shapeJoinStyle", QString::number( Qt::BevelJoin ) ).toUInt() );
1421  shapeTransparency = backgroundElem.attribute( "shapeTransparency", "0" ).toInt();
1423  static_cast< QgsMapRenderer::BlendMode >( backgroundElem.attribute( "shapeBlendMode", QString::number( QgsMapRenderer::BlendNormal ) ).toUInt() ) );
1424 
1425  // drop shadow
1426  QDomElement shadowElem = elem.firstChildElement( "shadow" );
1427  shadowDraw = shadowElem.attribute( "shadowDraw", "0" ).toInt();
1428  shadowUnder = static_cast< ShadowType >( shadowElem.attribute( "shadowUnder", QString::number( ShadowLowest ) ).toUInt() );//ShadowLowest ;
1429  shadowOffsetAngle = shadowElem.attribute( "shadowOffsetAngle", "135" ).toInt();
1430  shadowOffsetDist = shadowElem.attribute( "shadowOffsetDist", "1" ).toDouble();
1431  shadowOffsetUnits = static_cast< SizeUnit >( shadowElem.attribute( "shadowOffsetUnits", QString::number( MM ) ).toUInt() );
1432  if ( !shadowElem.hasAttribute( "shadowOffsetMapUnitScale" ) )
1433  {
1434  //fallback to older property
1435  shadowOffsetMapUnitScale.minScale = shadowElem.attribute( "shadowOffsetMapUnitMinScale", "0" ).toDouble();
1436  shadowOffsetMapUnitScale.maxScale = shadowElem.attribute( "shadowOffsetMapUnitMaxScale", "0" ).toDouble();
1437  }
1438  else
1439  {
1440  shadowOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( shadowElem.attribute( "shadowOffsetMapUnitScale" ) );
1441  }
1442  shadowOffsetGlobal = shadowElem.attribute( "shadowOffsetGlobal", "1" ).toInt();
1443  shadowRadius = shadowElem.attribute( "shadowRadius", "1.5" ).toDouble();
1444  shadowRadiusUnits = static_cast< SizeUnit >( shadowElem.attribute( "shadowRadiusUnits", QString::number( MM ) ).toUInt() );
1445  if ( !shadowElem.hasAttribute( "shadowRadiusMapUnitScale" ) )
1446  {
1447  //fallback to older property
1448  shadowRadiusMapUnitScale.minScale = shadowElem.attribute( "shadowRadiusMapUnitMinScale", "0" ).toDouble();
1449  shadowRadiusMapUnitScale.maxScale = shadowElem.attribute( "shadowRadiusMapUnitMaxScale", "0" ).toDouble();
1450  }
1451  else
1452  {
1453  shadowRadiusMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( shadowElem.attribute( "shadowRadiusMapUnitScale" ) );
1454  }
1455  shadowRadiusAlphaOnly = shadowElem.attribute( "shadowRadiusAlphaOnly", "0" ).toInt();
1456  shadowTransparency = shadowElem.attribute( "shadowTransparency", "30" ).toInt();
1457  shadowScale = shadowElem.attribute( "shadowScale", "100" ).toInt();
1458  shadowColor = QgsSymbolLayerV2Utils::decodeColor( shadowElem.attribute( "shadowColor", QgsSymbolLayerV2Utils::encodeColor( Qt::black ) ) );
1460  static_cast< QgsMapRenderer::BlendMode >( shadowElem.attribute( "shadowBlendMode", QString::number( QgsMapRenderer::BlendMultiply ) ).toUInt() ) );
1461 
1462  // placement
1463  QDomElement placementElem = elem.firstChildElement( "placement" );
1464  placement = static_cast< Placement >( placementElem.attribute( "placement" ).toInt() );
1465  placementFlags = placementElem.attribute( "placementFlags" ).toUInt();
1466  centroidWhole = placementElem.attribute( "centroidWhole", "0" ).toInt();
1467  centroidInside = placementElem.attribute( "centroidInside", "0" ).toInt();
1468  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( placementElem.attribute( "predefinedPositionOrder" ) );
1470  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
1471  fitInPolygonOnly = placementElem.attribute( "fitInPolygonOnly", "0" ).toInt();
1472  dist = placementElem.attribute( "dist" ).toDouble();
1473  distInMapUnits = placementElem.attribute( "distInMapUnits" ).toInt();
1474  if ( !placementElem.hasAttribute( "distMapUnitScale" ) )
1475  {
1476  //fallback to older property
1477  distMapUnitScale.minScale = placementElem.attribute( "distMapUnitMinScale", "0" ).toDouble();
1478  distMapUnitScale.maxScale = placementElem.attribute( "distMapUnitMaxScale", "0" ).toDouble();
1479  }
1480  else
1481  {
1482  distMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( placementElem.attribute( "distMapUnitScale" ) );
1483  }
1484  offsetType = static_cast< OffsetType >( placementElem.attribute( "offsetType", QString::number( FromPoint ) ).toUInt() );
1485  quadOffset = static_cast< QuadrantPosition >( placementElem.attribute( "quadOffset", QString::number( QuadrantOver ) ).toUInt() );
1486  xOffset = placementElem.attribute( "xOffset", "0" ).toDouble();
1487  yOffset = placementElem.attribute( "yOffset", "0" ).toDouble();
1488  labelOffsetInMapUnits = placementElem.attribute( "labelOffsetInMapUnits", "1" ).toInt();
1489  if ( !placementElem.hasAttribute( "labelOffsetMapUnitScale" ) )
1490  {
1491  //fallback to older property
1492  labelOffsetMapUnitScale.minScale = placementElem.attribute( "labelOffsetMapUnitMinScale", "0" ).toDouble();
1493  labelOffsetMapUnitScale.maxScale = placementElem.attribute( "labelOffsetMapUnitMaxScale", "0" ).toDouble();
1494  }
1495  else
1496  {
1497  labelOffsetMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( placementElem.attribute( "labelOffsetMapUnitScale" ) );
1498  }
1499  angleOffset = placementElem.attribute( "angleOffset", "0" ).toDouble();
1500  preserveRotation = placementElem.attribute( "preserveRotation", "1" ).toInt();
1501  maxCurvedCharAngleIn = placementElem.attribute( "maxCurvedCharAngleIn", "20" ).toDouble();
1502  maxCurvedCharAngleOut = placementElem.attribute( "maxCurvedCharAngleOut", "-20" ).toDouble();
1503  priority = placementElem.attribute( "priority" ).toInt();
1504  repeatDistance = placementElem.attribute( "repeatDistance", "0" ).toDouble();
1505  repeatDistanceUnit = static_cast< SizeUnit >( placementElem.attribute( "repeatDistanceUnit", QString::number( MM ) ).toUInt() );
1506  if ( !placementElem.hasAttribute( "repeatDistanceMapUnitScale" ) )
1507  {
1508  //fallback to older property
1509  repeatDistanceMapUnitScale.minScale = placementElem.attribute( "repeatDistanceMapUnitMinScale", "0" ).toDouble();
1510  repeatDistanceMapUnitScale.maxScale = placementElem.attribute( "repeatDistanceMapUnitMaxScale", "0" ).toDouble();
1511  }
1512  else
1513  {
1514  repeatDistanceMapUnitScale = QgsSymbolLayerV2Utils::decodeMapUnitScale( placementElem.attribute( "repeatDistanceMapUnitScale" ) );
1515  }
1516 
1517  // rendering
1518  QDomElement renderingElem = elem.firstChildElement( "rendering" );
1519  scaleMin = renderingElem.attribute( "scaleMin", "0" ).toInt();
1520  scaleMax = renderingElem.attribute( "scaleMax", "0" ).toInt();
1521  scaleVisibility = renderingElem.attribute( "scaleVisibility" ).toInt();
1522 
1523  fontLimitPixelSize = renderingElem.attribute( "fontLimitPixelSize", "0" ).toInt();
1524  fontMinPixelSize = renderingElem.attribute( "fontMinPixelSize", "0" ).toInt();
1525  fontMaxPixelSize = renderingElem.attribute( "fontMaxPixelSize", "10000" ).toInt();
1526  displayAll = renderingElem.attribute( "displayAll", "0" ).toInt();
1527  upsidedownLabels = static_cast< UpsideDownLabels >( renderingElem.attribute( "upsidedownLabels", QString::number( Upright ) ).toUInt() );
1528 
1529  labelPerPart = renderingElem.attribute( "labelPerPart" ).toInt();
1530  mergeLines = renderingElem.attribute( "mergeLines" ).toInt();
1531  minFeatureSize = renderingElem.attribute( "minFeatureSize" ).toDouble();
1532  limitNumLabels = renderingElem.attribute( "limitNumLabels", "0" ).toInt();
1533  maxNumLabels = renderingElem.attribute( "maxNumLabels", "2000" ).toInt();
1534  obstacle = renderingElem.attribute( "obstacle", "1" ).toInt();
1535  obstacleFactor = renderingElem.attribute( "obstacleFactor", "1" ).toDouble();
1536  obstacleType = static_cast< ObstacleType >( renderingElem.attribute( "obstacleType", QString::number( PolygonInterior ) ).toUInt() );
1537  zIndex = renderingElem.attribute( "zIndex", "0.0" ).toDouble();
1538 
1539  QDomElement ddElem = elem.firstChildElement( "data-defined" );
1540  readDataDefinedPropertyMap( nullptr, &ddElem, dataDefinedProperties );
1541 }
1542 
1543 
1544 
1546 {
1547  // we assume (enabled == true && drawLabels == true) so those are not saved
1548 
1549  // text style
1550  QDomElement textStyleElem = doc.createElement( "text-style" );
1551  textStyleElem.setAttribute( "fieldName", fieldName );
1552  textStyleElem.setAttribute( "isExpression", isExpression );
1553  textStyleElem.setAttribute( "fontFamily", textFont.family() );
1554  textStyleElem.setAttribute( "namedStyle", QgsFontUtils::untranslateNamedStyle( textNamedStyle ) );
1555  textStyleElem.setAttribute( "fontSize", textFont.pointSizeF() );
1556  textStyleElem.setAttribute( "fontSizeInMapUnits", fontSizeInMapUnits );
1557  textStyleElem.setAttribute( "fontSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( fontSizeMapUnitScale ) );
1558  textStyleElem.setAttribute( "fontWeight", textFont.weight() );
1559  textStyleElem.setAttribute( "fontItalic", textFont.italic() );
1560  textStyleElem.setAttribute( "fontStrikeout", textFont.strikeOut() );
1561  textStyleElem.setAttribute( "fontUnderline", textFont.underline() );
1562  textStyleElem.setAttribute( "textColor", QgsSymbolLayerV2Utils::encodeColor( textColor ) );
1563  textStyleElem.setAttribute( "fontCapitals", static_cast< unsigned int >( textFont.capitalization() ) );
1564  textStyleElem.setAttribute( "fontLetterSpacing", textFont.letterSpacing() );
1565  textStyleElem.setAttribute( "fontWordSpacing", textFont.wordSpacing() );
1566  textStyleElem.setAttribute( "textTransp", textTransp );
1567  textStyleElem.setAttribute( "blendMode", QgsMapRenderer::getBlendModeEnum( blendMode ) );
1568  textStyleElem.setAttribute( "previewBkgrdColor", previewBkgrdColor.name() );
1569 
1570  // text formatting
1571  QDomElement textFormatElem = doc.createElement( "text-format" );
1572  textFormatElem.setAttribute( "wrapChar", wrapChar );
1573  textFormatElem.setAttribute( "multilineHeight", multilineHeight );
1574  textFormatElem.setAttribute( "multilineAlign", static_cast< unsigned int >( multilineAlign ) );
1575  textFormatElem.setAttribute( "addDirectionSymbol", addDirectionSymbol );
1576  textFormatElem.setAttribute( "leftDirectionSymbol", leftDirectionSymbol );
1577  textFormatElem.setAttribute( "rightDirectionSymbol", rightDirectionSymbol );
1578  textFormatElem.setAttribute( "reverseDirectionSymbol", reverseDirectionSymbol );
1579  textFormatElem.setAttribute( "placeDirectionSymbol", static_cast< unsigned int >( placeDirectionSymbol ) );
1580  textFormatElem.setAttribute( "formatNumbers", formatNumbers );
1581  textFormatElem.setAttribute( "decimals", decimals );
1582  textFormatElem.setAttribute( "plussign", plusSign );
1583 
1584  // text buffer
1585  QDomElement textBufferElem = doc.createElement( "text-buffer" );
1586  textBufferElem.setAttribute( "bufferDraw", bufferDraw );
1587  textBufferElem.setAttribute( "bufferSize", bufferSize );
1588  textBufferElem.setAttribute( "bufferSizeInMapUnits", bufferSizeInMapUnits );
1589  textBufferElem.setAttribute( "bufferSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( bufferSizeMapUnitScale ) );
1590  textBufferElem.setAttribute( "bufferColor", QgsSymbolLayerV2Utils::encodeColor( bufferColor ) );
1591  textBufferElem.setAttribute( "bufferNoFill", bufferNoFill );
1592  textBufferElem.setAttribute( "bufferTransp", bufferTransp );
1593  textBufferElem.setAttribute( "bufferJoinStyle", static_cast< unsigned int >( bufferJoinStyle ) );
1594  textBufferElem.setAttribute( "bufferBlendMode", QgsMapRenderer::getBlendModeEnum( bufferBlendMode ) );
1595 
1596  // background
1597  QDomElement backgroundElem = doc.createElement( "background" );
1598  backgroundElem.setAttribute( "shapeDraw", shapeDraw );
1599  backgroundElem.setAttribute( "shapeType", static_cast< unsigned int >( shapeType ) );
1600  backgroundElem.setAttribute( "shapeSVGFile", shapeSVGFile );
1601  backgroundElem.setAttribute( "shapeSizeType", static_cast< unsigned int >( shapeSizeType ) );
1602  backgroundElem.setAttribute( "shapeSizeX", shapeSize.x() );
1603  backgroundElem.setAttribute( "shapeSizeY", shapeSize.y() );
1604  backgroundElem.setAttribute( "shapeSizeUnits", static_cast< unsigned int >( shapeSizeUnits ) );
1605  backgroundElem.setAttribute( "shapeSizeMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeSizeMapUnitScale ) );
1606  backgroundElem.setAttribute( "shapeRotationType", static_cast< unsigned int >( shapeRotationType ) );
1607  backgroundElem.setAttribute( "shapeRotation", shapeRotation );
1608  backgroundElem.setAttribute( "shapeOffsetX", shapeOffset.x() );
1609  backgroundElem.setAttribute( "shapeOffsetY", shapeOffset.y() );
1610  backgroundElem.setAttribute( "shapeOffsetUnits", static_cast< unsigned int >( shapeOffsetUnits ) );
1611  backgroundElem.setAttribute( "shapeOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeOffsetMapUnitScale ) );
1612  backgroundElem.setAttribute( "shapeRadiiX", shapeRadii.x() );
1613  backgroundElem.setAttribute( "shapeRadiiY", shapeRadii.y() );
1614  backgroundElem.setAttribute( "shapeRadiiUnits", static_cast< unsigned int >( shapeRadiiUnits ) );
1615  backgroundElem.setAttribute( "shapeRadiiMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeRadiiMapUnitScale ) );
1616  backgroundElem.setAttribute( "shapeFillColor", QgsSymbolLayerV2Utils::encodeColor( shapeFillColor ) );
1617  backgroundElem.setAttribute( "shapeBorderColor", QgsSymbolLayerV2Utils::encodeColor( shapeBorderColor ) );
1618  backgroundElem.setAttribute( "shapeBorderWidth", shapeBorderWidth );
1619  backgroundElem.setAttribute( "shapeBorderWidthUnits", static_cast< unsigned int >( shapeBorderWidthUnits ) );
1620  backgroundElem.setAttribute( "shapeBorderWidthMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shapeBorderWidthMapUnitScale ) );
1621  backgroundElem.setAttribute( "shapeJoinStyle", static_cast< unsigned int >( shapeJoinStyle ) );
1622  backgroundElem.setAttribute( "shapeTransparency", shapeTransparency );
1623  backgroundElem.setAttribute( "shapeBlendMode", QgsMapRenderer::getBlendModeEnum( shapeBlendMode ) );
1624 
1625  // drop shadow
1626  QDomElement shadowElem = doc.createElement( "shadow" );
1627  shadowElem.setAttribute( "shadowDraw", shadowDraw );
1628  shadowElem.setAttribute( "shadowUnder", static_cast< unsigned int >( shadowUnder ) );
1629  shadowElem.setAttribute( "shadowOffsetAngle", shadowOffsetAngle );
1630  shadowElem.setAttribute( "shadowOffsetDist", shadowOffsetDist );
1631  shadowElem.setAttribute( "shadowOffsetUnits", static_cast< unsigned int >( shadowOffsetUnits ) );
1632  shadowElem.setAttribute( "shadowOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowOffsetMapUnitScale ) );
1633  shadowElem.setAttribute( "shadowOffsetGlobal", shadowOffsetGlobal );
1634  shadowElem.setAttribute( "shadowRadius", shadowRadius );
1635  shadowElem.setAttribute( "shadowRadiusUnits", static_cast< unsigned int >( shadowRadiusUnits ) );
1636  shadowElem.setAttribute( "shadowRadiusMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( shadowRadiusMapUnitScale ) );
1637  shadowElem.setAttribute( "shadowRadiusAlphaOnly", shadowRadiusAlphaOnly );
1638  shadowElem.setAttribute( "shadowTransparency", shadowTransparency );
1639  shadowElem.setAttribute( "shadowScale", shadowScale );
1640  shadowElem.setAttribute( "shadowColor", QgsSymbolLayerV2Utils::encodeColor( shadowColor ) );
1641  shadowElem.setAttribute( "shadowBlendMode", QgsMapRenderer::getBlendModeEnum( shadowBlendMode ) );
1642 
1643  // placement
1644  QDomElement placementElem = doc.createElement( "placement" );
1645  placementElem.setAttribute( "placement", placement );
1646  placementElem.setAttribute( "placementFlags", static_cast< unsigned int >( placementFlags ) );
1647  placementElem.setAttribute( "centroidWhole", centroidWhole );
1648  placementElem.setAttribute( "centroidInside", centroidInside );
1649  placementElem.setAttribute( "predefinedPositionOrder", QgsLabelingUtils::encodePredefinedPositionOrder( predefinedPositionOrder ) );
1650  placementElem.setAttribute( "fitInPolygonOnly", fitInPolygonOnly );
1651  placementElem.setAttribute( "dist", dist );
1652  placementElem.setAttribute( "distInMapUnits", distInMapUnits );
1653  placementElem.setAttribute( "distMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( distMapUnitScale ) );
1654  placementElem.setAttribute( "offsetType", static_cast< unsigned int >( offsetType ) );
1655  placementElem.setAttribute( "quadOffset", static_cast< unsigned int >( quadOffset ) );
1656  placementElem.setAttribute( "xOffset", xOffset );
1657  placementElem.setAttribute( "yOffset", yOffset );
1658  placementElem.setAttribute( "labelOffsetInMapUnits", labelOffsetInMapUnits );
1659  placementElem.setAttribute( "labelOffsetMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( labelOffsetMapUnitScale ) );
1660  placementElem.setAttribute( "angleOffset", angleOffset );
1661  placementElem.setAttribute( "preserveRotation", preserveRotation );
1662  placementElem.setAttribute( "maxCurvedCharAngleIn", maxCurvedCharAngleIn );
1663  placementElem.setAttribute( "maxCurvedCharAngleOut", maxCurvedCharAngleOut );
1664  placementElem.setAttribute( "priority", priority );
1665  placementElem.setAttribute( "repeatDistance", repeatDistance );
1666  placementElem.setAttribute( "repeatDistanceUnit", repeatDistanceUnit );
1667  placementElem.setAttribute( "repeatDistanceMapUnitScale", QgsSymbolLayerV2Utils::encodeMapUnitScale( repeatDistanceMapUnitScale ) );
1668 
1669  // rendering
1670  QDomElement renderingElem = doc.createElement( "rendering" );
1671  renderingElem.setAttribute( "scaleVisibility", scaleVisibility );
1672  renderingElem.setAttribute( "scaleMin", scaleMin );
1673  renderingElem.setAttribute( "scaleMax", scaleMax );
1674  renderingElem.setAttribute( "fontLimitPixelSize", fontLimitPixelSize );
1675  renderingElem.setAttribute( "fontMinPixelSize", fontMinPixelSize );
1676  renderingElem.setAttribute( "fontMaxPixelSize", fontMaxPixelSize );
1677  renderingElem.setAttribute( "displayAll", displayAll );
1678  renderingElem.setAttribute( "upsidedownLabels", static_cast< unsigned int >( upsidedownLabels ) );
1679 
1680  renderingElem.setAttribute( "labelPerPart", labelPerPart );
1681  renderingElem.setAttribute( "mergeLines", mergeLines );
1682  renderingElem.setAttribute( "minFeatureSize", minFeatureSize );
1683  renderingElem.setAttribute( "limitNumLabels", limitNumLabels );
1684  renderingElem.setAttribute( "maxNumLabels", maxNumLabels );
1685  renderingElem.setAttribute( "obstacle", obstacle );
1686  renderingElem.setAttribute( "obstacleFactor", obstacleFactor );
1687  renderingElem.setAttribute( "obstacleType", static_cast< unsigned int >( obstacleType ) );
1688  renderingElem.setAttribute( "zIndex", zIndex );
1689 
1690  QDomElement ddElem = doc.createElement( "data-defined" );
1691  writeDataDefinedPropertyMap( nullptr, &ddElem, dataDefinedProperties );
1692 
1693  QDomElement elem = doc.createElement( "settings" );
1694  elem.appendChild( textStyleElem );
1695  elem.appendChild( textFormatElem );
1696  elem.appendChild( textBufferElem );
1697  elem.appendChild( backgroundElem );
1698  elem.appendChild( shadowElem );
1699  elem.appendChild( placementElem );
1700  elem.appendChild( renderingElem );
1701  elem.appendChild( ddElem );
1702  return elem;
1703 }
1704 
1706  bool active, bool useExpr, const QString& expr, const QString& field )
1707 {
1708  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
1709 
1710  if ( dataDefinedProperties.contains( p ) )
1711  {
1713  if ( it != dataDefinedProperties.constEnd() )
1714  {
1715  QgsDataDefined* dd = it.value();
1716  dd->setActive( active );
1717  dd->setExpressionString( expr );
1718  dd->setField( field );
1719  dd->setUseExpression( useExpr );
1720  }
1721  }
1722  else if ( !defaultVals )
1723  {
1724  QgsDataDefined* dd = new QgsDataDefined( active, useExpr, expr, field );
1725  dataDefinedProperties.insert( p, dd );
1726  }
1727 }
1728 
1730 {
1732  if ( it != dataDefinedProperties.end() )
1733  {
1734  delete( it.value() );
1736  }
1737 }
1738 
1740 {
1741  qDeleteAll( dataDefinedProperties );
1743 }
1744 
1746 {
1747  // TODO: update or remove this when project settings for labeling are migrated to better XML layout
1748  QString newValue = value;
1749  if ( !value.isEmpty() && !value.contains( "~~" ) )
1750  {
1751  QStringList values;
1752  values << "1"; // all old-style values are active if not empty
1753  values << "0";
1754  values << "";
1755  values << value; // all old-style values are only field names
1756  newValue = values.join( "~~" );
1757  }
1758 
1759  return newValue;
1760 }
1761 
1763 {
1765  return nullptr;
1766 
1768  if ( it != dataDefinedProperties.constEnd() )
1769  {
1770  return it.value();
1771  }
1772  return nullptr;
1773 }
1774 
1776 {
1779  if ( it != dataDefinedProperties.constEnd() )
1780  {
1781  return it.value()->toMap();
1782  }
1783  return map;
1784 }
1785 
1787 {
1789  {
1790  return QVariant();
1791  }
1792 
1793  //try to keep < 2.12 API - handle no passed expression context
1795  if ( !context )
1796  {
1797  scopedEc.reset( new QgsExpressionContext() );
1798  scopedEc->setFeature( f );
1799  scopedEc->setFields( fields );
1800  }
1801  const QgsExpressionContext* ec = context ? context : scopedEc.data();
1802 
1803  QgsDataDefined* dd = nullptr;
1805  if ( it != dataDefinedProperties.constEnd() )
1806  {
1807  dd = it.value();
1808  }
1809 
1810  if ( !dd )
1811  {
1812  return QVariant();
1813  }
1814 
1815  if ( !dd->isActive() )
1816  {
1817  return QVariant();
1818  }
1819 
1820  QVariant result = QVariant();
1821  bool useExpression = dd->useExpression();
1822  QString field = dd->field();
1823 
1824  //QgsDebugMsgLevel( QString( "isActive:" ) + isActive ? "1" : "0", 4 );
1825  //QgsDebugMsgLevel( QString( "useExpression:" ) + useExpression ? "1" : "0", 4 );
1826  //QgsDebugMsgLevel( QString( "expression:" ) + dd->expressionString(), 4 );
1827  //QgsDebugMsgLevel( QString( "field:" ) + field, 4 );
1828 
1829  if ( useExpression && dd->expressionIsPrepared() )
1830  {
1831  QgsExpression* expr = dd->expression();
1832  //QgsDebugMsgLevel( QString( "expr columns:" ) + expr->referencedColumns().join( "," ), 4 );
1833 
1834  result = expr->evaluate( ec );
1835  if ( expr->hasEvalError() )
1836  {
1837  QgsDebugMsgLevel( QString( "Evaluate error:" ) + expr->evalErrorString(), 4 );
1838  return QVariant();
1839  }
1840  }
1841  else if ( !useExpression && !field.isEmpty() )
1842  {
1843  // use direct attribute access instead of evaluating "field" expression (much faster)
1844  int indx = fields.indexFromName( field );
1845  if ( indx != -1 )
1846  {
1847  result = f.attribute( indx );
1848  }
1849  }
1850  return result;
1851 }
1852 
1854 {
1855  // null passed-around QVariant
1856  exprVal.clear();
1858  return false;
1859 
1860  //try to keep < 2.12 API - handle no passed expression context
1862  if ( !context )
1863  {
1864  scopedEc.reset( new QgsExpressionContext() );
1865  scopedEc->setFeature( *mCurFeat );
1866  scopedEc->setFields( mCurFields );
1867  }
1868  QgsExpressionContext* ec = context ? context : scopedEc.data();
1869 
1870  ec->setOriginalValueVariable( originalValue );
1871  QVariant result = dataDefinedValue( p, *mCurFeat, mCurFields, ec );
1872 
1873  if ( result.isValid() && !result.isNull() )
1874  {
1875  //QgsDebugMsgLevel( QString( "result type:" ) + QString( result.typeName() ), 4 );
1876  //QgsDebugMsgLevel( QString( "result string:" ) + result.toString(), 4 );
1877  exprVal = result;
1878  return true;
1879  }
1880 
1881  return false;
1882 }
1883 
1885 {
1887  return false;
1888 
1889  bool isActive = false;
1890 
1892  if ( it != dataDefinedProperties.constEnd() )
1893  {
1894  isActive = it.value()->isActive();
1895  }
1896 
1897  return isActive;
1898 }
1899 
1901 {
1903  return false;
1904 
1905  bool useExpression = false;
1907  if ( it != dataDefinedProperties.constEnd() )
1908  {
1909  useExpression = it.value()->useExpression();
1910  }
1911 
1912  return useExpression;
1913 }
1914 
1915 bool QgsPalLayerSettings::checkMinimumSizeMM( const QgsRenderContext& ct, const QgsGeometry* geom, double minSize ) const
1916 {
1917  return QgsPalLabeling::checkMinimumSizeMM( ct, geom, minSize );
1918 }
1919 
1920 void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString text, double& labelX, double& labelY, QgsFeature* f, QgsRenderContext *context )
1921 {
1922  if ( !fm || !f )
1923  {
1924  return;
1925  }
1926 
1927  //try to keep < 2.12 API - handle no passed render context
1929  if ( !context )
1930  {
1931  scopedRc.reset( new QgsRenderContext() );
1932  if ( f )
1933  scopedRc->expressionContext().setFeature( *f );
1934  }
1935  QgsRenderContext* rc = context ? context : scopedRc.data();
1936 
1937  QString wrapchr = wrapChar;
1938  double multilineH = multilineHeight;
1939 
1940  bool addDirSymb = addDirectionSymbol;
1941  QString leftDirSymb = leftDirectionSymbol;
1942  QString rightDirSymb = rightDirectionSymbol;
1944 
1945  if ( f == mCurFeat ) // called internally, use any stored data defined values
1946  {
1947  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
1948  {
1949  wrapchr = dataDefinedValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
1950  }
1951 
1952  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
1953  {
1954  multilineH = dataDefinedValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
1955  }
1956 
1957  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
1958  {
1959  addDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
1960  }
1961 
1962  if ( addDirSymb )
1963  {
1964 
1965  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
1966  {
1967  leftDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
1968  }
1969  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbRight ) )
1970  {
1971  rightDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
1972  }
1973 
1974  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
1975  {
1976  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( dataDefinedValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt() );
1977  }
1978 
1979  }
1980 
1981  }
1982  else // called externally with passed-in feature, evaluate data defined
1983  {
1986  if ( exprVal.isValid() )
1987  {
1988  wrapchr = exprVal.toString();
1989  }
1990  exprVal.clear();
1991  rc->expressionContext().setOriginalValueVariable( multilineH );
1993  if ( exprVal.isValid() )
1994  {
1995  bool ok;
1996  double size = exprVal.toDouble( &ok );
1997  if ( ok )
1998  {
1999  multilineH = size;
2000  }
2001  }
2002 
2003  exprVal.clear();
2004  rc->expressionContext().setOriginalValueVariable( addDirSymb );
2006  if ( exprVal.isValid() )
2007  {
2008  addDirSymb = exprVal.toBool();
2009  }
2010 
2011  if ( addDirSymb ) // don't do extra evaluations if not adding a direction symbol
2012  {
2013  exprVal.clear();
2014  rc->expressionContext().setOriginalValueVariable( leftDirSymb );
2016  if ( exprVal.isValid() )
2017  {
2018  leftDirSymb = exprVal.toString();
2019  }
2020  exprVal.clear();
2021  rc->expressionContext().setOriginalValueVariable( rightDirSymb );
2023  if ( exprVal.isValid() )
2024  {
2025  rightDirSymb = exprVal.toString();
2026  }
2027  exprVal.clear();
2028  rc->expressionContext().setOriginalValueVariable( static_cast< int >( placeDirSymb ) );
2030  if ( exprVal.isValid() )
2031  {
2032  bool ok;
2033  int enmint = exprVal.toInt( &ok );
2034  if ( ok )
2035  {
2036  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( enmint );
2037  }
2038  }
2039  }
2040 
2041  }
2042 
2043  if ( wrapchr.isEmpty() )
2044  {
2045  wrapchr = QLatin1String( "\n" ); // default to new line delimiter
2046  }
2047 
2048  //consider the space needed for the direction symbol
2049  if ( addDirSymb && placement == QgsPalLayerSettings::Line
2050  && ( !leftDirSymb.isEmpty() || !rightDirSymb.isEmpty() ) )
2051  {
2052  QString dirSym = leftDirSymb;
2053 
2054  if ( fm->width( rightDirSymb ) > fm->width( dirSym ) )
2055  dirSym = rightDirSymb;
2056 
2057  if ( placeDirSymb == QgsPalLayerSettings::SymbolLeftRight )
2058  {
2059  text.append( dirSym );
2060  }
2061  else
2062  {
2063  text.prepend( dirSym + QLatin1String( "\n" ) ); // SymbolAbove or SymbolBelow
2064  }
2065  }
2066 
2067  double w = 0.0, h = 0.0;
2068  QStringList multiLineSplit = QgsPalLabeling::splitToLines( text, wrapchr );
2069  int lines = multiLineSplit.size();
2070 
2071  double labelHeight = fm->ascent() + fm->descent(); // ignore +1 for baseline
2072 
2073  h += fm->height() + static_cast< double >(( lines - 1 ) * labelHeight * multilineH );
2074  h /= rasterCompressFactor;
2075 
2076  for ( int i = 0; i < lines; ++i )
2077  {
2078  double width = fm->width( multiLineSplit.at( i ) );
2079  if ( width > w )
2080  {
2081  w = width;
2082  }
2083  }
2084  w /= rasterCompressFactor;
2085 
2086 #if 0 // XXX strk
2087  QgsPoint ptSize = xform->toMapCoordinatesF( w, h );
2088  labelX = qAbs( ptSize.x() - ptZero.x() );
2089  labelY = qAbs( ptSize.y() - ptZero.y() );
2090 #else
2091  double uPP = xform->mapUnitsPerPixel();
2092  labelX = w * uPP;
2093  labelY = h * uPP;
2094 #endif
2095 }
2096 
2097 void QgsPalLayerSettings::registerFeature( QgsFeature& f, QgsRenderContext &context, QgsLabelFeature** labelFeature , QgsGeometry* obstacleGeometry )
2098 {
2099  // either used in QgsPalLabeling (palLayer is set) or in QgsLabelingEngineV2 (labelFeature is set)
2100  Q_ASSERT( labelFeature );
2101 
2102  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2103  mCurFeat = &f;
2104 
2105  // data defined is obstacle? calculate this first, to avoid wasting time working with obstacles we don't require
2106  bool isObstacle = obstacle; // start with layer default
2108  {
2109  isObstacle = exprVal.toBool();
2110  }
2111 
2112  if ( !drawLabels )
2113  {
2114  if ( isObstacle )
2115  {
2116  registerObstacleFeature( f, context, labelFeature, obstacleGeometry );
2117  }
2118  return;
2119  }
2120 
2121 // mCurFields = &layer->pendingFields();
2122 
2123  // store data defined-derived values for later adding to label feature for use during rendering
2124  dataDefinedValues.clear();
2125 
2126  // data defined show label? defaults to show label if not 0
2128  {
2129  bool showLabel = dataDefinedEvaluate( QgsPalLayerSettings::Show, exprVal, &context.expressionContext(), true );
2130  showLabel &= exprVal.toBool();
2131  QgsDebugMsgLevel( QString( "exprVal Show:%1" ).arg( showLabel ? "true" : "false" ), 4 );
2132  if ( !showLabel )
2133  {
2134  return;
2135  }
2136  }
2137 
2138  // data defined scale visibility?
2139  bool useScaleVisibility = scaleVisibility;
2141  {
2142  QgsDebugMsgLevel( QString( "exprVal ScaleVisibility:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
2143  useScaleVisibility = exprVal.toBool();
2144  }
2145 
2146  if ( useScaleVisibility )
2147  {
2148  // data defined min scale?
2149  double minScale = scaleMin;
2151  {
2152  QgsDebugMsgLevel( QString( "exprVal MinScale:%1" ).arg( exprVal.toDouble() ), 4 );
2153  bool conversionOk;
2154  double mins = exprVal.toDouble( &conversionOk );
2155  if ( conversionOk )
2156  {
2157  minScale = mins;
2158  }
2159  }
2160 
2161  // scales closer than 1:1
2162  if ( minScale < 0 )
2163  {
2164  minScale = 1 / qAbs( minScale );
2165  }
2166 
2167  if ( !qgsDoubleNear( minScale, 0.0 ) && context.rendererScale() < minScale )
2168  {
2169  return;
2170  }
2171 
2172  // data defined max scale?
2173  double maxScale = scaleMax;
2175  {
2176  QgsDebugMsgLevel( QString( "exprVal MaxScale:%1" ).arg( exprVal.toDouble() ), 4 );
2177  bool conversionOk;
2178  double maxs = exprVal.toDouble( &conversionOk );
2179  if ( conversionOk )
2180  {
2181  maxScale = maxs;
2182  }
2183  }
2184 
2185  // scales closer than 1:1
2186  if ( maxScale < 0 )
2187  {
2188  maxScale = 1 / qAbs( maxScale );
2189  }
2190 
2191  if ( !qgsDoubleNear( maxScale, 0.0 ) && context.rendererScale() > maxScale )
2192  {
2193  return;
2194  }
2195  }
2196 
2197  QFont labelFont = textFont;
2198  // labelFont will be added to label feature for use during label painting
2199 
2200  // data defined font units?
2203  {
2204  QString units = exprVal.toString().trimmed();
2205  QgsDebugMsgLevel( QString( "exprVal Font units:%1" ).arg( units ), 4 );
2206  if ( !units.isEmpty() )
2207  {
2208  fontunits = _decodeUnits( units );
2209  }
2210  }
2211 
2212  //data defined label size?
2213  double fontSize = labelFont.pointSizeF(); // font size doesn't have its own class data member
2214  if ( dataDefinedEvaluate( QgsPalLayerSettings::Size, exprVal, &context.expressionContext(), fontSize ) )
2215  {
2216  QgsDebugMsgLevel( QString( "exprVal Size:%1" ).arg( exprVal.toDouble() ), 4 );
2217  bool ok;
2218  double size = exprVal.toDouble( &ok );
2219  if ( ok )
2220  {
2221  fontSize = size;
2222  }
2223  }
2224  if ( fontSize <= 0.0 )
2225  {
2226  return;
2227  }
2228 
2229  int fontPixelSize = sizeToPixel( fontSize, context, fontunits, true, fontSizeMapUnitScale );
2230  // don't try to show font sizes less than 1 pixel (Qt complains)
2231  if ( fontPixelSize < 1 )
2232  {
2233  return;
2234  }
2235  labelFont.setPixelSize( fontPixelSize );
2236 
2237  // NOTE: labelFont now always has pixelSize set, so pointSize or pointSizeF might return -1
2238 
2239  // defined 'minimum/maximum pixel font size'?
2240  if ( fontunits == QgsPalLayerSettings::MapUnits )
2241  {
2242  bool useFontLimitPixelSize = fontLimitPixelSize;
2244  {
2245  QgsDebugMsgLevel( QString( "exprVal FontLimitPixel:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
2246  useFontLimitPixelSize = exprVal.toBool();
2247  }
2248 
2249  if ( useFontLimitPixelSize )
2250  {
2251  int fontMinPixel = fontMinPixelSize;
2253  {
2254  bool ok;
2255  int sizeInt = exprVal.toInt( &ok );
2256  QgsDebugMsgLevel( QString( "exprVal FontMinPixel:%1" ).arg( sizeInt ), 4 );
2257  if ( ok )
2258  {
2259  fontMinPixel = sizeInt;
2260  }
2261  }
2262 
2263  int fontMaxPixel = fontMaxPixelSize;
2265  {
2266  bool ok;
2267  int sizeInt = exprVal.toInt( &ok );
2268  QgsDebugMsgLevel( QString( "exprVal FontMaxPixel:%1" ).arg( sizeInt ), 4 );
2269  if ( ok )
2270  {
2271  fontMaxPixel = sizeInt;
2272  }
2273  }
2274 
2275  if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
2276  {
2277  return;
2278  }
2279  }
2280  }
2281 
2282  // NOTE: the following parsing functions calculate and store any data defined values for later use in QgsPalLabeling::drawLabeling
2283  // this is done to provide clarity, and because such parsing is not directly related to PAL feature registration calculations
2284 
2285  // calculate rest of font attributes and store any data defined values
2286  // this is done here for later use in making label backgrounds part of collision management (when implemented)
2287  labelFont.setCapitalization( QFont::MixedCase ); // reset this - we don't use QFont's handling as it breaks with curved labels
2288  parseTextStyle( labelFont, fontunits, context );
2289  parseTextFormatting( context );
2290  parseTextBuffer( context );
2291  parseShapeBackground( context );
2292  parseDropShadow( context );
2293 
2294  QString labelText;
2295 
2296  // Check to see if we are a expression string.
2297  if ( isExpression )
2298  {
2300  if ( exp->hasParserError() )
2301  {
2302  QgsDebugMsgLevel( QString( "Expression parser error:%1" ).arg( exp->parserErrorString() ), 4 );
2303  return;
2304  }
2305  exp->setScale( context.rendererScale() );
2306 // QVariant result = exp->evaluate( &f, layer->pendingFields() );
2307  QVariant result = exp->evaluate( &context.expressionContext() ); // expression prepared in QgsPalLabeling::prepareLayer()
2308  if ( exp->hasEvalError() )
2309  {
2310  QgsDebugMsgLevel( QString( "Expression parser eval error:%1" ).arg( exp->evalErrorString() ), 4 );
2311  return;
2312  }
2313  labelText = result.isNull() ? "" : result.toString();
2314  }
2315  else
2316  {
2317  const QVariant &v = f.attribute( fieldIndex );
2318  labelText = v.isNull() ? "" : v.toString();
2319  }
2320 
2321  // apply capitalization
2323  // maintain API - capitalization may have been set in textFont
2324  if ( textFont.capitalization() != QFont::MixedCase )
2325  {
2326  capitalization = static_cast< QgsStringUtils::Capitalization >( textFont.capitalization() );
2327  }
2328  // data defined font capitalization?
2330  {
2331  QString fcase = exprVal.toString().trimmed();
2332  QgsDebugMsgLevel( QString( "exprVal FontCase:%1" ).arg( fcase ), 4 );
2333 
2334  if ( !fcase.isEmpty() )
2335  {
2336  if ( fcase.compare( "NoChange", Qt::CaseInsensitive ) == 0 )
2337  {
2338  capitalization = QgsStringUtils::MixedCase;
2339  }
2340  else if ( fcase.compare( "Upper", Qt::CaseInsensitive ) == 0 )
2341  {
2342  capitalization = QgsStringUtils::AllUppercase;
2343  }
2344  else if ( fcase.compare( "Lower", Qt::CaseInsensitive ) == 0 )
2345  {
2346  capitalization = QgsStringUtils::AllLowercase;
2347  }
2348  else if ( fcase.compare( "Capitalize", Qt::CaseInsensitive ) == 0 )
2349  {
2351  }
2352  }
2353  }
2354  labelText = QgsStringUtils::capitalize( labelText, capitalization );
2355 
2356  // data defined format numbers?
2357  bool formatnum = formatNumbers;
2359  {
2360  formatnum = exprVal.toBool();
2361  QgsDebugMsgLevel( QString( "exprVal NumFormat:%1" ).arg( formatnum ? "true" : "false" ), 4 );
2362  }
2363 
2364  // format number if label text is coercible to a number
2365  if ( formatnum )
2366  {
2367  // data defined decimal places?
2368  int decimalPlaces = decimals;
2370  {
2371  bool ok;
2372  int dInt = exprVal.toInt( &ok );
2373  QgsDebugMsgLevel( QString( "exprVal NumDecimals:%1" ).arg( dInt ), 4 );
2374  if ( ok && dInt > 0 ) // needs to be positive
2375  {
2376  decimalPlaces = dInt;
2377  }
2378  }
2379 
2380  // data defined plus sign?
2381  bool signPlus = plusSign;
2383  {
2384  signPlus = exprVal.toBool();
2385  QgsDebugMsgLevel( QString( "exprVal NumPlusSign:%1" ).arg( signPlus ? "true" : "false" ), 4 );
2386  }
2387 
2388  QVariant textV( labelText );
2389  bool ok;
2390  double d = textV.toDouble( &ok );
2391  if ( ok )
2392  {
2393  QString numberFormat;
2394  if ( d > 0 && signPlus )
2395  {
2396  numberFormat.append( '+' );
2397  }
2398  numberFormat.append( "%1" );
2399  labelText = numberFormat.arg( d, 0, 'f', decimalPlaces );
2400  }
2401  }
2402 
2403 
2404  // NOTE: this should come AFTER any option that affects font metrics
2405  QScopedPointer<QFontMetricsF> labelFontMetrics( new QFontMetricsF( labelFont ) );
2406  double labelX, labelY; // will receive label size
2407  calculateLabelSize( labelFontMetrics.data(), labelText, labelX, labelY, mCurFeat, &context );
2408 
2409 
2410  // maximum angle between curved label characters (hardcoded defaults used in QGIS <2.0)
2411  //
2412  double maxcharanglein = 20.0; // range 20.0-60.0
2413  double maxcharangleout = -20.0; // range 20.0-95.0
2414 
2416  {
2417  maxcharanglein = maxCurvedCharAngleIn;
2418  maxcharangleout = maxCurvedCharAngleOut;
2419 
2420  //data defined maximum angle between curved label characters?
2422  {
2423  QString ptstr = exprVal.toString().trimmed();
2424  QgsDebugMsgLevel( QString( "exprVal CurvedCharAngleInOut:%1" ).arg( ptstr ), 4 );
2425 
2426  if ( !ptstr.isEmpty() )
2427  {
2428  QPointF maxcharanglePt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
2429  maxcharanglein = qBound( 20.0, static_cast< double >( maxcharanglePt.x() ), 60.0 );
2430  maxcharangleout = qBound( 20.0, static_cast< double >( maxcharanglePt.y() ), 95.0 );
2431  }
2432  }
2433  // make sure maxcharangleout is always negative
2434  maxcharangleout = -( qAbs( maxcharangleout ) );
2435  }
2436 
2437  // data defined centroid whole or clipped?
2438  bool wholeCentroid = centroidWhole;
2440  {
2441  QString str = exprVal.toString().trimmed();
2442  QgsDebugMsgLevel( QString( "exprVal CentroidWhole:%1" ).arg( str ), 4 );
2443 
2444  if ( !str.isEmpty() )
2445  {
2446  if ( str.compare( "Visible", Qt::CaseInsensitive ) == 0 )
2447  {
2448  wholeCentroid = false;
2449  }
2450  else if ( str.compare( "Whole", Qt::CaseInsensitive ) == 0 )
2451  {
2452  wholeCentroid = true;
2453  }
2454  }
2455  }
2456 
2457  const QgsGeometry* geom = f.constGeometry();
2458  if ( !geom )
2459  {
2460  return;
2461  }
2462 
2463  // whether we're going to create a centroid for polygon
2464  bool centroidPoly = (( placement == QgsPalLayerSettings::AroundPoint
2466  && geom->type() == QGis::Polygon );
2467 
2468  // CLIP the geometry if it is bigger than the extent
2469  // don't clip if centroid is requested for whole feature
2470  bool doClip = false;
2471  if ( !centroidPoly || !wholeCentroid )
2472  {
2473  doClip = true;
2474  }
2475 
2476  // if using fitInPolygonOnly option, generate the permissible zone (must happen before geometry is modified - eg
2477  // as a result of using perimeter based labeling and the geometry is converted to a boundary)
2478  QgsGeometry permissibleZone;
2479  if ( geom->type() == QGis::Polygon && fitInPolygonOnly )
2480  {
2481  permissibleZone = *geom;
2482  if ( QgsPalLabeling::geometryRequiresPreparation( &permissibleZone, context, ct, doClip ? extentGeom : nullptr ) )
2483  {
2484  QgsGeometry* preparedZone = QgsPalLabeling::prepareGeometry( &permissibleZone, context, ct, doClip ? extentGeom : nullptr );
2485  permissibleZone = *preparedZone;
2486  delete preparedZone;
2487  }
2488  }
2489 
2490  const GEOSGeometry* geos_geom = nullptr;
2491  const QgsGeometry* preparedGeom = geom;
2492  QScopedPointer<QgsGeometry> scopedPreparedGeom;
2493  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, doClip ? extentGeom : nullptr ) )
2494  {
2495  scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, doClip ? extentGeom : nullptr ) );
2496  if ( !scopedPreparedGeom.data() )
2497  return;
2498  preparedGeom = scopedPreparedGeom.data();
2499  geos_geom = scopedPreparedGeom.data()->asGeos();
2500  }
2501  else
2502  {
2503  geos_geom = geom->asGeos();
2504  }
2505  const GEOSGeometry* geosObstacleGeom = nullptr;
2506  QScopedPointer<QgsGeometry> scopedObstacleGeom;
2507  if ( isObstacle )
2508  {
2509  if ( obstacleGeometry && QgsPalLabeling::geometryRequiresPreparation( obstacleGeometry, context, ct, doClip ? extentGeom : nullptr ) )
2510  {
2511  scopedObstacleGeom.reset( QgsPalLabeling::prepareGeometry( obstacleGeometry, context, ct, doClip ? extentGeom : nullptr ) );
2512  obstacleGeometry = scopedObstacleGeom.data();
2513  }
2514  if ( obstacleGeometry )
2515  {
2516  geosObstacleGeom = obstacleGeometry->asGeos();
2517  }
2518  }
2519 
2520  if ( minFeatureSize > 0 && !checkMinimumSizeMM( context, preparedGeom, minFeatureSize ) )
2521  return;
2522 
2523  if ( !geos_geom )
2524  return; // invalid geometry
2525 
2526  // likelihood exists label will be registered with PAL and may be drawn
2527  // check if max number of features to label (already registered with PAL) has been reached
2528  // Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
2529  if ( limitNumLabels )
2530  {
2531  if ( !maxNumLabels )
2532  {
2533  return;
2534  }
2535  if ( mFeatsRegPal >= maxNumLabels )
2536  {
2537  return;
2538  }
2539 
2540  int divNum = static_cast< int >(( static_cast< double >( mFeaturesToLabel ) / maxNumLabels ) + 0.5 );
2541  if ( divNum && ( mFeatsRegPal == static_cast< int >( mFeatsSendingToPal / divNum ) ) )
2542  {
2543  mFeatsSendingToPal += 1;
2544  if ( divNum && mFeatsSendingToPal % divNum )
2545  {
2546  return;
2547  }
2548  }
2549  }
2550 
2551  GEOSGeometry* geos_geom_clone;
2552  GEOSGeomTypes geomType = ( GEOSGeomTypes ) GEOSGeomTypeId_r( QgsGeometry::getGEOSHandler(), geos_geom );
2553  if (( geomType == GEOS_POLYGON || geomType == GEOS_MULTIPOLYGON ) && repeatDistance > 0 && placement == Line )
2554  {
2555  geos_geom_clone = GEOSBoundary_r( QgsGeometry::getGEOSHandler(), geos_geom );
2556  }
2557  else
2558  {
2559  geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
2560  }
2561  GEOSGeometry* geosObstacleGeomClone = nullptr;
2562  if ( geosObstacleGeom )
2563  {
2564  geosObstacleGeomClone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geosObstacleGeom );
2565  }
2566 
2567 
2568  //data defined position / alignment / rotation?
2569  bool dataDefinedPosition = false;
2570  bool layerDefinedRotation = false;
2571  bool dataDefinedRotation = false;
2572  double xPos = 0.0, yPos = 0.0, angle = 0.0;
2573  bool ddXPos = false, ddYPos = false;
2574  double quadOffsetX = 0.0, quadOffsetY = 0.0;
2575  double offsetX = 0.0, offsetY = 0.0;
2576 
2577  //data defined quadrant offset?
2578  bool ddFixedQuad = false;
2579  QuadrantPosition quadOff = quadOffset;
2580  if ( dataDefinedEvaluate( QgsPalLayerSettings::OffsetQuad, exprVal, &context.expressionContext(), static_cast< int >( quadOff ) ) )
2581  {
2582  bool ok;
2583  int quadInt = exprVal.toInt( &ok );
2584  QgsDebugMsgLevel( QString( "exprVal OffsetQuad:%1" ).arg( quadInt ), 4 );
2585  if ( ok && 0 <= quadInt && quadInt <= 8 )
2586  {
2587  quadOff = static_cast< QuadrantPosition >( quadInt );
2588  ddFixedQuad = true;
2589  }
2590  }
2591 
2592  // adjust quadrant offset of labels
2593  switch ( quadOff )
2594  {
2595  case QuadrantAboveLeft:
2596  quadOffsetX = -1.0;
2597  quadOffsetY = 1.0;
2598  break;
2599  case QuadrantAbove:
2600  quadOffsetX = 0.0;
2601  quadOffsetY = 1.0;
2602  break;
2603  case QuadrantAboveRight:
2604  quadOffsetX = 1.0;
2605  quadOffsetY = 1.0;
2606  break;
2607  case QuadrantLeft:
2608  quadOffsetX = -1.0;
2609  quadOffsetY = 0.0;
2610  break;
2611  case QuadrantRight:
2612  quadOffsetX = 1.0;
2613  quadOffsetY = 0.0;
2614  break;
2615  case QuadrantBelowLeft:
2616  quadOffsetX = -1.0;
2617  quadOffsetY = -1.0;
2618  break;
2619  case QuadrantBelow:
2620  quadOffsetX = 0.0;
2621  quadOffsetY = -1.0;
2622  break;
2623  case QuadrantBelowRight:
2624  quadOffsetX = 1.0;
2625  quadOffsetY = -1.0;
2626  break;
2627  case QuadrantOver:
2628  default:
2629  break;
2630  }
2631 
2632  //data defined label offset?
2633  double xOff = xOffset;
2634  double yOff = yOffset;
2636  {
2637  QString ptstr = exprVal.toString().trimmed();
2638  QgsDebugMsgLevel( QString( "exprVal OffsetXY:%1" ).arg( ptstr ), 4 );
2639 
2640  if ( !ptstr.isEmpty() )
2641  {
2642  QPointF ddOffPt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
2643  xOff = ddOffPt.x();
2644  yOff = ddOffPt.y();
2645  }
2646  }
2647 
2648  // data defined label offset units?
2649  bool offinmapunits = labelOffsetInMapUnits;
2651  {
2652  QString units = exprVal.toString().trimmed();
2653  QgsDebugMsgLevel( QString( "exprVal OffsetUnits:%1" ).arg( units ), 4 );
2654  if ( !units.isEmpty() )
2655  {
2656  offinmapunits = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2657  }
2658  }
2659 
2660  // adjust offset of labels to match chosen unit and map scale
2661  // offsets match those of symbology: -x = left, -y = up
2662  double mapUntsPerMM = labelOffsetMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor();
2663  if ( !qgsDoubleNear( xOff, 0.0 ) )
2664  {
2665  offsetX = xOff; // must be positive to match symbology offset direction
2666  if ( !offinmapunits )
2667  {
2668  offsetX *= mapUntsPerMM; //convert offset from mm to map units
2669  }
2670  }
2671  if ( !qgsDoubleNear( yOff, 0.0 ) )
2672  {
2673  offsetY = -yOff; // must be negative to match symbology offset direction
2674  if ( !offinmapunits )
2675  {
2676  offsetY *= mapUntsPerMM; //convert offset from mm to map units
2677  }
2678  }
2679 
2680  // layer defined rotation?
2681  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2682  if ( placement == QgsPalLayerSettings::OverPoint && !qgsDoubleNear( angleOffset, 0.0 ) )
2683  {
2684  layerDefinedRotation = true;
2685  angle = angleOffset * M_PI / 180; // convert to radians
2686  }
2687 
2688  const QgsMapToPixel& m2p = context.mapToPixel();
2689  //data defined rotation?
2691  {
2692  bool ok;
2693  double rotD = exprVal.toDouble( &ok );
2694  QgsDebugMsgLevel( QString( "exprVal Rotation:%1" ).arg( rotD ), 4 );
2695  if ( ok )
2696  {
2697  dataDefinedRotation = true;
2698  // TODO: add setting to disable having data defined rotation follow
2699  // map rotation ?
2700  rotD -= m2p.mapRotation();
2701  angle = rotD * M_PI / 180.0;
2702  }
2703  }
2704 
2706  {
2707  if ( !exprVal.isNull() )
2708  xPos = exprVal.toDouble( &ddXPos );
2709  QgsDebugMsgLevel( QString( "exprVal PositionX:%1" ).arg( xPos ), 4 );
2710 
2712  {
2713  //data defined position. But field values could be NULL -> positions will be generated by PAL
2714  if ( !exprVal.isNull() )
2715  yPos = exprVal.toDouble( &ddYPos );
2716  QgsDebugMsgLevel( QString( "exprVal PositionY:%1" ).arg( yPos ), 4 );
2717 
2718  if ( ddXPos && ddYPos )
2719  {
2720  dataDefinedPosition = true;
2721  // layer rotation set, but don't rotate pinned labels unless data defined
2722  if ( layerDefinedRotation && !dataDefinedRotation )
2723  {
2724  angle = 0.0;
2725  }
2726 
2727  //x/y shift in case of alignment
2728  double xdiff = 0.0;
2729  double ydiff = 0.0;
2730 
2731  //horizontal alignment
2732  if ( dataDefinedEvaluate( QgsPalLayerSettings::Hali, exprVal, &context.expressionContext() ) )
2733  {
2734  QString haliString = exprVal.toString();
2735  QgsDebugMsgLevel( QString( "exprVal Hali:%1" ).arg( haliString ), 4 );
2736  if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
2737  {
2738  xdiff -= labelX / 2.0;
2739  }
2740  else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
2741  {
2742  xdiff -= labelX;
2743  }
2744  }
2745 
2746  //vertical alignment
2747  if ( dataDefinedEvaluate( QgsPalLayerSettings::Vali, exprVal, &context.expressionContext() ) )
2748  {
2749  QString valiString = exprVal.toString();
2750  QgsDebugMsgLevel( QString( "exprVal Vali:%1" ).arg( valiString ), 4 );
2751 
2752  if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
2753  {
2754  if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 )
2755  {
2756  ydiff -= labelY;
2757  }
2758  else
2759  {
2760  double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
2761  if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
2762  {
2763  ydiff -= labelY * descentRatio;
2764  }
2765  else //'Cap' or 'Half'
2766  {
2767  double capHeightRatio = ( labelFontMetrics->boundingRect( 'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
2768  ydiff -= labelY * capHeightRatio;
2769  if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
2770  {
2771  ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
2772  }
2773  }
2774  }
2775  }
2776  }
2777 
2778  if ( dataDefinedRotation )
2779  {
2780  //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
2781  double xd = xdiff * cos( angle ) - ydiff * sin( angle );
2782  double yd = xdiff * sin( angle ) + ydiff * cos( angle );
2783  xdiff = xd;
2784  ydiff = yd;
2785  }
2786 
2787  //project xPos and yPos from layer to map CRS, handle rotation
2788  QgsGeometry ddPoint( new QgsPointV2( xPos, yPos ) );
2789  if ( QgsPalLabeling::geometryRequiresPreparation( &ddPoint, context, ct ) )
2790  {
2791  QgsGeometry* newPoint = QgsPalLabeling::prepareGeometry( &ddPoint, context, ct );
2792  xPos = static_cast< QgsPointV2* >( newPoint->geometry() )->x();
2793  yPos = static_cast< QgsPointV2* >( newPoint->geometry() )->y();
2794  delete newPoint;
2795  }
2796 
2797  xPos += xdiff;
2798  yPos += ydiff;
2799  }
2800  else
2801  {
2802  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2803  if ( dataDefinedRotation && placement != QgsPalLayerSettings::OverPoint )
2804  {
2805  angle = 0.0;
2806  }
2807  }
2808  }
2809  }
2810 
2811  // data defined always show?
2812  bool alwaysShow = false;
2814  {
2815  alwaysShow = exprVal.toBool();
2816  }
2817 
2818  // set repeat distance
2819  // data defined repeat distance?
2820  double repeatDist = repeatDistance;
2822  {
2823  bool ok;
2824  double distD = exprVal.toDouble( &ok );
2825  if ( ok )
2826  {
2827  repeatDist = distD;
2828  }
2829  }
2830 
2831  // data defined label-repeat distance units?
2832  bool repeatdistinmapunit = repeatDistanceUnit == QgsPalLayerSettings::MapUnits;
2834  {
2835  QString units = exprVal.toString().trimmed();
2836  QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 );
2837  if ( !units.isEmpty() )
2838  {
2839  repeatdistinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2840  }
2841  }
2842 
2843  if ( !qgsDoubleNear( repeatDist, 0.0 ) )
2844  {
2845  if ( !repeatdistinmapunit )
2846  {
2847  repeatDist *= mapUntsPerMM; //convert repeat distance from mm to map units
2848  }
2849  }
2850 
2851  // feature to the layer
2852  QgsTextLabelFeature* lf = new QgsTextLabelFeature( f.id(), geos_geom_clone, QSizeF( labelX, labelY ) );
2853  mFeatsRegPal++;
2854 
2855  *labelFeature = lf;
2856  ( *labelFeature )->setHasFixedPosition( dataDefinedPosition );
2857  ( *labelFeature )->setFixedPosition( QgsPoint( xPos, yPos ) );
2858  // use layer-level defined rotation, but not if position fixed
2859  ( *labelFeature )->setHasFixedAngle( dataDefinedRotation || ( !dataDefinedPosition && !qgsDoubleNear( angle, 0.0 ) ) );
2860  ( *labelFeature )->setFixedAngle( angle );
2861  ( *labelFeature )->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
2862  ( *labelFeature )->setPositionOffset( QgsPoint( offsetX, offsetY ) );
2863  ( *labelFeature )->setOffsetType( offsetType );
2864  ( *labelFeature )->setAlwaysShow( alwaysShow );
2865  ( *labelFeature )->setRepeatDistance( repeatDist );
2866  ( *labelFeature )->setLabelText( labelText );
2867  ( *labelFeature )->setPermissibleZone( permissibleZone );
2868  if ( geosObstacleGeomClone )
2869  {
2870  ( *labelFeature )->setObstacleGeometry( geosObstacleGeomClone );
2871 
2872  if ( geom->type() == QGis::Point )
2873  {
2874  //register symbol size
2875  ( *labelFeature )->setSymbolSize( QSizeF( obstacleGeometry->boundingBox().width(),
2876  obstacleGeometry->boundingBox().height() ) );
2877  }
2878  }
2879 
2880  //set label's visual margin so that top visual margin is the leading, and bottom margin is the font's descent
2881  //this makes labels align to the font's baseline or highest character
2882  double topMargin = qMax( 0.25 * labelFontMetrics->ascent(), 0.0 );
2883  double bottomMargin = 1.0 + labelFontMetrics->descent();
2884  QgsLabelFeature::VisualMargin vm( topMargin, 0.0, bottomMargin, 0.0 );
2886  ( *labelFeature )->setVisualMargin( vm );
2887 
2888  // store the label's calculated font for later use during painting
2889  QgsDebugMsgLevel( QString( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString(), labelFont.styleName() ), 4 );
2890  lf->setDefinedFont( labelFont );
2891 
2892  // TODO: only for placement which needs character info
2893  // account for any data defined font metrics adjustments
2894  lf->calculateInfo( placement == QgsPalLayerSettings::Curved, labelFontMetrics.data(), xform, rasterCompressFactor, maxcharanglein, maxcharangleout );
2895  // for labelFeature the LabelInfo is passed to feat when it is registered
2896 
2897  // TODO: allow layer-wide feature dist in PAL...?
2898 
2899  // data defined label-feature distance?
2900  double distance = dist;
2902  {
2903  bool ok;
2904  double distD = exprVal.toDouble( &ok );
2905  if ( ok )
2906  {
2907  distance = distD;
2908  }
2909  }
2910 
2911  // data defined label-feature distance units?
2912  bool distinmapunit = distInMapUnits;
2914  {
2915  QString units = exprVal.toString().trimmed();
2916  QgsDebugMsgLevel( QString( "exprVal DistanceUnits:%1" ).arg( units ), 4 );
2917  if ( !units.isEmpty() )
2918  {
2919  distinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2920  }
2921  }
2922 
2923  if ( !qgsDoubleNear( distance, 0.0 ) )
2924  {
2925  if ( distinmapunit ) //convert distance from mm/map units to pixels
2926  {
2927  distance /= distMapUnitScale.computeMapUnitsPerPixel( context );
2928  }
2929  else //mm
2930  {
2931  distance *= vectorScaleFactor;
2932  }
2933  double d = sqrt( ptOne.sqrDist( ptZero ) ) * distance;
2934  ( *labelFeature )->setDistLabel( d );
2935  }
2936 
2937  if ( ddFixedQuad )
2938  {
2939  ( *labelFeature )->setHasFixedQuadrant( true );
2940  }
2941 
2942  // data defined z-index?
2943  double z = zIndex;
2945  {
2946  bool ok;
2947  double zIndexD = exprVal.toDouble( &ok );
2948  if ( ok )
2949  {
2950  z = zIndexD;
2951  }
2952  }
2953  ( *labelFeature )->setZIndex( z );
2954 
2955  // data defined priority?
2957  {
2958  bool ok;
2959  double priorityD = exprVal.toDouble( &ok );
2960  if ( ok )
2961  {
2962  priorityD = qBound( 0.0, priorityD, 10.0 );
2963  priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
2964  ( *labelFeature )->setPriority( priorityD );
2965  }
2966  }
2967 
2968  ( *labelFeature )->setIsObstacle( isObstacle );
2969 
2970  double featObstacleFactor = obstacleFactor;
2972  {
2973  bool ok;
2974  double factorD = exprVal.toDouble( &ok );
2975  if ( ok )
2976  {
2977  factorD = qBound( 0.0, factorD, 10.0 );
2978  factorD = factorD / 5.0 + 0.0001; // convert 0 -> 10 to 0.0001 -> 2.0
2979  featObstacleFactor = factorD;
2980  }
2981  }
2982  ( *labelFeature )->setObstacleFactor( featObstacleFactor );
2983 
2985  if ( positionOrder.isEmpty() )
2986  positionOrder = QgsPalLayerSettings::DEFAULT_PLACEMENT_ORDER;
2987 
2989  {
2990  QString orderD = exprVal.toString();
2991  positionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( orderD );
2992  }
2993  ( *labelFeature )->setPredefinedPositionOrder( positionOrder );
2994 
2995  // add parameters for data defined labeling to label feature
2996  lf->setDataDefinedValues( dataDefinedValues );
2997 }
2998 
2999 void QgsPalLayerSettings::registerObstacleFeature( QgsFeature& f, QgsRenderContext &context, QgsLabelFeature** obstacleFeature, QgsGeometry* obstacleGeometry )
3000 {
3001  mCurFeat = &f;
3002 
3003  const QgsGeometry* geom = nullptr;
3004  if ( obstacleGeometry )
3005  {
3006  geom = obstacleGeometry;
3007  }
3008  else
3009  {
3010  geom = f.constGeometry();
3011  }
3012 
3013  if ( !geom )
3014  {
3015  return;
3016  }
3017 
3018  const GEOSGeometry* geos_geom = nullptr;
3019  QScopedPointer<QgsGeometry> scopedPreparedGeom;
3020 
3021  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, extentGeom ) )
3022  {
3023  scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, extentGeom ) );
3024  if ( !scopedPreparedGeom.data() )
3025  return;
3026  geos_geom = scopedPreparedGeom.data()->asGeos();
3027  }
3028  else
3029  {
3030  geos_geom = geom->asGeos();
3031  }
3032 
3033  if ( !geos_geom )
3034  return; // invalid geometry
3035 
3036  GEOSGeometry* geos_geom_clone;
3037  geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
3038 
3039  // feature to the layer
3040  *obstacleFeature = new QgsLabelFeature( f.id(), geos_geom_clone, QSizeF( 0, 0 ) );
3041  ( *obstacleFeature )->setIsObstacle( true );
3042  mFeatsRegPal++;
3043 }
3044 
3045 bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
3047  QVariant& exprVal, QgsExpressionContext& context, const QVariant& originalValue )
3048 {
3049  if ( dataDefinedEvaluate( p, exprVal, &context, originalValue ) )
3050  {
3051  QString dbgStr = QString( "exprVal %1:" ).arg( mDataDefinedNames.value( p ).first ) + "%1";
3052 
3053  switch ( valType )
3054  {
3055  case DDBool:
3056  {
3057  bool bol = exprVal.toBool();
3058  QgsDebugMsgLevel( dbgStr.arg( bol ? "true" : "false" ), 4 );
3059  dataDefinedValues.insert( p, QVariant( bol ) );
3060  return true;
3061  }
3062  case DDInt:
3063  {
3064  bool ok;
3065  int size = exprVal.toInt( &ok );
3066  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3067 
3068  if ( ok )
3069  {
3070  dataDefinedValues.insert( p, QVariant( size ) );
3071  return true;
3072  }
3073  return false;
3074  }
3075  case DDIntPos:
3076  {
3077  bool ok;
3078  int size = exprVal.toInt( &ok );
3079  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3080 
3081  if ( ok && size > 0 )
3082  {
3083  dataDefinedValues.insert( p, QVariant( size ) );
3084  return true;
3085  }
3086  return false;
3087  }
3088  case DDDouble:
3089  {
3090  bool ok;
3091  double size = exprVal.toDouble( &ok );
3092  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3093 
3094  if ( ok )
3095  {
3096  dataDefinedValues.insert( p, QVariant( size ) );
3097  return true;
3098  }
3099  return false;
3100  }
3101  case DDDoublePos:
3102  {
3103  bool ok;
3104  double size = exprVal.toDouble( &ok );
3105  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3106 
3107  if ( ok && size > 0.0 )
3108  {
3109  dataDefinedValues.insert( p, QVariant( size ) );
3110  return true;
3111  }
3112  return false;
3113  }
3114  case DDRotation180:
3115  {
3116  bool ok;
3117  double rot = exprVal.toDouble( &ok );
3118  QgsDebugMsgLevel( dbgStr.arg( rot ), 4 );
3119  if ( ok )
3120  {
3121  if ( rot < -180.0 && rot >= -360 )
3122  {
3123  rot += 360;
3124  }
3125  if ( rot > 180.0 && rot <= 360 )
3126  {
3127  rot -= 360;
3128  }
3129  if ( rot >= -180 && rot <= 180 )
3130  {
3131  dataDefinedValues.insert( p, QVariant( rot ) );
3132  return true;
3133  }
3134  }
3135  return false;
3136  }
3137  case DDTransparency:
3138  {
3139  bool ok;
3140  int size = exprVal.toInt( &ok );
3141  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
3142  if ( ok && size >= 0 && size <= 100 )
3143  {
3144  dataDefinedValues.insert( p, QVariant( size ) );
3145  return true;
3146  }
3147  return false;
3148  }
3149  case DDString:
3150  {
3151  QString str = exprVal.toString(); // don't trim whitespace
3152  QgsDebugMsgLevel( dbgStr.arg( str ), 4 );
3153 
3154  dataDefinedValues.insert( p, QVariant( str ) ); // let it stay empty if it is
3155  return true;
3156  }
3157  case DDUnits:
3158  {
3159  QString unitstr = exprVal.toString().trimmed();
3160  QgsDebugMsgLevel( dbgStr.arg( unitstr ), 4 );
3161 
3162  if ( !unitstr.isEmpty() )
3163  {
3164  dataDefinedValues.insert( p, QVariant( static_cast< int >( _decodeUnits( unitstr ) ) ) );
3165  return true;
3166  }
3167  return false;
3168  }
3169  case DDColor:
3170  {
3171  QString colorstr = exprVal.toString().trimmed();
3172  QgsDebugMsgLevel( dbgStr.arg( colorstr ), 4 );
3173  QColor color = QgsSymbolLayerV2Utils::decodeColor( colorstr );
3174 
3175  if ( color.isValid() )
3176  {
3177  dataDefinedValues.insert( p, QVariant( color ) );
3178  return true;
3179  }
3180  return false;
3181  }
3182  case DDJoinStyle:
3183  {
3184  QString joinstr = exprVal.toString().trimmed();
3185  QgsDebugMsgLevel( dbgStr.arg( joinstr ), 4 );
3186 
3187  if ( !joinstr.isEmpty() )
3188  {
3189  dataDefinedValues.insert( p, QVariant( static_cast< int >( _decodePenJoinStyle( joinstr ) ) ) );
3190  return true;
3191  }
3192  return false;
3193  }
3194  case DDBlendMode:
3195  {
3196  QString blendstr = exprVal.toString().trimmed();
3197  QgsDebugMsgLevel( dbgStr.arg( blendstr ), 4 );
3198 
3199  if ( !blendstr.isEmpty() )
3200  {
3201  dataDefinedValues.insert( p, QVariant( static_cast< int >( QgsSymbolLayerV2Utils::decodeBlendMode( blendstr ) ) ) );
3202  return true;
3203  }
3204  return false;
3205  }
3206  case DDPointF:
3207  {
3208  QString ptstr = exprVal.toString().trimmed();
3209  QgsDebugMsgLevel( dbgStr.arg( ptstr ), 4 );
3210 
3211  if ( !ptstr.isEmpty() )
3212  {
3213  dataDefinedValues.insert( p, QVariant( QgsSymbolLayerV2Utils::decodePoint( ptstr ) ) );
3214  return true;
3215  }
3216  return false;
3217  }
3218  }
3219  }
3220  return false;
3221 }
3222 
3223 void QgsPalLayerSettings::parseTextStyle( QFont& labelFont,
3225  QgsRenderContext &context )
3226 {
3227  // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
3228 
3229  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3230 
3231  // Two ways to generate new data defined font:
3232  // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
3233  // 2) Family + named style (bold or italic is ignored)
3234 
3235  // data defined font family?
3236  QString ddFontFamily( "" );
3237  if ( dataDefinedEvaluate( QgsPalLayerSettings::Family, exprVal, &context.expressionContext(), labelFont.family() ) )
3238  {
3239  QString family = exprVal.toString().trimmed();
3240  QgsDebugMsgLevel( QString( "exprVal Font family:%1" ).arg( family ), 4 );
3241 
3242  if ( labelFont.family() != family )
3243  {
3244  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
3245  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
3246  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
3247  {
3248  ddFontFamily = family;
3249  }
3250  }
3251  }
3252 
3253  // data defined named font style?
3254  QString ddFontStyle( "" );
3256  {
3257  QString fontstyle = exprVal.toString().trimmed();
3258  QgsDebugMsgLevel( QString( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
3259  ddFontStyle = fontstyle;
3260  }
3261 
3262  // data defined bold font style?
3263  bool ddBold = false;
3264  if ( dataDefinedEvaluate( QgsPalLayerSettings::Bold, exprVal, &context.expressionContext(), labelFont.bold() ) )
3265  {
3266  bool bold = exprVal.toBool();
3267  QgsDebugMsgLevel( QString( "exprVal Font bold:%1" ).arg( bold ? "true" : "false" ), 4 );
3268  ddBold = bold;
3269  }
3270 
3271  // data defined italic font style?
3272  bool ddItalic = false;
3273  if ( dataDefinedEvaluate( QgsPalLayerSettings::Italic, exprVal, &context.expressionContext(), labelFont.italic() ) )
3274  {
3275  bool italic = exprVal.toBool();
3276  QgsDebugMsgLevel( QString( "exprVal Font italic:%1" ).arg( italic ? "true" : "false" ), 4 );
3277  ddItalic = italic;
3278  }
3279 
3280  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
3281  // (currently defaults to what has been read in from layer settings)
3282  QFont newFont;
3283  QFont appFont = QApplication::font();
3284  bool newFontBuilt = false;
3285  if ( ddBold || ddItalic )
3286  {
3287  // new font needs built, since existing style needs removed
3288  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
3289  newFontBuilt = true;
3290  newFont.setBold( ddBold );
3291  newFont.setItalic( ddItalic );
3292  }
3293  else if ( !ddFontStyle.isEmpty()
3294  && ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
3295  {
3296  if ( !ddFontFamily.isEmpty() )
3297  {
3298  // both family and style are different, build font from database
3299  QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
3300  if ( appFont != styledfont )
3301  {
3302  newFont = styledfont;
3303  newFontBuilt = true;
3304  }
3305  }
3306 
3307  // update the font face style
3308  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
3309  }
3310  else if ( !ddFontFamily.isEmpty() )
3311  {
3312  if ( ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
3313  {
3314  // just family is different, build font from database
3315  QFont styledfont = mFontDB.font( ddFontFamily, textNamedStyle, appFont.pointSize() );
3316  if ( appFont != styledfont )
3317  {
3318  newFont = styledfont;
3319  newFontBuilt = true;
3320  }
3321  }
3322  else
3323  {
3324  newFont = QFont( ddFontFamily );
3325  newFontBuilt = true;
3326  }
3327  }
3328 
3329  if ( newFontBuilt )
3330  {
3331  // copy over existing font settings
3332  //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
3333  newFont.setPixelSize( labelFont.pixelSize() );
3334  newFont.setUnderline( labelFont.underline() );
3335  newFont.setStrikeOut( labelFont.strikeOut() );
3336  newFont.setWordSpacing( labelFont.wordSpacing() );
3337  newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
3338 
3339  labelFont = newFont;
3340  }
3341 
3342  // data defined word spacing?
3343  double wordspace = labelFont.wordSpacing();
3344  if ( dataDefinedEvaluate( QgsPalLayerSettings::FontWordSpacing, exprVal, &context.expressionContext(), wordspace ) )
3345  {
3346  bool ok;
3347  double wspacing = exprVal.toDouble( &ok );
3348  QgsDebugMsgLevel( QString( "exprVal FontWordSpacing:%1" ).arg( wspacing ), 4 );
3349  if ( ok )
3350  {
3351  wordspace = wspacing;
3352  }
3353  }
3354  labelFont.setWordSpacing( scaleToPixelContext( wordspace, context, fontunits, false, fontSizeMapUnitScale ) );
3355 
3356  // data defined letter spacing?
3357  double letterspace = labelFont.letterSpacing();
3358  if ( dataDefinedEvaluate( QgsPalLayerSettings::FontLetterSpacing, exprVal, &context.expressionContext(), letterspace ) )
3359  {
3360  bool ok;
3361  double lspacing = exprVal.toDouble( &ok );
3362  QgsDebugMsgLevel( QString( "exprVal FontLetterSpacing:%1" ).arg( lspacing ), 4 );
3363  if ( ok )
3364  {
3365  letterspace = lspacing;
3366  }
3367  }
3368  labelFont.setLetterSpacing( QFont::AbsoluteSpacing, scaleToPixelContext( letterspace, context, fontunits, false, fontSizeMapUnitScale ) );
3369 
3370  // data defined strikeout font style?
3371  if ( dataDefinedEvaluate( QgsPalLayerSettings::Strikeout, exprVal, &context.expressionContext(), labelFont.strikeOut() ) )
3372  {
3373  bool strikeout = exprVal.toBool();
3374  QgsDebugMsgLevel( QString( "exprVal Font strikeout:%1" ).arg( strikeout ? "true" : "false" ), 4 );
3375  labelFont.setStrikeOut( strikeout );
3376  }
3377 
3378  // data defined underline font style?
3379  if ( dataDefinedEvaluate( QgsPalLayerSettings::Underline, exprVal, &context.expressionContext(), labelFont.underline() ) )
3380  {
3381  bool underline = exprVal.toBool();
3382  QgsDebugMsgLevel( QString( "exprVal Font underline:%1" ).arg( underline ? "true" : "false" ), 4 );
3383  labelFont.setUnderline( underline );
3384  }
3385 
3386  // pass the rest on to QgsPalLabeling::drawLabeling
3387 
3388  // data defined font color?
3389  dataDefinedValEval( DDColor, QgsPalLayerSettings::Color, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( textColor ) );
3390 
3391  // data defined font transparency?
3392  dataDefinedValEval( DDTransparency, QgsPalLayerSettings::FontTransp, exprVal, context.expressionContext(), textTransp );
3393 
3394  // data defined font blend mode?
3395  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::FontBlendMode, exprVal, context.expressionContext() );
3396 
3397 }
3398 
3399 void QgsPalLayerSettings::parseTextBuffer( QgsRenderContext &context )
3400 {
3401  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3402 
3403  // data defined draw buffer?
3404  bool drawBuffer = bufferDraw;
3405  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::BufferDraw, exprVal, context.expressionContext(), bufferDraw ) )
3406  {
3407  drawBuffer = exprVal.toBool();
3408  }
3409 
3410  if ( !drawBuffer )
3411  {
3412  return;
3413  }
3414 
3415  // data defined buffer size?
3416  double bufrSize = bufferSize;
3417  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::BufferSize, exprVal, context.expressionContext(), bufferSize ) )
3418  {
3419  bufrSize = exprVal.toDouble();
3420  }
3421 
3422  // data defined buffer transparency?
3423  int bufTransp = bufferTransp;
3424  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::BufferTransp, exprVal, context.expressionContext(), bufferTransp ) )
3425  {
3426  bufTransp = exprVal.toInt();
3427  }
3428 
3429  drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufTransp < 100 );
3430 
3431  if ( !drawBuffer )
3432  {
3433  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
3434  dataDefinedValues.remove( QgsPalLayerSettings::BufferSize );
3435  dataDefinedValues.remove( QgsPalLayerSettings::BufferTransp );
3436  return; // don't bother evaluating values that won't be used
3437  }
3438 
3439  // data defined buffer units?
3440  dataDefinedValEval( DDUnits, QgsPalLayerSettings::BufferUnit, exprVal, context.expressionContext() );
3441 
3442  // data defined buffer color?
3443  dataDefinedValEval( DDColor, QgsPalLayerSettings::BufferColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( bufferColor ) );
3444 
3445  // data defined buffer pen join style?
3447 
3448  // data defined buffer blend mode?
3449  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::BufferBlendMode, exprVal, context.expressionContext() );
3450 }
3451 
3452 void QgsPalLayerSettings::parseTextFormatting( QgsRenderContext &context )
3453 {
3454  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3455 
3456  // data defined multiline wrap character?
3457  QString wrapchr = wrapChar;
3458  if ( dataDefinedValEval( DDString, QgsPalLayerSettings::MultiLineWrapChar, exprVal, context.expressionContext(), wrapChar ) )
3459  {
3460  wrapchr = exprVal.toString();
3461  }
3462 
3463  // data defined multiline height?
3464  dataDefinedValEval( DDDouble, QgsPalLayerSettings::MultiLineHeight, exprVal, context.expressionContext() );
3465 
3466  // data defined multiline text align?
3468  {
3469  QString str = exprVal.toString().trimmed();
3470  QgsDebugMsgLevel( QString( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
3471 
3472  if ( !str.isEmpty() )
3473  {
3474  // "Left"
3476 
3477  if ( str.compare( "Center", Qt::CaseInsensitive ) == 0 )
3478  {
3480  }
3481  else if ( str.compare( "Right", Qt::CaseInsensitive ) == 0 )
3482  {
3483  aligntype = QgsPalLayerSettings::MultiRight;
3484  }
3485  else if ( str.compare( "Follow", Qt::CaseInsensitive ) == 0 )
3486  {
3488  }
3489  dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant( static_cast< int >( aligntype ) ) );
3490  }
3491  }
3492 
3493  // data defined direction symbol?
3494  bool drawDirSymb = addDirectionSymbol;
3495  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbDraw, exprVal, context.expressionContext(), addDirectionSymbol ) )
3496  {
3497  drawDirSymb = exprVal.toBool();
3498  }
3499 
3500  if ( drawDirSymb )
3501  {
3502  // data defined direction left symbol?
3503  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbLeft, exprVal, context.expressionContext(), leftDirectionSymbol );
3504 
3505  // data defined direction right symbol?
3506  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbRight, exprVal, context.expressionContext(), rightDirectionSymbol );
3507 
3508  // data defined direction symbol placement?
3510  {
3511  QString str = exprVal.toString().trimmed();
3512  QgsDebugMsgLevel( QString( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
3513 
3514  if ( !str.isEmpty() )
3515  {
3516  // "LeftRight"
3518 
3519  if ( str.compare( "Above", Qt::CaseInsensitive ) == 0 )
3520  {
3522  }
3523  else if ( str.compare( "Below", Qt::CaseInsensitive ) == 0 )
3524  {
3526  }
3527  dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant( static_cast< int >( placetype ) ) );
3528  }
3529  }
3530 
3531  // data defined direction symbol reversed?
3532  dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbReverse, exprVal, context.expressionContext(), reverseDirectionSymbol );
3533  }
3534 
3535  // formatting for numbers is inline with generation of base label text and not passed to label painting
3536 }
3537 
3538 void QgsPalLayerSettings::parseShapeBackground( QgsRenderContext &context )
3539 {
3540  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3541 
3542  // data defined draw shape?
3543  bool drawShape = shapeDraw;
3544  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShapeDraw, exprVal, context.expressionContext(), shapeDraw ) )
3545  {
3546  drawShape = exprVal.toBool();
3547  }
3548 
3549  if ( !drawShape )
3550  {
3551  return;
3552  }
3553 
3554  // data defined shape transparency?
3555  int shapeTransp = shapeTransparency;
3556  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::ShapeTransparency, exprVal, context.expressionContext(), shapeTransparency ) )
3557  {
3558  shapeTransp = exprVal.toInt();
3559  }
3560 
3561  drawShape = ( drawShape && shapeTransp < 100 ); // size is not taken into account (could be)
3562 
3563  if ( !drawShape )
3564  {
3565  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
3566  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
3567  return; // don't bother evaluating values that won't be used
3568  }
3569 
3570  // data defined shape kind?
3573  {
3574  QString skind = exprVal.toString().trimmed();
3575  QgsDebugMsgLevel( QString( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
3576 
3577  if ( !skind.isEmpty() )
3578  {
3579  // "Rectangle"
3581 
3582  if ( skind.compare( "Square", Qt::CaseInsensitive ) == 0 )
3583  {
3585  }
3586  else if ( skind.compare( "Ellipse", Qt::CaseInsensitive ) == 0 )
3587  {
3589  }
3590  else if ( skind.compare( "Circle", Qt::CaseInsensitive ) == 0 )
3591  {
3593  }
3594  else if ( skind.compare( "SVG", Qt::CaseInsensitive ) == 0 )
3595  {
3597  }
3598  shapeKind = shpkind;
3599  dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant( static_cast< int >( shpkind ) ) );
3600  }
3601  }
3602 
3603  // data defined shape SVG path?
3604  QString svgPath = shapeSVGFile;
3606  {
3607  QString svgfile = exprVal.toString().trimmed();
3608  QgsDebugMsgLevel( QString( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
3609 
3610  // '' empty paths are allowed
3611  svgPath = svgfile;
3612  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgfile ) );
3613  }
3614 
3615  // data defined shape size type?
3618  {
3619  QString stype = exprVal.toString().trimmed();
3620  QgsDebugMsgLevel( QString( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
3621 
3622  if ( !stype.isEmpty() )
3623  {
3624  // "Buffer"
3626 
3627  if ( stype.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
3628  {
3630  }
3631  shpSizeType = sizType;
3632  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant( static_cast< int >( sizType ) ) );
3633  }
3634  }
3635 
3636  // data defined shape size X? (SVGs only use X for sizing)
3637  double ddShpSizeX = shapeSize.x();
3638  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeX, exprVal, context.expressionContext(), ddShpSizeX ) )
3639  {
3640  ddShpSizeX = exprVal.toDouble();
3641  }
3642 
3643  // data defined shape size Y?
3644  double ddShpSizeY = shapeSize.y();
3645  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeY, exprVal, context.expressionContext(), ddShpSizeY ) )
3646  {
3647  ddShpSizeY = exprVal.toDouble();
3648  }
3649 
3650  // don't continue under certain circumstances (e.g. size is fixed)
3651  bool skip = false;
3652  if ( shapeKind == QgsPalLayerSettings::ShapeSVG
3653  && ( svgPath.isEmpty()
3654  || ( !svgPath.isEmpty()
3655  && shpSizeType == QgsPalLayerSettings::SizeFixed
3656  && ddShpSizeX == 0.0 ) ) )
3657  {
3658  skip = true;
3659  }
3660  if ( shapeKind != QgsPalLayerSettings::ShapeSVG
3661  && shpSizeType == QgsPalLayerSettings::SizeFixed
3662  && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
3663  {
3664  skip = true;
3665  }
3666 
3667  if ( skip )
3668  {
3669  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
3670  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
3671  dataDefinedValues.remove( QgsPalLayerSettings::ShapeKind );
3672  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSVGFile );
3673  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeX );
3674  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeY );
3675  return; // don't bother evaluating values that won't be used
3676  }
3677 
3678  // data defined shape size units?
3679  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeSizeUnits, exprVal, context.expressionContext() );
3680 
3681  // data defined shape rotation type?
3683  {
3684  QString rotstr = exprVal.toString().trimmed();
3685  QgsDebugMsgLevel( QString( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
3686 
3687  if ( !rotstr.isEmpty() )
3688  {
3689  // "Sync"
3691 
3692  if ( rotstr.compare( "Offset", Qt::CaseInsensitive ) == 0 )
3693  {
3695  }
3696  else if ( rotstr.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
3697  {
3699  }
3700  dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant( static_cast< int >( rottype ) ) );
3701  }
3702  }
3703 
3704  // data defined shape rotation?
3705  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShapeRotation, exprVal, context.expressionContext(), shapeRotation );
3706 
3707  // data defined shape offset?
3708  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeOffset, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodePoint( shapeOffset ) );
3709 
3710  // data defined shape offset units?
3711  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeOffsetUnits, exprVal, context.expressionContext() );
3712 
3713  // data defined shape radii?
3714  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeRadii, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodePoint( shapeRadii ) );
3715 
3716  // data defined shape radii units?
3717  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeRadiiUnits, exprVal, context.expressionContext() );
3718 
3719  // data defined shape blend mode?
3720  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShapeBlendMode, exprVal, context.expressionContext() );
3721 
3722  // data defined shape fill color?
3723  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShapeFillColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( shapeFillColor ) );
3724 
3725  // data defined shape border color?
3727 
3728  // data defined shape border width?
3729  dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShapeBorderWidth, exprVal, context.expressionContext(), shapeBorderWidth );
3730 
3731  // data defined shape border width units?
3732  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeBorderWidthUnits, exprVal, context.expressionContext() );
3733 
3734  // data defined shape join style?
3736 
3737 }
3738 
3739 void QgsPalLayerSettings::parseDropShadow( QgsRenderContext &context )
3740 {
3741  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3742 
3743  // data defined draw shadow?
3744  bool drawShadow = shadowDraw;
3745  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShadowDraw, exprVal, context.expressionContext(), shadowDraw ) )
3746  {
3747  drawShadow = exprVal.toBool();
3748  }
3749 
3750  if ( !drawShadow )
3751  {
3752  return;
3753  }
3754 
3755  // data defined shadow transparency?
3756  int shadowTransp = shadowTransparency;
3757  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::ShadowTransparency, exprVal, context.expressionContext(), shadowTransparency ) )
3758  {
3759  shadowTransp = exprVal.toInt();
3760  }
3761 
3762  // data defined shadow offset distance?
3763  double shadowOffDist = shadowOffsetDist;
3764  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowOffsetDist, exprVal, context.expressionContext(), shadowOffsetDist ) )
3765  {
3766  shadowOffDist = exprVal.toDouble();
3767  }
3768 
3769  // data defined shadow offset distance?
3770  double shadowRad = shadowRadius;
3771  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRadius ) )
3772  {
3773  shadowRad = exprVal.toDouble();
3774  }
3775 
3776  drawShadow = ( drawShadow && shadowTransp < 100 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
3777 
3778  if ( !drawShadow )
3779  {
3780  dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
3781  dataDefinedValues.remove( QgsPalLayerSettings::ShadowTransparency );
3782  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOffsetDist );
3783  dataDefinedValues.remove( QgsPalLayerSettings::ShadowRadius );
3784  return; // don't bother evaluating values that won't be used
3785  }
3786 
3787  // data defined shadow under type?
3789  {
3790  QString str = exprVal.toString().trimmed();
3791  QgsDebugMsgLevel( QString( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
3792 
3793  if ( !str.isEmpty() )
3794  {
3795  // "Lowest"
3797 
3798  if ( str.compare( "Text", Qt::CaseInsensitive ) == 0 )
3799  {
3801  }
3802  else if ( str.compare( "Buffer", Qt::CaseInsensitive ) == 0 )
3803  {
3805  }
3806  else if ( str.compare( "Background", Qt::CaseInsensitive ) == 0 )
3807  {
3809  }
3810  dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant( static_cast< int >( shdwtype ) ) );
3811  }
3812  }
3813 
3814  // data defined shadow offset angle?
3815  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShadowOffsetAngle, exprVal, context.expressionContext(), shadowOffsetAngle );
3816 
3817  // data defined shadow offset units?
3818  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowOffsetUnits, exprVal, context.expressionContext() );
3819 
3820  // data defined shadow radius?
3821  dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRadius );
3822 
3823  // data defined shadow radius units?
3824  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowRadiusUnits, exprVal, context.expressionContext() );
3825 
3826  // data defined shadow scale? ( gui bounds to 0-2000, no upper bound here )
3827  dataDefinedValEval( DDIntPos, QgsPalLayerSettings::ShadowScale, exprVal, context.expressionContext(), shadowScale );
3828 
3829  // data defined shadow color?
3830  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShadowColor, exprVal, context.expressionContext(), QgsSymbolLayerV2Utils::encodeColor( shadowColor ) );
3831 
3832  // data defined shadow blend mode?
3833  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShadowBlendMode, exprVal, context.expressionContext() );
3834 }
3835 
3836 int QgsPalLayerSettings::sizeToPixel( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor, const QgsMapUnitScale& mapUnitScale ) const
3837 {
3838  return static_cast< int >( scaleToPixelContext( size, c, unit, rasterfactor, mapUnitScale ) + 0.5 );
3839 }
3840 
3841 double QgsPalLayerSettings::scaleToPixelContext( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor, const QgsMapUnitScale& mapUnitScale ) const
3842 {
3843  // if render context is that of device (i.e. not a scaled map), just return size
3844  double mapUnitsPerPixel = mapUnitScale.computeMapUnitsPerPixel( c );
3845 
3846  if ( unit == MapUnits && mapUnitsPerPixel > 0.0 )
3847  {
3848  size = size / mapUnitsPerPixel * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3849  }
3850  else // e.g. in points or mm
3851  {
3852  double ptsTomm = ( unit == Points ? 0.352778 : 1 );
3853  size *= ptsTomm * c.scaleFactor() * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3854  }
3855  return size;
3856 }
3857 
3858 // -------------
3859 
3861  : mEngine( new QgsLabelingEngineV2() )
3862 {
3863 }
3864 
3866 {
3867  delete mEngine;
3868  mEngine = nullptr;
3869 }
3870 
3872 {
3873  return staticWillUseLayer( layer );
3874 }
3875 
3877 {
3878  QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( layerID ) );
3879  if ( !layer )
3880  return false;
3881  return staticWillUseLayer( layer );
3882 }
3883 
3884 
3886 {
3887  // don't do QgsPalLayerSettings::readFromLayer( layer ) if not needed
3888  bool enabled = false;
3889  if ( layer->customProperty( "labeling" ).toString() == "pal" )
3890  enabled = layer->labelsEnabled() || layer->diagramsEnabled();
3891  else if ( layer->labeling()->type() == "rule-based" )
3892  return true;
3893 
3894  return enabled;
3895 }
3896 
3897 
3899 {
3900 }
3901 
3903 {
3904  Q_UNUSED( layerID );
3905 }
3906 
3907 
3909 {
3910  if ( !willUseLayer( layer ) )
3911  {
3912  return 0;
3913  }
3914 
3915  if ( !layer->labeling() )
3916  return 0;
3917 
3918  QgsVectorLayerLabelProvider* lp = layer->labeling()->provider( layer );
3919  if ( !lp )
3920  return 0;
3921 
3922  //QgsVectorLayerLabelProvider* lp = new QgsVectorLayerLabelProvider( layer, false );
3923  // need to be added before calling prepare() - uses map settings from engine
3924  mEngine->addProvider( lp );
3925  mLabelProviders[layer->id()] = lp; // fast lookup table by layer ID
3926 
3927  if ( !lp->prepare( ctx, attrNames ) )
3928  {
3929  mEngine->removeProvider( lp );
3930  return 0;
3931  }
3932 
3933  return 1; // init successful
3934 }
3935 
3937 {
3939  // need to be added before calling prepare() - uses map settings from engine
3940  mEngine->addProvider( dp );
3941  mDiagramProviders[layer->id()] = dp; // fast lookup table by layer ID
3942 
3943  if ( !dp->prepare( ctx, attrNames ) )
3944  {
3945  mEngine->removeProvider( dp );
3946  return 0;
3947  }
3948 
3949  return 1;
3950 }
3951 
3953 {
3954  QgsDebugMsg( "Called addDiagramLayer()... need to use prepareDiagramLayer() instead!" );
3955  Q_UNUSED( layer );
3956  Q_UNUSED( s );
3957  return 0;
3958 }
3959 
3961 {
3962  if ( QgsVectorLayerLabelProvider* provider = mLabelProviders.value( layerID, nullptr ) )
3963  provider->registerFeature( f, context );
3964 }
3965 
3967 {
3968  if ( !geometry )
3969  {
3970  return false;
3971  }
3972 
3973  //requires reprojection
3974  if ( ct )
3975  return true;
3976 
3977  //requires fixing
3978  if ( geometry->type() == QGis::Polygon && !geometry->isGeosValid() )
3979  return true;
3980 
3981  //requires rotation
3982  const QgsMapToPixel& m2p = context.mapToPixel();
3983  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
3984  return true;
3985 
3986  //requires clip
3987  if ( clipGeometry && !clipGeometry->contains( geometry ) )
3988  return true;
3989 
3990  return false;
3991 }
3992 
3993 QStringList QgsPalLabeling::splitToLines( const QString &text, const QString &wrapCharacter )
3994 {
3995  QStringList multiLineSplit;
3996  if ( !wrapCharacter.isEmpty() && wrapCharacter != QLatin1String( "\n" ) )
3997  {
3998  //wrap on both the wrapchr and new line characters
3999  Q_FOREACH ( const QString& line, text.split( wrapCharacter ) )
4000  {
4001  multiLineSplit.append( line.split( '\n' ) );
4002  }
4003  }
4004  else
4005  {
4006  multiLineSplit = text.split( '\n' );
4007  }
4008 
4009  return multiLineSplit;
4010 }
4011 
4013 {
4014  QStringList graphemes;
4015  QTextBoundaryFinder boundaryFinder( QTextBoundaryFinder::Grapheme, text );
4016  int currentBoundary = -1;
4017  int previousBoundary = 0;
4018  while (( currentBoundary = boundaryFinder.toNextBoundary() ) > 0 )
4019  {
4020  graphemes << text.mid( previousBoundary, currentBoundary - previousBoundary );
4021  previousBoundary = currentBoundary;
4022  }
4023  return graphemes;
4024 }
4025 
4027 {
4028  if ( !geometry )
4029  {
4030  return nullptr;
4031  }
4032 
4033  //don't modify the feature's geometry so that geometry based expressions keep working
4034  QgsGeometry* geom = new QgsGeometry( *geometry );
4035  QScopedPointer<QgsGeometry> clonedGeometry( geom );
4036 
4037  //reproject the geometry if necessary
4038  if ( ct )
4039  {
4040  try
4041  {
4042  geom->transform( *ct );
4043  }
4044  catch ( QgsCsException &cse )
4045  {
4046  Q_UNUSED( cse );
4047  QgsDebugMsgLevel( QString( "Ignoring feature due to transformation exception" ), 4 );
4048  return nullptr;
4049  }
4050  }
4051 
4052  // Rotate the geometry if needed, before clipping
4053  const QgsMapToPixel& m2p = context.mapToPixel();
4054  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
4055  {
4056  QgsPoint center = context.extent().center();
4057 
4058  if ( ct )
4059  {
4060  try
4061  {
4062  center = ct->transform( center );
4063  }
4064  catch ( QgsCsException &cse )
4065  {
4066  Q_UNUSED( cse );
4067  QgsDebugMsgLevel( QString( "Ignoring feature due to transformation exception" ), 4 );
4068  return nullptr;
4069  }
4070  }
4071 
4072  if ( geom->rotate( m2p.mapRotation(), center ) )
4073  {
4074  QgsDebugMsg( QString( "Error rotating geometry" ).arg( geom->exportToWkt() ) );
4075  return nullptr;
4076  }
4077  }
4078 
4079  if ( !geom->asGeos() )
4080  return nullptr; // there is something really wrong with the geometry
4081 
4082  // fix invalid polygons
4083  if ( geom->type() == QGis::Polygon && !geom->isGeosValid() )
4084  {
4085  QgsGeometry* bufferGeom = geom->buffer( 0, 0 );
4086  if ( !bufferGeom )
4087  {
4088  return nullptr;
4089  }
4090  geom = bufferGeom;
4091  clonedGeometry.reset( geom );
4092  }
4093 
4094  if ( clipGeometry && !clipGeometry->contains( geom ) )
4095  {
4096  QgsGeometry* clipGeom = geom->intersection( clipGeometry ); // creates new geometry
4097  if ( !clipGeom )
4098  {
4099  return nullptr;
4100  }
4101  geom = clipGeom;
4102  clonedGeometry.reset( geom );
4103  }
4104 
4105  return clonedGeometry.take();
4106 }
4107 
4108 bool QgsPalLabeling::checkMinimumSizeMM( const QgsRenderContext& context, const QgsGeometry* geom, double minSize )
4109 {
4110  if ( minSize <= 0 )
4111  {
4112  return true;
4113  }
4114 
4115  if ( !geom )
4116  {
4117  return false;
4118  }
4119 
4120  QGis::GeometryType featureType = geom->type();
4121  if ( featureType == QGis::Point ) //minimum size does not apply to point features
4122  {
4123  return true;
4124  }
4125 
4126  double mapUnitsPerMM = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
4127  if ( featureType == QGis::Line )
4128  {
4129  double length = geom->length();
4130  if ( length >= 0.0 )
4131  {
4132  return ( length >= ( minSize * mapUnitsPerMM ) );
4133  }
4134  }
4135  else if ( featureType == QGis::Polygon )
4136  {
4137  double area = geom->area();
4138  if ( area >= 0.0 )
4139  {
4140  return ( sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
4141  }
4142  }
4143  return true; //should never be reached. Return true in this case to label such geometries anyway.
4144 }
4145 
4147 {
4148  if ( QgsVectorLayerDiagramProvider* provider = mDiagramProviders.value( layerID, nullptr ) )
4149  provider->registerFeature( feat, context );
4150 }
4151 
4152 
4154 {
4155  init( mr->mapSettings() );
4156 }
4157 
4158 
4159 void QgsPalLabeling::init( const QgsMapSettings& mapSettings )
4160 {
4161  mEngine->setMapSettings( mapSettings );
4162 }
4163 
4165 {
4166  delete mEngine;
4167  mEngine = new QgsLabelingEngineV2();
4168 }
4169 
4171 {
4172  Q_UNUSED( layerName );
4173  return mInvalidLayerSettings;
4174 }
4175 
4178 {
4179  //font color
4180  if ( ddValues.contains( QgsPalLayerSettings::Color ) )
4181  {
4182  QVariant ddColor = ddValues.value( QgsPalLayerSettings::Color );
4183  tmpLyr.textColor = ddColor.value<QColor>();
4184  }
4185 
4186  //font transparency
4187  if ( ddValues.contains( QgsPalLayerSettings::FontTransp ) )
4188  {
4189  tmpLyr.textTransp = ddValues.value( QgsPalLayerSettings::FontTransp ).toInt();
4190  }
4191 
4192  tmpLyr.textColor.setAlphaF(( 100.0 - static_cast< double >( tmpLyr.textTransp ) ) / 100.0 );
4193 
4194  //font blend mode
4195  if ( ddValues.contains( QgsPalLayerSettings::FontBlendMode ) )
4196  {
4197  tmpLyr.blendMode = static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::FontBlendMode ).toInt() );
4198  }
4199 }
4200 
4203 {
4205  {
4206  tmpLyr.wrapChar = ddValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
4207  }
4208 
4209  if ( !tmpLyr.wrapChar.isEmpty() || tmpLyr.getLabelExpression()->expression().contains( "wordwrap" ) )
4210  {
4211 
4213  {
4214  tmpLyr.multilineHeight = ddValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
4215  }
4216 
4218  {
4220  }
4221 
4222  }
4223 
4224  if ( ddValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
4225  {
4226  tmpLyr.addDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
4227  }
4228 
4229  if ( tmpLyr.addDirectionSymbol )
4230  {
4231 
4232  if ( ddValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
4233  {
4234  tmpLyr.leftDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
4235  }
4236  if ( ddValues.contains( QgsPalLayerSettings::DirSymbRight ) )
4237  {
4238  tmpLyr.rightDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
4239  }
4240 
4242  {
4244  }
4245 
4247  {
4249  }
4250 
4251  }
4252 }
4253 
4256 {
4257  //buffer draw
4258  if ( ddValues.contains( QgsPalLayerSettings::BufferDraw ) )
4259  {
4260  tmpLyr.bufferDraw = ddValues.value( QgsPalLayerSettings::BufferDraw ).toBool();
4261  }
4262 
4263  if ( !tmpLyr.bufferDraw )
4264  {
4265  // tmpLyr.bufferSize > 0.0 && tmpLyr.bufferTransp < 100 figured in during evaluation
4266  return; // don't continue looking for unused values
4267  }
4268 
4269  //buffer size
4270  if ( ddValues.contains( QgsPalLayerSettings::BufferSize ) )
4271  {
4272  tmpLyr.bufferSize = ddValues.value( QgsPalLayerSettings::BufferSize ).toDouble();
4273  }
4274 
4275  //buffer transparency
4276  if ( ddValues.contains( QgsPalLayerSettings::BufferTransp ) )
4277  {
4278  tmpLyr.bufferTransp = ddValues.value( QgsPalLayerSettings::BufferTransp ).toInt();
4279  }
4280 
4281  //buffer size units
4282  if ( ddValues.contains( QgsPalLayerSettings::BufferUnit ) )
4283  {
4285  tmpLyr.bufferSizeInMapUnits = ( bufunit == QgsPalLayerSettings::MapUnits );
4286  }
4287 
4288  //buffer color
4289  if ( ddValues.contains( QgsPalLayerSettings::BufferColor ) )
4290  {
4291  QVariant ddColor = ddValues.value( QgsPalLayerSettings::BufferColor );
4292  tmpLyr.bufferColor = ddColor.value<QColor>();
4293  }
4294 
4295  // apply any transparency
4296  tmpLyr.bufferColor.setAlphaF(( 100.0 - static_cast< double >( tmpLyr.bufferTransp ) ) / 100.0 );
4297 
4298  //buffer pen join style
4300  {
4301  tmpLyr.bufferJoinStyle = static_cast< Qt::PenJoinStyle >( ddValues.value( QgsPalLayerSettings::BufferJoinStyle ).toInt() );
4302  }
4303 
4304  //buffer blend mode
4306  {
4307  tmpLyr.bufferBlendMode = static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::BufferBlendMode ).toInt() );
4308  }
4309 }
4310 
4313 {
4314  //shape draw
4315  if ( ddValues.contains( QgsPalLayerSettings::ShapeDraw ) )
4316  {
4317  tmpLyr.shapeDraw = ddValues.value( QgsPalLayerSettings::ShapeDraw ).toBool();
4318  }
4319 
4320  if ( !tmpLyr.shapeDraw )
4321  {
4322  return; // don't continue looking for unused values
4323  }
4324 
4325  if ( ddValues.contains( QgsPalLayerSettings::ShapeKind ) )
4326  {
4327  tmpLyr.shapeType = static_cast< QgsPalLayerSettings::ShapeType >( ddValues.value( QgsPalLayerSettings::ShapeKind ).toInt() );
4328  }
4329 
4330  if ( ddValues.contains( QgsPalLayerSettings::ShapeSVGFile ) )
4331  {
4332  tmpLyr.shapeSVGFile = ddValues.value( QgsPalLayerSettings::ShapeSVGFile ).toString();
4333  }
4334 
4335  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeType ) )
4336  {
4337  tmpLyr.shapeSizeType = static_cast< QgsPalLayerSettings::SizeType >( ddValues.value( QgsPalLayerSettings::ShapeSizeType ).toInt() );
4338  }
4339 
4340  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeX ) )
4341  {
4342  tmpLyr.shapeSize.setX( ddValues.value( QgsPalLayerSettings::ShapeSizeX ).toDouble() );
4343  }
4344  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeY ) )
4345  {
4346  tmpLyr.shapeSize.setY( ddValues.value( QgsPalLayerSettings::ShapeSizeY ).toDouble() );
4347  }
4348 
4350  {
4351  tmpLyr.shapeSizeUnits = static_cast< QgsPalLayerSettings::SizeUnit >( ddValues.value( QgsPalLayerSettings::ShapeSizeUnits ).toInt() );
4352  }
4353 
4355  {
4357  }
4358 
4359  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotation ) )
4360  {
4361  tmpLyr.shapeRotation = ddValues.value( QgsPalLayerSettings::ShapeRotation ).toDouble();
4362  }
4363 
4364  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffset ) )
4365  {
4366  tmpLyr.shapeOffset = ddValues.value( QgsPalLayerSettings::ShapeOffset ).toPointF();
4367  }
4368 
4370  {
4371  tmpLyr.shapeOffsetUnits = static_cast< QgsPalLayerSettings::SizeUnit >( ddValues.value( QgsPalLayerSettings::ShapeOffsetUnits ).toInt() );
4372  }
4373 
4374  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadii ) )
4375  {
4376  tmpLyr.shapeRadii = ddValues.value( QgsPalLayerSettings::ShapeRadii ).toPointF();
4377  }
4378 
4380  {
4381  tmpLyr.shapeRadiiUnits = static_cast< QgsPalLayerSettings::SizeUnit >( ddValues.value( QgsPalLayerSettings::ShapeRadiiUnits ).toInt() );
4382  }
4383 
4385  {
4386  tmpLyr.shapeTransparency = ddValues.value( QgsPalLayerSettings::ShapeTransparency ).toInt();
4387  }
4388 
4390  {
4391  tmpLyr.shapeBlendMode = static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::ShapeBlendMode ).toInt() );
4392  }
4393 
4395  {
4396  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeFillColor );
4397  tmpLyr.shapeFillColor = ddColor.value<QColor>();
4398  }
4399 
4401  {
4403  tmpLyr.shapeBorderColor = ddColor.value<QColor>();
4404  }
4405 
4407  {
4408  tmpLyr.shapeBorderWidth = ddValues.value( QgsPalLayerSettings::ShapeBorderWidth ).toDouble();
4409  }
4410 
4412  {
4414  }
4415 
4417  {
4418  tmpLyr.shapeJoinStyle = static_cast< Qt::PenJoinStyle >( ddValues.value( QgsPalLayerSettings::ShapeJoinStyle ).toInt() );
4419  }
4420 }
4421 
4424 {
4425  //shadow draw
4426  if ( ddValues.contains( QgsPalLayerSettings::ShadowDraw ) )
4427  {
4428  tmpLyr.shadowDraw = ddValues.value( QgsPalLayerSettings::ShadowDraw ).toBool();
4429  }
4430 
4431  if ( !tmpLyr.shadowDraw )
4432  {
4433  return; // don't continue looking for unused values
4434  }
4435 
4436  if ( ddValues.contains( QgsPalLayerSettings::ShadowUnder ) )
4437  {
4438  tmpLyr.shadowUnder = static_cast< QgsPalLayerSettings::ShadowType >( ddValues.value( QgsPalLayerSettings::ShadowUnder ).toInt() );
4439  }
4440 
4442  {
4443  tmpLyr.shadowOffsetAngle = ddValues.value( QgsPalLayerSettings::ShadowOffsetAngle ).toInt();
4444  }
4445 
4447  {
4448  tmpLyr.shadowOffsetDist = ddValues.value( QgsPalLayerSettings::ShadowOffsetDist ).toDouble();
4449  }
4450 
4452  {
4454  }
4455 
4456  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadius ) )
4457  {
4458  tmpLyr.shadowRadius = ddValues.value( QgsPalLayerSettings::ShadowRadius ).toDouble();
4459  }
4460 
4462  {
4464  }
4465 
4467  {
4469  }
4470 
4471  if ( ddValues.contains( QgsPalLayerSettings::ShadowScale ) )
4472  {
4473  tmpLyr.shadowScale = ddValues.value( QgsPalLayerSettings::ShadowScale ).toInt();
4474  }
4475 
4476  if ( ddValues.contains( QgsPalLayerSettings::ShadowColor ) )
4477  {
4478  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShadowColor );
4479  tmpLyr.shadowColor = ddColor.value<QColor>();
4480  }
4481 
4483  {
4484  tmpLyr.shadowBlendMode = static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::ShadowBlendMode ).toInt() );
4485  }
4486 }
4487 
4488 
4489 
4491 {
4492  mEngine->run( context );
4493 }
4494 
4496 {
4497 }
4498 
4500 {
4502 }
4503 
4505 {
4507 }
4508 
4510 {
4511  return mEngine->takeResults();
4512 }
4513 
4514 void QgsPalLabeling::numCandidatePositions( int& candPoint, int& candLine, int& candPolygon )
4515 {
4516  mEngine->numCandidatePositions( candPoint, candLine, candPolygon );
4517 }
4518 
4519 void QgsPalLabeling::setNumCandidatePositions( int candPoint, int candLine, int candPolygon )
4520 {
4521  mEngine->setNumCandidatePositions( candPoint, candLine, candPolygon );
4522 }
4523 
4525 {
4526  mEngine->setSearchMethod( s );
4527 }
4528 
4530 {
4531  return mEngine->searchMethod();
4532 }
4533 
4535 {
4537 }
4538 
4540 {
4542 }
4543 
4545 {
4547 }
4548 
4550 {
4552 }
4553 
4555 {
4557 }
4558 
4560 {
4562 }
4563 
4565 {
4567 }
4568 
4570 {
4572 }
4573 
4575 {
4577 }
4578 
4580 {
4582 }
4583 
4585 {
4587 }
4588 
4590 {
4592 }
4593 
4595 {
4596  QgsPoint outPt = xform->transform( lp->getX(), lp->getY() );
4597 
4598  painter->save();
4599 
4600 #if 0 // TODO: generalize some of this
4601  double w = lp->getWidth();
4602  double h = lp->getHeight();
4603  double cx = lp->getX() + w / 2.0;
4604  double cy = lp->getY() + h / 2.0;
4605  double scale = 1.0 / xform->mapUnitsPerPixel();
4606  double rotation = xform->mapRotation();
4607  double sw = w * scale;
4608  double sh = h * scale;
4609  QRectF rect( -sw / 2, -sh / 2, sw, sh );
4610 
4611  painter->translate( xform->transform( QPointF( cx, cy ) ).toQPointF() );
4612  if ( rotation )
4613  {
4614  // Only if not horizontal
4615  if ( lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT &&
4616  lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT_OVER &&
4617  lp->getFeaturePart()->getLayer()->getArrangement() != P_HORIZ )
4618  {
4619  painter->rotate( rotation );
4620  }
4621  }
4622  painter->translate( rect.bottomLeft() );
4623  painter->rotate( -lp->getAlpha() * 180 / M_PI );
4624  painter->translate( -rect.bottomLeft() );
4625 #else
4626  QgsPoint outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
4627  QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
4628  painter->translate( QPointF( outPt.x(), outPt.y() ) );
4629  painter->rotate( -lp->getAlpha() * 180 / M_PI );
4630 #endif
4631 
4632  if ( lp->conflictsWithObstacle() )
4633  {
4634  painter->setPen( QColor( 255, 0, 0, 64 ) );
4635  }
4636  else
4637  {
4638  painter->setPen( QColor( 0, 0, 0, 64 ) );
4639  }
4640  painter->drawRect( rect );
4641  painter->restore();
4642 
4643  // save the rect
4644  rect.moveTo( outPt.x(), outPt.y() );
4645  if ( candidates )
4646  candidates->append( QgsLabelCandidate( rect, lp->cost() * 1000 ) );
4647 
4648  // show all parts of the multipart label
4649  if ( lp->getNextPart() )
4650  drawLabelCandidateRect( lp->getNextPart(), painter, xform, candidates );
4651 }
4652 
4653 
4655  const QgsLabelComponent& component,
4656  const QgsPalLayerSettings& tmpLyr )
4657 {
4658  QPainter* p = context.painter();
4659 
4660  double penSize = tmpLyr.scaleToPixelContext( tmpLyr.bufferSize, context,
4662 
4663  QPainterPath path;
4664  path.setFillRule( Qt::WindingFill );
4665  path.addText( 0, 0, tmpLyr.textFont, component.text() );
4666  QPen pen( tmpLyr.bufferColor );
4667  pen.setWidthF( penSize );
4668  pen.setJoinStyle( tmpLyr.bufferJoinStyle );
4669  QColor tmpColor( tmpLyr.bufferColor );
4670  // honor pref for whether to fill buffer interior
4671  if ( tmpLyr.bufferNoFill )
4672  {
4673  tmpColor.setAlpha( 0 );
4674  }
4675 
4676  // store buffer's drawing in QPicture for drop shadow call
4677  QPicture buffPict;
4678  QPainter buffp;
4679  buffp.begin( &buffPict );
4680  buffp.setPen( pen );
4681  buffp.setBrush( tmpColor );
4682  buffp.drawPath( path );
4683  buffp.end();
4684 
4685  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowBuffer )
4686  {
4687  QgsLabelComponent bufferComponent = component;
4688  bufferComponent.setOrigin( QgsPoint( 0.0, 0.0 ) );
4689  bufferComponent.setPicture( &buffPict );
4690  bufferComponent.setPictureBuffer( penSize / 2.0 );
4691  drawLabelShadow( context, bufferComponent, tmpLyr );
4692  }
4693 
4694  p->save();
4695  if ( context.useAdvancedEffects() )
4696  {
4697  p->setCompositionMode( tmpLyr.bufferBlendMode );
4698  }
4699 // p->setPen( pen );
4700 // p->setBrush( tmpColor );
4701 // p->drawPath( path );
4702 
4703  // scale for any print output or image saving @ specific dpi
4704  p->scale( component.dpiRatio(), component.dpiRatio() );
4705  _fixQPictureDPI( p );
4706  p->drawPicture( 0, 0, buffPict );
4707  p->restore();
4708 }
4709 
4711  QgsLabelComponent component,
4712  const QgsPalLayerSettings& tmpLyr )
4713 {
4714  QPainter* p = context.painter();
4715  double labelWidth = component.size().x(), labelHeight = component.size().y();
4716  //QgsDebugMsgLevel( QString( "Background label rotation: %1" ).arg( component.rotation() ), 4 );
4717 
4718  // shared calculations between shapes and SVG
4719 
4720  // configure angles, set component rotation and rotationOffset
4722  {
4723  component.setRotation( -( component.rotation() * 180 / M_PI ) ); // RotationSync
4724  component.setRotationOffset(
4726  }
4727  else // RotationFixed
4728  {
4729  component.setRotation( 0.0 ); // don't use label's rotation
4730  component.setRotationOffset( tmpLyr.shapeRotation );
4731  }
4732 
4733  // mm to map units conversion factor
4734  double mmToMapUnits = tmpLyr.shapeSizeMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor();
4735 
4736  // TODO: the following label-buffered generated shapes and SVG symbols should be moved into marker symbology classes
4737 
4738  if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeSVG )
4739  {
4740  // all calculations done in shapeSizeUnits, which are then passed to symbology class for painting
4741 
4742  if ( tmpLyr.shapeSVGFile.isEmpty() )
4743  return;
4744 
4745  double sizeOut = 0.0;
4746  // only one size used for SVG sizing/scaling (no use of shapeSize.y() or Y field in gui)
4748  {
4749  sizeOut = tmpLyr.shapeSize.x();
4750  }
4751  else if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeBuffer )
4752  {
4753  // add buffer to greatest dimension of label
4754  if ( labelWidth >= labelHeight )
4755  sizeOut = labelWidth;
4756  else if ( labelHeight > labelWidth )
4757  sizeOut = labelHeight;
4758 
4759  // label size in map units, convert to shapeSizeUnits, if different
4760  if ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM )
4761  {
4762  sizeOut /= mmToMapUnits;
4763  }
4764 
4765  // add buffer
4766  sizeOut += tmpLyr.shapeSize.x() * 2;
4767  }
4768 
4769  // don't bother rendering symbols smaller than 1x1 pixels in size
4770  // TODO: add option to not show any svgs under/over a certian size
4771  if ( tmpLyr.scaleToPixelContext( sizeOut, context, tmpLyr.shapeSizeUnits, false, tmpLyr.shapeSizeMapUnitScale ) < 1.0 )
4772  return;
4773 
4774  QgsStringMap map; // for SVG symbology marker
4776  map["size"] = QString::number( sizeOut );
4777  map["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
4779  map["angle"] = QString::number( 0.0 ); // angle is handled by this local painter
4780 
4781  // offset is handled by this local painter
4782  // TODO: see why the marker renderer doesn't seem to translate offset *after* applying rotation
4783  //map["offset"] = QgsSymbolLayerV2Utils::encodePoint( tmpLyr.shapeOffset );
4784  //map["offset_unit"] = QgsUnitTypes::encodeUnit(
4785  // tmpLyr.shapeOffsetUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
4786 
4787  map["fill"] = tmpLyr.shapeFillColor.name();
4788  map["outline"] = tmpLyr.shapeBorderColor.name();
4789  map["outline-width"] = QString::number( tmpLyr.shapeBorderWidth );
4790 
4791  // TODO: fix overriding SVG symbol's border width/units in QgsSvgCache
4792  // currently broken, fall back to symbol's
4793  //map["outline_width_unit"] = QgsUnitTypes::encodeUnit(
4794  // tmpLyr.shapeBorderWidthUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
4795 
4796  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowShape )
4797  {
4798  // configure SVG shadow specs
4799  QgsStringMap shdwmap( map );
4800  shdwmap["fill"] = tmpLyr.shadowColor.name();
4801  shdwmap["outline"] = tmpLyr.shadowColor.name();
4802  shdwmap["size"] = QString::number( sizeOut * tmpLyr.rasterCompressFactor );
4803 
4804  // store SVG's drawing in QPicture for drop shadow call
4805  QPicture svgPict;
4806  QPainter svgp;
4807  svgp.begin( &svgPict );
4808 
4809  // draw shadow symbol
4810 
4811  // clone current render context map unit/mm conversion factors, but not
4812  // other map canvas parameters, then substitute this painter for use in symbology painting
4813  // NOTE: this is because the shadow needs to be scaled correctly for output to map canvas,
4814  // but will be created relative to the SVG's computed size, not the current map canvas
4815  QgsRenderContext shdwContext;
4816  shdwContext.setMapToPixel( context.mapToPixel() );
4817  shdwContext.setScaleFactor( context.scaleFactor() );
4818  shdwContext.setPainter( &svgp );
4819 
4820  QgsSymbolLayerV2* symShdwL = QgsSvgMarkerSymbolLayerV2::create( shdwmap );
4821  QgsSvgMarkerSymbolLayerV2* svgShdwM = static_cast<QgsSvgMarkerSymbolLayerV2*>( symShdwL );
4822  QgsSymbolV2RenderContext svgShdwContext( shdwContext, QgsSymbolV2::Mixed,
4823  ( 100.0 - static_cast< double >( tmpLyr.shapeTransparency ) ) / 100.0 );
4824 
4825  double svgSize = tmpLyr.scaleToPixelContext( sizeOut, context, tmpLyr.shapeSizeUnits, true, tmpLyr.shapeSizeMapUnitScale );
4826  svgShdwM->renderPoint( QPointF( svgSize / 2, -svgSize / 2 ), svgShdwContext );
4827  svgp.end();
4828 
4829  component.setPicture( &svgPict );
4830  // TODO: when SVG symbol's border width/units is fixed in QgsSvgCache, adjust for it here
4831  component.setPictureBuffer( 0.0 );
4832 
4833  component.setSize( QgsPoint( svgSize, svgSize ) );
4834  component.setOffset( QgsPoint( 0.0, 0.0 ) );
4835 
4836  // rotate about origin center of SVG
4837  p->save();
4838  p->translate( component.center().x(), component.center().y() );
4839  p->rotate( component.rotation() );
4840  p->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
4841  double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits, true, tmpLyr.shapeOffsetMapUnitScale );
4842  double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits, true, tmpLyr.shapeOffsetMapUnitScale );
4843  p->translate( QPointF( xoff, yoff ) );
4844  p->rotate( component.rotationOffset() );
4845  p->translate( -svgSize / 2, svgSize / 2 );
4846 
4847  drawLabelShadow( context, component, tmpLyr );
4848  p->restore();
4849 
4850  delete svgShdwM;
4851  svgShdwM = nullptr;
4852  }
4853 
4854  // draw the actual symbol
4856  QgsSvgMarkerSymbolLayerV2* svgM = static_cast<QgsSvgMarkerSymbolLayerV2*>( symL );
4857  QgsSymbolV2RenderContext svgContext( context, QgsSymbolV2::Mixed,
4858  ( 100.0 - static_cast< double >( tmpLyr.shapeTransparency ) ) / 100.0 );
4859 
4860  p->save();
4861  if ( context.useAdvancedEffects() )
4862  {
4863  p->setCompositionMode( tmpLyr.shapeBlendMode );
4864  }
4865  p->translate( component.center().x(), component.center().y() );
4866  p->rotate( component.rotation() );
4867  double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits, false, tmpLyr.shapeOffsetMapUnitScale );
4868  double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits, false, tmpLyr.shapeOffsetMapUnitScale );
4869  p->translate( QPointF( xoff, yoff ) );
4870  p->rotate( component.rotationOffset() );
4871  svgM->renderPoint( QPointF( 0, 0 ), svgContext );
4872  p->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
4873  p->restore();
4874 
4875  delete svgM;
4876  svgM = nullptr;
4877 
4878  }
4879  else // Generated Shapes
4880  {
4881  // all calculations done in shapeSizeUnits
4882 
4883  double w = labelWidth / ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM ? mmToMapUnits : 1 );
4884  double h = labelHeight / ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM ? mmToMapUnits : 1 );
4885 
4886  double xsize = tmpLyr.shapeSize.x();
4887  double ysize = tmpLyr.shapeSize.y();
4888 
4890  {
4891  w = xsize;
4892  h = ysize;
4893  }
4894  else if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeBuffer )
4895  {
4897  {
4898  if ( w > h )
4899  h = w;
4900  else if ( h > w )
4901  w = h;
4902  }
4903  else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeCircle )
4904  {
4905  // start with label bound by circle
4906  h = sqrt( pow( w, 2 ) + pow( h, 2 ) );
4907  w = h;
4908  }
4909  else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeEllipse )
4910  {
4911  // start with label bound by ellipse
4912  h = h / sqrt( 2.0 ) * 2;
4913  w = w / sqrt( 2.0 ) * 2;
4914  }
4915 
4916  w += xsize * 2;
4917  h += ysize * 2;
4918  }
4919 
4920  // convert everything over to map pixels from here on
4921  w = tmpLyr.scaleToPixelContext( w, context, tmpLyr.shapeSizeUnits, true, tmpLyr.shapeSizeMapUnitScale );
4922  h = tmpLyr.scaleToPixelContext( h, context, tmpLyr.shapeSizeUnits, true, tmpLyr.shapeSizeMapUnitScale );
4923 
4924  // offsets match those of symbology: -x = left, -y = up
4925  QRectF rect( -w / 2.0, - h / 2.0, w, h );
4926 
4927  if ( rect.isNull() )
4928  return;
4929 
4930  p->save();
4931  p->translate( QPointF( component.center().x(), component.center().y() ) );
4932  p->rotate( component.rotation() );
4933  double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits, false, tmpLyr.shapeOffsetMapUnitScale );
4934  double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits, false, tmpLyr.shapeOffsetMapUnitScale );
4935  p->translate( QPointF( xoff, yoff ) );
4936  p->rotate( component.rotationOffset() );
4937 
4938  double penSize = tmpLyr.scaleToPixelContext( tmpLyr.shapeBorderWidth, context, tmpLyr.shapeBorderWidthUnits, true, tmpLyr.shapeBorderWidthMapUnitScale );
4939 
4940  QPen pen;
4941  if ( tmpLyr.shapeBorderWidth > 0 )
4942  {
4943  pen.setColor( tmpLyr.shapeBorderColor );
4944  pen.setWidthF( penSize );
4946  pen.setJoinStyle( tmpLyr.shapeJoinStyle );
4947  }
4948  else
4949  {
4950  pen = Qt::NoPen;
4951  }
4952 
4953  // store painting in QPicture for shadow drawing
4954  QPicture shapePict;
4955  QPainter shapep;
4956  shapep.begin( &shapePict );
4957  shapep.setPen( pen );
4958  shapep.setBrush( tmpLyr.shapeFillColor );
4959 
4962  {
4964  {
4965  shapep.drawRoundedRect( rect, tmpLyr.shapeRadii.x(), tmpLyr.shapeRadii.y(), Qt::RelativeSize );
4966  }
4967  else
4968  {
4969  double xRadius = tmpLyr.scaleToPixelContext( tmpLyr.shapeRadii.x(), context, tmpLyr.shapeRadiiUnits, true, tmpLyr.shapeRadiiMapUnitScale );
4970  double yRadius = tmpLyr.scaleToPixelContext( tmpLyr.shapeRadii.y(), context, tmpLyr.shapeRadiiUnits, true, tmpLyr.shapeRadiiMapUnitScale );
4971  shapep.drawRoundedRect( rect, xRadius, yRadius );
4972  }
4973  }
4974  else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeEllipse
4976  {
4977  shapep.drawEllipse( rect );
4978  }
4979  shapep.end();
4980 
4981  p->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
4982 
4983  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowShape )
4984  {
4985  component.setPicture( &shapePict );
4986  component.setPictureBuffer( penSize / 2.0 );
4987 
4988  component.setSize( QgsPoint( rect.width(), rect.height() ) );
4989  component.setOffset( QgsPoint( rect.width() / 2, -rect.height() / 2 ) );
4990  drawLabelShadow( context, component, tmpLyr );
4991  }
4992 
4993  p->setOpacity(( 100.0 - static_cast< double >( tmpLyr.shapeTransparency ) ) / 100.0 );
4994  if ( context.useAdvancedEffects() )
4995  {
4996  p->setCompositionMode( tmpLyr.shapeBlendMode );
4997  }
4998 
4999  // scale for any print output or image saving @ specific dpi
5000  p->scale( component.dpiRatio(), component.dpiRatio() );
5001  _fixQPictureDPI( p );
5002  p->drawPicture( 0, 0, shapePict );
5003  p->restore();
5004  }
5005 }
5006 
5008  const QgsLabelComponent& component,
5009  const QgsPalLayerSettings& tmpLyr )
5010 {
5011  // incoming component sizes should be multiplied by rasterCompressFactor, as
5012  // this allows shadows to be created at paint device dpi (e.g. high resolution),
5013  // then scale device painter by 1.0 / rasterCompressFactor for output
5014 
5015  QPainter* p = context.painter();
5016  double componentWidth = component.size().x(), componentHeight = component.size().y();
5017  double xOffset = component.offset().x(), yOffset = component.offset().y();
5018  double pictbuffer = component.pictureBuffer();
5019 
5020  // generate pixmap representation of label component drawing
5021  bool mapUnits = ( tmpLyr.shadowRadiusUnits == QgsPalLayerSettings::MapUnits );
5022  double radius = tmpLyr.scaleToPixelContext( tmpLyr.shadowRadius, context, tmpLyr.shadowRadiusUnits, !mapUnits, tmpLyr.shadowRadiusMapUnitScale );
5023  radius /= ( mapUnits ? tmpLyr.vectorScaleFactor / component.dpiRatio() : 1 );
5024  radius = static_cast< int >( radius + 0.5 );
5025 
5026  // TODO: add labeling gui option to adjust blurBufferClippingScale to minimize pixels, or
5027  // to ensure shadow isn't clipped too tight. (Or, find a better method of buffering)
5028  double blurBufferClippingScale = 3.75;
5029  int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
5030 
5031  QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
5032  componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
5033  QImage::Format_ARGB32_Premultiplied );
5034 
5035  // TODO: add labeling gui option to not show any shadows under/over a certian size
5036  // keep very small QImages from causing paint device issues, i.e. must be at least > 1
5037  int minBlurImgSize = 1;
5038  // max limitation on QgsSvgCache is 10,000 for screen, which will probably be reasonable for future caching here, too
5039  // 4 x QgsSvgCache limit for output to print/image at higher dpi
5040  // TODO: should it be higher, scale with dpi, or have no limit? Needs testing with very large labels rendered at high dpi output
5041  int maxBlurImgSize = 40000;
5042  if ( blurImg.isNull()
5043  || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
5044  || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
5045  return;
5046 
5047  blurImg.fill( QColor( Qt::transparent ).rgba() );
5048  QPainter pictp;
5049  if ( !pictp.begin( &blurImg ) )
5050  return;
5051  pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
5052  QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
5053  blurbuffer + pictbuffer + componentHeight + yOffset );
5054 
5055  pictp.drawPicture( imgOffset,
5056  *component.picture() );
5057 
5058  // overlay shadow color
5059  pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
5060  pictp.fillRect( blurImg.rect(), tmpLyr.shadowColor );
5061  pictp.end();
5062 
5063  // blur the QImage in-place
5064  if ( tmpLyr.shadowRadius > 0.0 && radius > 0 )
5065  {
5066  QgsSymbolLayerV2Utils::blurImageInPlace( blurImg, blurImg.rect(), radius, tmpLyr.shadowRadiusAlphaOnly );
5067  }
5068 
5069  if ( tmpLyr.showingShadowRects ) // engine setting, not per layer
5070  {
5071  // debug rect for QImage shadow registration and clipping visualization
5072  QPainter picti;
5073  picti.begin( &blurImg );
5074  picti.setBrush( Qt::Dense7Pattern );
5075  QPen imgPen( QColor( 0, 0, 255, 255 ) );
5076  imgPen.setWidth( 1 );
5077  picti.setPen( imgPen );
5078  picti.setOpacity( 0.1 );
5079  picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
5080  picti.end();
5081  }
5082 
5083  double offsetDist = tmpLyr.scaleToPixelContext( tmpLyr.shadowOffsetDist, context, tmpLyr.shadowOffsetUnits, true, tmpLyr.shadowOffsetMapUnitScale );
5084  double angleRad = tmpLyr.shadowOffsetAngle * M_PI / 180; // to radians
5085  if ( tmpLyr.shadowOffsetGlobal )
5086  {
5087  // TODO: check for differences in rotation origin and cw/ccw direction,
5088  // when this shadow function is used for something other than labels
5089 
5090  // it's 0-->cw-->360 for labels
5091  //QgsDebugMsgLevel( QString( "Shadow aggregated label rotation (degrees): %1" ).arg( component.rotation() + component.rotationOffset() ), 4 );
5092  angleRad -= ( component.rotation() * M_PI / 180 + component.rotationOffset() * M_PI / 180 );
5093  }
5094 
5095  QPointF transPt( -offsetDist * cos( angleRad + M_PI / 2 ),
5096  -offsetDist * sin( angleRad + M_PI / 2 ) );
5097 
5098  p->save();
5099  p->setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
5100  if ( context.useAdvancedEffects() )
5101  {
5102  p->setCompositionMode( tmpLyr.shadowBlendMode );
5103  }
5104  p->setOpacity(( 100.0 - static_cast< double >( tmpLyr.shadowTransparency ) ) / 100.0 );
5105 
5106  double scale = static_cast< double >( tmpLyr.shadowScale ) / 100.0;
5107  // TODO: scale from center/center, left/center or left/top, instead of default left/bottom?
5108  p->scale( scale, scale );
5109  if ( component.useOrigin() )
5110  {
5111  p->translate( component.origin().x(), component.origin().y() );
5112  }
5113  p->translate( transPt );
5114  p->translate( -imgOffset.x(),
5115  -imgOffset.y() );
5116  p->drawImage( 0, 0, blurImg );
5117  p->restore();
5118 
5119  // debug rects
5120  if ( tmpLyr.showingShadowRects ) // engine setting, not per layer
5121  {
5122  // draw debug rect for QImage painting registration
5123  p->save();
5124  p->setBrush( Qt::NoBrush );
5125  QPen imgPen( QColor( 255, 0, 0, 10 ) );
5126  imgPen.setWidth( 2 );
5127  imgPen.setStyle( Qt::DashLine );
5128  p->setPen( imgPen );
5129  p->scale( scale, scale );
5130  if ( component.useOrigin() )
5131  {
5132  p->translate( component.origin().x(), component.origin().y() );
5133  }
5134  p->translate( transPt );
5135  p->translate( -imgOffset.x(),
5136  -imgOffset.y() );
5137  p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
5138  p->restore();
5139 
5140  // draw debug rect for passed in component dimensions
5141  p->save();
5142  p->setBrush( Qt::NoBrush );
5143  QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
5144  componentRectPen.setWidth( 1 );
5145  if ( component.useOrigin() )
5146  {
5147  p->translate( component.origin().x(), component.origin().y() );
5148  }
5149  p->setPen( componentRectPen );
5150  p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
5151  p->restore();
5152  }
5153 }
5154 
5156 {
5158 }
5159 
5161 {
5163 }
5164 
5166 {
5167  QgsProject::instance()->removeEntry( "PAL", "/SearchMethod" );
5168  QgsProject::instance()->removeEntry( "PAL", "/CandidatesPoint" );
5169  QgsProject::instance()->removeEntry( "PAL", "/CandidatesLine" );
5170  QgsProject::instance()->removeEntry( "PAL", "/CandidatesPolygon" );
5171  QgsProject::instance()->removeEntry( "PAL", "/ShowingCandidates" );
5172  QgsProject::instance()->removeEntry( "PAL", "/ShowingShadowRects" );
5173  QgsProject::instance()->removeEntry( "PAL", "/ShowingAllLabels" );
5174  QgsProject::instance()->removeEntry( "PAL", "/ShowingPartialsLabels" );
5175  QgsProject::instance()->removeEntry( "PAL", "/DrawOutlineLabels" );
5176 }
5177 
5179 {
5180  QgsPalLabeling* lbl = new QgsPalLabeling();
5187  return lbl;
5188 }
5189 
5190 
5192 {
5193  mLabelSearchTree = new QgsLabelSearchTree();
5194 }
5195 
5197 {
5198  delete mLabelSearchTree;
5199  mLabelSearchTree = nullptr;
5200 }
5201 
5203 {
5204  QList<QgsLabelPosition> positions;
5205 
5206  QList<QgsLabelPosition*> positionPointers;
5207  if ( mLabelSearchTree )
5208  {
5209  mLabelSearchTree->label( p, positionPointers );
5210  QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
5211  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
5212  {
5213  positions.push_back( QgsLabelPosition( **pointerIt ) );
5214  }
5215  }
5216 
5217  return positions;
5218 }
5219 
5221 {
5222  QList<QgsLabelPosition> positions;
5223 
5224  QList<QgsLabelPosition*> positionPointers;
5225  if ( mLabelSearchTree )
5226  {
5227  mLabelSearchTree->labelsInRect( r, positionPointers );
5228  QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
5229  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
5230  {
5231  positions.push_back( QgsLabelPosition( **pointerIt ) );
5232  }
5233  }
5234 
5235  return positions;
5236 }
const QgsMapSettings & mapSettings()
bridge to QgsMapSettings
virtual void registerDiagramFeature(const QString &layerID, QgsFeature &feat, QgsRenderContext &context) override
called for every diagram feature
Label below point, slightly right of center.
static QString encodeOutputUnit(QgsSymbolV2::OutputUnit unit)
static void _fixQPictureDPI(QPainter *p)
QgsPoint transform(const QgsPoint &p, TransformDirection direction=ForwardTransform) const
Transform the point from Source Coordinate System to Destination Coordinate System If the direction i...
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
Class for parsing and evaluation of expressions (formerly called "search strings").
void setShowingCandidates(bool showing)
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
void setActive(bool active)
void setOpacity(qreal opacity)
A rectangle specified with double values.
Definition: qgsrectangle.h:35
static QgsGeometry * prepareGeometry(const QgsGeometry *geometry, QgsRenderContext &context, const QgsCoordinateTransform *ct, QgsGeometry *clipGeometry=nullptr)
Prepares a geometry for registration with PAL.
double rendererScale() const
Label on bottom-left of point.
virtual void registerFeature(const QString &layerID, QgsFeature &feat, QgsRenderContext &context) override
Register a feature for labelling.
void setStyle(Qt::PenStyle style)
QgsPoint center() const
Center point of the rectangle.
Definition: qgsrectangle.h:217
QHash< QString, QgsVectorLayerLabelProvider * > mLabelProviders
hashtable of label providers, being filled during labeling (key = layer ID)
QString & append(QChar ch)
static void dataDefinedShapeBackground(QgsPalLayerSettings &tmpLyr, const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > &ddValues)
double dpiRatio() const
bool isShowingPartialsLabels() const
iterator erase(iterator pos)
int size() const
Return number of items.
Definition: qgsfield.cpp:370
QgsMapUnitScale shapeSizeMapUnitScale
static void dataDefinedTextStyle(QgsPalLayerSettings &tmpLyr, const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > &ddValues)
bool drawLabelRectOnly() const
Returns whether the engine will only draw the outline rectangles of labels, not the label contents th...
A container class for data source field mapping or expression.
QgsMapUnitScale shadowRadiusMapUnitScale
bool end()
bool contains(const Key &key) const
GeometryType
Definition: qgis.h:111
Q_DECL_DEPRECATED QVariant evaluate(const QgsFeature *f)
Evaluate the feature and return the result.
int pixelSize() const
double rotationOffset() const
void setOrigin(const QgsPoint &point)
void fillRect(const QRectF &rectangle, const QBrush &brush)
void setCompositionMode(CompositionMode mode)
virtual bool willUseLayer(QgsVectorLayer *layer) override
called to find out whether the layer is used for labeling
virtual bool prepare(const QgsRenderContext &context, QStringList &attributeNames)
Prepare for registration of features.
void setNumCandidatePositions(int candPoint, int candLine, int candPolygon)
double rotation() const
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspoint.cpp:121
QDomNode appendChild(const QDomNode &newChild)
bool expressionIsPrepared() const
Returns whether the data defined object&#39;s expression is prepared.
void push_back(const T &value)
QString name() const
QgsLabelingResults * takeResults()
Return pointer to recently computed results (in drawLabeling()) and pass the ownership of results to ...
QString attribute(const QString &name, const QString &defValue) const
A class to query the labeling structure at a given point (small wraper around pal RTree class) ...
QMap< QString, QString > dataDefinedMap(QgsPalLayerSettings::DataDefinedProperties p) const
Get property value as separate values split into Qmap.
double scaleToPixelContext(double size, const QgsRenderContext &c, SizeUnit unit, bool rasterfactor=false, const QgsMapUnitScale &mapUnitScale=QgsMapUnitScale()) const
Calculates size (considering output size should be in pixel or map units, scale factors and optionall...
UpsideDownLabels upsidedownLabels
double obstacleFactor
Obstacle factor, where 1.0 = default, < 1.0 more likely to be covered by labels, 1.0 less likely to be covered
Label on top-left of point.
qreal pointSizeF() const
void setShowingPartialsLabels(bool showing)
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setSearchMethod(Search s)
int weight() const
void loadEngineSettings()
load/save engine settings to project file
Whether to show debugging rectangles for drop shadows.
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
static QString encodeColor(const QColor &color)
static void drawLabelCandidateRect(pal::LabelPosition *lp, QPainter *painter, const QgsMapToPixel *xform, QList< QgsLabelCandidate > *candidates=nullptr)
double getY(int i=0) const
get the down-left y coordinate
QPainter::CompositionMode bufferBlendMode
QString & prepend(QChar ch)
void renderPoint(QPointF point, QgsSymbolV2RenderContext &context) override
QgsMapUnitScale shadowOffsetMapUnitScale
void scale(qreal sx, qreal sy)
void addProvider(QgsAbstractLabelProvider *provider)
Add provider of label features. Takes ownership of the provider.
const_iterator constBegin() const
const T & at(int i) const
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
bool dataDefinedUseExpression(QgsPalLayerSettings::DataDefinedProperties p) const
Whether data definition is set to use an expression.
QDomElement toXmlElement(QDomDocument &document, const QString &elementName) const
Returns a DOM element containing the properties of the data defined container.
QuadrantPosition quadOffset
void setUnderline(bool enable)
static void drawLabelBuffer(QgsRenderContext &context, const QgsLabelComponent &component, const QgsPalLayerSettings &tmpLyr)
static QgsMapRenderer::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
virtual Q_DECL_DEPRECATED int addDiagramLayer(QgsVectorLayer *layer, const QgsDiagramLayerSettings *s) override
adds a diagram layer to the labeling engine
Class that adds extra information to QgsLabelFeature for text labels.
void save()
QString evalErrorString() const
Returns evaluation error.
void numCandidatePositions(int &candPoint, int &candLine, int &candPolygon)
double computeMapUnitsPerPixel(const QgsRenderContext &c) const
Computes a map units per pixel scaling factor, respecting the minimum and maximum scales set for the ...
The QgsLabelingEngineV2 class provides map labeling functionality.
bool dataDefinedIsActive(QgsPalLayerSettings::DataDefinedProperties p) const
Whether data definition is active.
void setDefinedFont(const QFont &f)
Set font to be used for rendering.
QgsExpression * expression()
T value() const
Container of fields for a vector layer.
Definition: qgsfield.h:187
Label on top of point, slightly right of center.
static QgsPalLayerSettings fromLayer(QgsVectorLayer *layer)
The QgsVectorLayerLabelProvider class implements a label provider for vector layers.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
void setJoinStyle(Qt::PenJoinStyle style)
void setAlpha(int alpha)
bool drawLabels
Whether to draw labels for this layer.
QHash< QString, QgsVectorLayerDiagramProvider * > mDiagramProviders
hashtable of diagram providers (key = layer ID)
void readFromLayer(QgsVectorLayer *layer)
static QPointF decodePoint(const QString &str)
const_iterator constFind(const Key &key) const
const QgsGeometry * constGeometry() const
Gets a const pointer to the geometry object associated with this feature.
Definition: qgsfeature.cpp:82
static void dataDefinedDropShadow(QgsPalLayerSettings &tmpLyr, const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > &ddValues)
Capitalization capitalization() const
QgsMapUnitScale repeatDistanceMapUnitScale
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
MultiLineAlign multilineAlign
void removeDataDefinedProperty(QgsPalLayerSettings::DataDefinedProperties p)
Set a property to static instead data defined.
QString join(const QString &separator) const
bool isNull() const
FeaturePart * getFeaturePart()
return the feature corresponding to this labelposition
void readSettingsFromProject()
Read configuration of the labeling engine from the current project file.
void rotate(qreal angle)
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
bool contains(const QgsPoint *p) const
Test for containment of a point (uses GEOS)
void addText(const QPointF &point, const QFont &font, const QString &text)
A non GUI class for rendering a map layer set onto a QPainter.
static bool geometryRequiresPreparation(const QgsGeometry *geometry, QgsRenderContext &context, const QgsCoordinateTransform *ct, QgsGeometry *clipGeometry=nullptr)
Checks whether a geometry requires preparation before registration with PAL.
void setNumCandidatePositions(int candPoint, int candLine, int candPolygon)
Set number of candidate positions that will be generated for each label feature.
bool useOrigin() const
QVector< PredefinedPointPosition > predefinedPositionOrder
Ordered list of predefined label positions for points.
void clear()
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
double toDouble(bool *ok) const
static QStringList splitToLines(const QString &text, const QString &wrapCharacter)
Splits a text string to a list of separate lines, using a specified wrap character.
virtual bool prepare(const QgsRenderContext &context, QStringList &attributeNames)
Prepare for registration of features.
QString parserErrorString() const
Returns parser error.
qreal width(const QString &text) const
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
double maxScale
The maximum scale, or 0.0 if unset.
QString name() const
Gets the name of the field.
Definition: qgsfield.cpp:84
void setRotationOffset(const double rotation)
bool isGeosValid() const
Checks validity of the geometry using GEOS.
const QgsPoint & offset() const
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:385
bool bold() const
static void drawLabelBackground(QgsRenderContext &context, QgsLabelComponent component, const QgsPalLayerSettings &tmpLyr)
Qt::PenJoinStyle bufferJoinStyle
int size() const
double y() const
Get the y value of the point.
Definition: qgspoint.h:136
QgsMapLayer * mapLayer(const QString &theLayerId)
Retrieve a pointer to a loaded layer by id.
double zIndex
Z-Index of label, where labels with a higher z-index are rendered on top of labels with a lower z-ind...
double mapRotation() const
Return current map rotation in degrees.
void setHasFixedPosition(bool enabled)
Set whether the label should use a fixed position instead of being automatically placed.
bool italic() const
void reset(T *other)
bool useExpression() const
Returns if the field or the expression part is active.
QgsMapUnitScale fontSizeMapUnitScale
The QgsMapSettings class contains configuration for rendering of the map.
QgsMapUnitScale shapeBorderWidthMapUnitScale
QString styleName() const
virtual Q_DECL_DEPRECATED void init(QgsMapRenderer *mr) override
called when we&#39;re going to start with rendering
void removeProvider(QgsAbstractLabelProvider *provider)
Remove provider if the provider&#39;s initialization failed. Provider instance is deleted.
static bool staticWillUseLayer(QgsVectorLayer *layer)
called to find out whether the layer is used for labeling
void setBold(bool enable)
virtual void clearActiveLayer(const QString &layerID) override
clears data defined objects from PAL layer settings for a registered layer
QgsPoint transform(const QgsPoint &p) const
Transform the point from map (world) coordinates to device coordinates.
QgsGeometry * extentGeom
void drawRect(const QRectF &rectangle)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:34
void setPixelSize(int pixelSize)
void setUseExpression(bool use)
Controls if the field or the expression part is active.
void setSearchMethod(QgsPalLabeling::Search s)
Set which search method to use for removal collisions between labels.
Mixed units in symbol layers.
Definition: qgssymbolv2.h:66
double cost() const
Returns the candidate label position&#39;s geographical cost.
The output shall be in millimeters.
Definition: qgssymbolv2.h:64
static QPainter::CompositionMode decodeBlendMode(const QString &s)
ObstacleType obstacleType
Controls how features act as obstacles for labels.
void setRotation(const double rotation)
QFont font()
QString number(int n, int base)
bool isDrawingOutlineLabels() const
qreal x() const
qreal y() const
void append(const T &value)
static void dataDefinedTextBuffer(QgsPalLayerSettings &tmpLyr, const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > &ddValues)
void setIsObstacle(bool enabled)
Sets whether the feature will act as an obstacle for labels.
bool setFromXmlElement(const QDomElement &element)
Sets the properties of the data defined container from an XML element.
void setScaleFactor(double factor)
QString id() const
Get this layer&#39;s unique ID, this ID is used to access this layer from map layer registry.
Label on left of point.
uint toUInt(bool *ok) const
static QString capitalize(const QString &string, Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
QDomDocument ownerDocument() const
const QgsCoordinateTransform * ct
int toInt(bool *ok) const
bool isNull() const
QgsMapUnitScale shapeRadiiMapUnitScale
const Key & key() const
Offset distance applies from point geometry.
bool isShowingShadowRectangles() const
void fill(uint pixelValue)
void setFillRule(Qt::FillRule fillRule)
static QString encodePoint(QPointF point)
bool hasAttribute(const QString &name) const
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
const QgsRectangle & extent() const
QgsPalLayerSettings mInvalidLayerSettings
SizeUnit shapeBorderWidthUnits
QPainter::CompositionMode blendMode
Whether to draw rectangles of generated candidates (good for debugging)
int red() const
static bool fontFamilyOnSystem(const QString &family)
Check whether font family is on system in a quick manner, which does not compare [foundry].
void setPen(const QColor &color)
Labels can be placed above a line feature.
int width() const
void drawEllipse(const QRectF &rectangle)
qreal letterSpacing() const
void setAttribute(const QString &name, const QString &value)
void setField(const QString &field)
Set the field name which this QgsDataDefined represents.
The QgsVectorLayerDiagramProvider class implements support for diagrams within the labeling engine...
QString updateDataDefinedString(const QString &value)
Convert old property value to new one as delimited values.
double getHeight() const
void drawRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius, Qt::SizeMode mode)
QgsGeometry * buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:207
QString expressionString() const
Returns the expression string of this QgsDataDefined.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspointv2.h:34
int toInt(bool *ok, int base) const
bool isEmpty() const
void setDataDefinedValues(const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > &values)
Set data-defined values.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
virtual void drawLabeling(QgsRenderContext &context) override
called when the map is drawn and labels should be placed
Q_DECL_DEPRECATED QgsPalLayerSettings & layer(const QString &layerName) override
bool isEmpty() const
QRect rect() const
QString trimmed() const
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
const_iterator constEnd() const
static QString symbolNameToPath(QString name)
Get symbol&#39;s path from its name.
The output shall be in map unitx.
Definition: qgssymbolv2.h:65
#define M_PI
Arranges candidates in a circle around a point (or centroid of a polygon).
LabelPosition * getNextPart() const
const QgsMapToPixel * xform
void calculateInfo(bool curvedLabeling, QFontMetricsF *fm, const QgsMapToPixel *xform, double fontScale, double maxinangle, double maxoutangle)
calculate data for info(). setDefinedFont() must have been called already.
QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined *> dataDefinedProperties
Map of current data defined properties.
QPaintDevice * device() const
void setWidthF(qreal width)
void readXml(QDomElement &elem)
Read settings from a DOM element.
void setBrush(const QBrush &brush)
void setPainter(QPainter *p)
bool removeEntry(const QString &scope, const QString &key)
Remove the given key.
const T & value() const
bool underline() const
QgsMapUnitScale bufferSizeMapUnitScale
QString exportToWkt(int precision=17) const
Exports the geometry to WKT.
OffsetType
Behaviour modifier for label offset and distance, only applies in some label placement modes...
QgsFeature * mCurFeat
QGis::GeometryType geometryType() const
Returns point, line or polygon.
bool isShowingAllLabels() const
void calculateLabelSize(const QFontMetricsF *fm, QString text, double &labelX, double &labelY, QgsFeature *f=nullptr, QgsRenderContext *context=nullptr)
void registerFeature(QgsFeature &f, QgsRenderContext &context, QgsLabelFeature **labelFeature=nullptr, QgsGeometry *obstacleGeometry=nullptr)
Register a feature for labelling.
void removeCustomProperty(const QString &key)
Remove a custom property from layer.
QPainter::CompositionMode shapeBlendMode
iterator end()
const Key & key() const
const QgsAbstractVectorLayerLabeling * labeling() const
Access to labeling configuration.
void setColor(const QColor &color)
QGis::GeometryType type() const
Returns type of the geometry as a QGis::GeometryType.
T & value() const
double mapUnitsPerPixel() const
Return current map units per pixel.
QgsDataDefined * dataDefinedProperty(QgsPalLayerSettings::DataDefinedProperties p)
Get a data defined property pointer.
Stores visual margins for labels (left, right, top and bottom)
const T & value() const
static QPainter::CompositionMode getCompositionMode(BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
SizeUnit
Units used for option sizes, before being converted to rendered sizes.
int alpha() const
const QgsPoint & center() const
A class to represent a point.
Definition: qgspoint.h:65
QVariant dataDefinedValue(QgsPalLayerSettings::DataDefinedProperties p, QgsFeature &f, const QgsFields &fields, const QgsExpressionContext *context=nullptr) const
Get data defined property value from expression string or attribute field name.
QgsPalLabeling::Search searchMethod() const
Which search method to use for removal collisions between labels.
static Qt::PenJoinStyle _decodePenJoinStyle(const QString &str)
Qt::PenJoinStyle shapeJoinStyle
Convert just the first letter of each word to uppercase, leave the rest untouched.
void writeSettingsToProject()
Write configuration of the labeling engine to the current project file.
virtual QgsPalLabeling * clone() override
called when passing engine among map renderers
double length() const
Returns the length of geometry using GEOS.
Convert all characters to uppercase.
int logicalDpiX() const
int logicalDpiY() const
T * data() const
int green() const
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
QList< QgsLabelPosition > labelsWithinRect(const QgsRectangle &r) const
return infos about labels within a given (map) rectangle
const QgsPoint & origin() const
const T value(const Key &key) const
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:65
Capitalization
Capitalization options.
void setWordSpacing(qreal spacing)
bool isNull() const
QgsLabelingResults * results() const
For internal use by the providers.
bool contains(QChar ch, Qt::CaseSensitivity cs) const
void clear()
double pictureBuffer() const
void setMapSettings(const QgsMapSettings &mapSettings)
Associate map settings instance.
QString expression() const
Return the original, unmodified expression string.
QgsExpressionContext & expressionContext()
Gets the expression context.
QString field() const
Get the field which this QgsDataDefined represents.
void run(QgsRenderContext &context)
compute the labeling with given map settings and providers
void setItalic(bool enable)
void setPointSizeF(qreal pointSize)
Q_GUI_EXPORT int qt_defaultDpiX()
bool isNull() const
unsigned int placementFlags
Whether to draw all labels even if there would be collisions.
QgsPalLayerSettings & operator=(const QgsPalLayerSettings &s)
copy operator - only copies the permanent members
Whether to use also label candidates that are partially outside of the map view.
void setRenderHints(QFlags< QPainter::RenderHint > hints, bool on)
void restore()
Placement
Placement modes which determine how label candidates are generated for a feature. ...
bool isShowingCandidates() const
static QString encodePredefinedPositionOrder(const QVector< QgsPalLayerSettings::PredefinedPointPosition > &positions)
Encodes an ordered list of predefined point label positions to a string.
static QString untranslateNamedStyle(const QString &namedStyle)
Returns the english named style of a font, if possible.
static QColor _readColor(QgsVectorLayer *layer, const QString &property, const QColor &defaultColor=Qt::black, bool withAlpha=true)
Q_GUI_EXPORT int qt_defaultDpiY()
int blue() const
QgsExpression * getLabelExpression()
Returns the QgsExpression for this label settings.
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point...
void setDrawingOutlineLabels(bool outline)
const QgsPoint & size() const
Contains information about the context of a rendering operation.
int indexFromName(const QString &name) const
Look up field&#39;s index from name. Returns -1 on error.
Definition: qgsfield.cpp:424
void setShowingShadowRectangles(bool showing)
void setPicture(QPicture *picture)
virtual int prepareDiagramLayer(QgsVectorLayer *layer, QStringList &attrNames, QgsRenderContext &ctx) override
adds a diagram layer to the labeling engine
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
virtual const QgsFields & fields() const =0
Return a map of indexes with field names for this layer.
void setDataDefinedProperty(QgsPalLayerSettings::DataDefinedProperties p, bool active, bool useExpr, const QString &expr, const QString &field)
Set a property as data defined.
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, QFlags< Qt::ImageConversionFlag > flags)
QgsAbstractGeometryV2 * geometry() const
Returns the underlying geometry store.
QPainter * painter()
The QgsLabelFeature class describes a feature that should be used within the labeling engine...
qreal width() const
const QgsMapToPixel & mapToPixel() const
QString mid(int position, int n) const
void drawPath(const QPainterPath &path)
Whether to only draw the label rect and not the actual label text (used for unit tests) ...
double getAlpha() const
get alpha
void setOffset(const QgsPoint &point)
static QgsPalLayerSettings::SizeUnit _decodeUnits(const QString &str)
static GEOSContextHandle_t getGEOSHandler()
Return GEOS context handle.
void setWidth(int width)
Mixed case, ie no change.
QgsMapUnitScale distMapUnitScale
QString toString() const
Struct for storing maximum and minimum scales for measurements in map units.
qreal ascent() const
bool fitInPolygonOnly
True if only labels which completely fit within a polygon are allowed.
double getWidth() const
virtual Q_DECL_DEPRECATED QList< QgsLabelPosition > labelsWithinRect(const QgsRectangle &r) override
return infos about labels within a given (map) rectangle
bool conflictsWithObstacle() const
Returns whether the position is marked as conflicting with an obstacle feature.
bool isEmpty() const
double getX(int i=0) const
get the down-left x coordinate
bool isEmpty() const
Search searchMethod() const
QString family() const
int rotate(double rotation, const QgsPoint &center)
Rotate this geometry around the Z axis.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
void setX(qreal x)
void setY(qreal y)
bool useAdvancedEffects() const
Returns true if advanced effects such as blend modes such be used.
OffsetType offsetType
Offset type for layer (only applies in certain placement modes)
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:381
QDomElement firstChildElement(const QString &tagName) const
const GEOSGeometry * asGeos(double precision=0) const
Returns a geos geometry.
QgsRectangle boundingBox() const
Returns the bounding box of this feature.
bool testFlag(Flag f) const
Test whether a particular flag is enabled.
virtual void clearActiveLayers() override
clears all PAL layer settings for registered layers
QPointF bottomLeft() const
void setMapToPixel(const QgsMapToPixel &mtp)
static QStringList splitToGraphemes(const QString &text)
Splits a text string to a list of graphemes, which are the smallest allowable character divisions in ...
qreal descent() const
QgsGeometry * intersection(const QgsGeometry *geometry) const
Returns a geometry representing the points shared by this geometry and other.
Class for doing transforms between two map coordinate systems.
void setStrikeOut(bool enable)
static void _writeColor(QgsVectorLayer *layer, const QString &property, const QColor &color, bool withAlpha=true)
LabelPosition is a candidate feature label position.
Definition: labelposition.h:50
bool toBool() const
void translate(const QPointF &offset)
int transform(const QgsCoordinateTransform &ct)
Transform this geometry as described by CoordinateTransform ct.
void setCapitalization(Capitalization caps)
QDomElement writeXml(QDomDocument &doc)
Write settings into a DOM element.
void writeToLayer(QgsVectorLayer *layer)
Label on right of point.
void removeAllDataDefinedProperties()
Clear all data-defined properties.
void setAlphaF(qreal alpha)
bool isValid() const
QString text() const
const QPicture * picture() const
virtual int prepareLayer(QgsVectorLayer *layer, QStringList &attrNames, QgsRenderContext &ctx) override
hook called when drawing layer before issuing select()
qreal height() const
int height() const
double toDouble(bool *ok) const
static QColor decodeColor(const QString &str)
Signifies that the AboveLine and BelowLine flags should respect the map&#39;s orientation rather than the...
double sqrDist(double x, double y) const
Returns the squared distance between this point and x,y.
Definition: qgspoint.cpp:345
iterator insert(const Key &key, const T &value)
QPainter::CompositionMode shadowBlendMode
bool isExpression
Is this label made from a expression string eg FieldName || &#39;mm&#39;.
Convert all characters to lowercase.
virtual Q_DECL_DEPRECATED QList< QgsLabelPosition > labelsAtPosition(const QgsPoint &p) override
return infos about labels at a given (map) position
void setSize(const QgsPoint &point)
Class that stores computed placement from labeling engine.
void setShowingAllLabels(bool showing)
virtual QString type() const =0
Unique type string of the labeling configuration implementation.
bool isEmpty() const
Custom exception class for Coordinate Reference System related exceptions.
QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
void drawPicture(const QPointF &point, const QPicture &picture)
static QVector< QgsPalLayerSettings::PredefinedPointPosition > decodePredefinedPositionOrder(const QString &positionString)
Decodes a string to an ordered list of predefined point label positions.
QgsMapUnitScale shapeOffsetMapUnitScale
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
double area() const
Returns the area of the geometry using GEOS.
QgsVectorDataProvider * dataProvider()
Returns the data provider.
double rasterScaleFactor() const
void setPictureBuffer(const double buffer)
const_iterator constEnd() const
QDomElement createElement(const QString &tagName)
Q_DECL_DEPRECATED const QList< QgsLabelCandidate > & candidates()
bool strikeOut() const
const_iterator constBegin() const
virtual QgsAttrPalIndexNameHash palAttributeIndexNames() const
Return list of indexes to names for QgsPalLabeling fix.
QgsPoint toMapCoordinatesF(double x, double y) const
Transform device coordinates to map (world) coordinates.
void setScale(double scale)
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
static bool checkMinimumSizeMM(const QgsRenderContext &context, const QgsGeometry *geom, double minSize)
Checks whether a geometry exceeds the minimum required size for a geometry to be labeled.
double scaleFactor() const
Represents a vector layer which manages a vector based data sets.
bool begin(QPaintDevice *device)
double minScale
The minimum scale, or 0.0 if unset.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:271
int compare(const QString &other) const
void setDrawLabelRectOnly(bool drawRect)
Sets whether the engine should only draw the outline rectangles of labels, not the label contents the...
QFont font(const QString &family, const QString &style, int pointSize) const
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
void setLetterSpacing(SpacingType type, qreal spacing)
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
void setFlag(Flag f, bool enabled=true)
Set whether a particual flag is enabled.
QString toString() const
Whether to render labels as text or outlines.
static void blurImageInPlace(QImage &image, QRect rect, int radius, bool alphaOnly)
Blurs an image in place, e.g.
int sizeToPixel(double size, const QgsRenderContext &c, SizeUnit unit, bool rasterfactor=false, const QgsMapUnitScale &mapUnitScale=QgsMapUnitScale()) const
Calculates pixel size (considering output size should be in pixel or map units, scale factors and opt...
void setExpressionString(const QString &expr)
Sets the expression for this QgsDataDefined.
bool isActive() const
iterator find(const Key &key)
QgsLabelingResults * takeResults()
Return pointer to recently computed results and pass the ownership of results to the caller...
Maintains current state of more grainular and temporal values when creating/painting component parts ...
RotationType shapeRotationType
bool dataDefinedEvaluate(QgsPalLayerSettings::DataDefinedProperties p, QVariant &exprVal, QgsExpressionContext *context=nullptr, const QVariant &originalValue=QVariant()) const
Get data defined property value from expression string or attribute field name.
virtual QgsVectorLayerLabelProvider * provider(QgsVectorLayer *layer) const =0
Factory for label provider implementation.
int pointSize() const
QgsMapUnitScale labelOffsetMapUnitScale
double x() const
Get the x value of the point.
Definition: qgspoint.h:128
void numCandidatePositions(int &candPoint, int &candLine, int &candPolygon)
Get number of candidate positions that will be generated for each label feature (default to 8) ...
bool hasNext() const
virtual void exit() override
called when we&#39;re done with rendering
qreal height() const
bool diagramsEnabled() const
Returns whether the layer contains diagrams which are enabled and should be drawn.
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:212
qreal wordSpacing() const
void moveTo(qreal x, qreal y)
const T value(const Key &key) const
uint toUInt(bool *ok, int base) const
int remove(const Key &key)
ObstacleType
Valid obstacle types, which affect how features within the layer will act as obstacles for labels...
DirectionSymbols placeDirectionSymbol
QList< QgsLabelPosition > labelsAtPosition(const QgsPoint &p) const
return infos about labels at a given (map) position
static void drawLabelShadow(QgsRenderContext &context, const QgsLabelComponent &component, const QgsPalLayerSettings &tmpLyr)
static void dataDefinedTextFormatting(QgsPalLayerSettings &tmpLyr, const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > &ddValues)
QgsLabelingEngineV2 * mEngine
New labeling engine to interface with PAL.