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