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