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