QGIS API Documentation  2.99.0-Master (c42dad3)
qgspallabeling.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspallabeling.cpp
3  Smart labeling for vector layers
4  -------------------
5  begin : June 2009
6  copyright : (C) Martin Dobias
7  email : wonder dot sk at gmail dot com
8 
9  ***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgspallabeling.h"
19 #include "qgstextlabelfeature.h"
20 #include "qgsunittypes.h"
21 #include "qgscsexception.h"
22 
23 #include <list>
24 
25 #include <pal/pal.h>
26 #include <pal/feature.h>
27 #include <pal/layer.h>
28 #include <pal/palexception.h>
29 #include <pal/problem.h>
30 #include <pal/labelposition.h>
31 
32 #include <cmath>
33 
34 #include <QApplication>
35 #include <QByteArray>
36 #include <QString>
37 #include <QFontMetrics>
38 #include <QTime>
39 #include <QPainter>
40 
41 #include "diagram/qgsdiagram.h"
42 #include "qgsdiagramrenderer.h"
43 #include "qgsfontutils.h"
44 #include "qgslabelsearchtree.h"
45 #include "qgsexpression.h"
46 #include "qgsdatadefined.h"
47 #include "qgslabelingengine.h"
48 #include "qgsvectorlayerlabeling.h"
49 
50 #include <qgslogger.h>
51 #include <qgsvectorlayer.h>
52 #include <qgsmaplayerregistry.h>
53 #include <qgsvectordataprovider.h>
56 #include <qgsgeometry.h>
57 #include <qgsmarkersymbollayer.h>
58 #include <qgspainting.h>
59 #include <qgsproject.h>
60 #include "qgssymbollayerutils.h"
62 #include <QMessageBox>
63 
64 
65 using namespace pal;
66 
67 // -------------
68 
69 /* ND: Default point label position priority. These are set to match variants of the ideal placement priority described
70  in "Making Maps", Krygier & Wood (2011) (p216),
71  "Elements of Cartography", Robinson et al (1995)
72  and "Designing Better Maps", Brewer (2005) (p76)
73  Note that while they agree on positions 1-4, 5-8 are more contentious so I've selected these placements
74  based on my preferences, and to follow Krygier and Wood's placements more closer. (I'm not going to disagree
75  with Denis Wood on anything cartography related...!)
76 */
77 QVector< QgsPalLayerSettings::PredefinedPointPosition > QgsPalLayerSettings::DEFAULT_PLACEMENT_ORDER = QVector< QgsPalLayerSettings::PredefinedPointPosition >()
86 //debugging only - don't use these placements by default
87 /* << QgsPalLayerSettings::TopSlightlyLeft
88 << QgsPalLayerSettings::BottomSlightlyLeft;
89 << QgsPalLayerSettings::TopMiddle
90 << QgsPalLayerSettings::BottomMiddle;*/
91 
93  : upsidedownLabels( Upright )
94  , mCurFeat( nullptr )
95  , xform( nullptr )
96  , extentGeom( nullptr )
97  , mFeaturesToLabel( 0 )
98  , mFeatsSendingToPal( 0 )
99  , mFeatsRegPal( 0 )
100  , expression( nullptr )
101 {
102  enabled = false;
103  drawLabels = true;
104  isExpression = false;
105  fieldIndex = 0;
106 
107  previewBkgrdColor = Qt::white;
108  useSubstitutions = false;
109 
110  // text formatting
111  wrapChar = QLatin1String( "" );
113  addDirectionSymbol = false;
114  leftDirectionSymbol = QStringLiteral( "<" );
115  rightDirectionSymbol = QStringLiteral( ">" );
116  reverseDirectionSymbol = false;
118  formatNumbers = false;
119  decimals = 3;
120  plusSign = false;
121 
122  // placement
125  centroidWhole = false;
126  centroidInside = false;
127  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
128  fitInPolygonOnly = false;
130  xOffset = 0;
131  yOffset = 0;
132  labelOffsetInMapUnits = true;
133  dist = 0;
134  distInMapUnits = false;
136  angleOffset = 0;
137  preserveRotation = true;
138  maxCurvedCharAngleIn = 25.0;
139  maxCurvedCharAngleOut = -25.0;
140  priority = 5;
141  repeatDistance = 0;
143 
144  // rendering
145  scaleVisibility = false;
146  scaleMin = 1;
147  scaleMax = 10000000;
148  fontLimitPixelSize = false;
149  fontMinPixelSize = 0; //trigger to turn it on by default for map unit labels
150  fontMaxPixelSize = 10000;
151  displayAll = false;
153 
154  labelPerPart = false;
155  mergeLines = false;
156  minFeatureSize = 0.0;
157  limitNumLabels = false;
158  maxNumLabels = 2000;
159  obstacle = true;
160  obstacleFactor = 1.0;
162  zIndex = 0.0;
163 
164  // data defined string and old-style index values
165  // NOTE: in QPair use -1 for second value (other values are for old-style layer properties migration)
166 
167  // text style
168  mDataDefinedNames.insert( Size, QPair<QString, int>( QStringLiteral( "Size" ), 0 ) );
169  mDataDefinedNames.insert( Bold, QPair<QString, int>( QStringLiteral( "Bold" ), 1 ) );
170  mDataDefinedNames.insert( Italic, QPair<QString, int>( QStringLiteral( "Italic" ), 2 ) );
171  mDataDefinedNames.insert( Underline, QPair<QString, int>( QStringLiteral( "Underline" ), 3 ) );
172  mDataDefinedNames.insert( Color, QPair<QString, int>( QStringLiteral( "Color" ), 4 ) );
173  mDataDefinedNames.insert( Strikeout, QPair<QString, int>( QStringLiteral( "Strikeout" ), 5 ) );
174  mDataDefinedNames.insert( Family, QPair<QString, int>( QStringLiteral( "Family" ), 6 ) );
175  mDataDefinedNames.insert( FontStyle, QPair<QString, int>( QStringLiteral( "FontStyle" ), -1 ) );
176  mDataDefinedNames.insert( FontSizeUnit, QPair<QString, int>( QStringLiteral( "FontSizeUnit" ), -1 ) );
177  mDataDefinedNames.insert( FontTransp, QPair<QString, int>( QStringLiteral( "FontTransp" ), 18 ) );
178  mDataDefinedNames.insert( FontCase, QPair<QString, int>( QStringLiteral( "FontCase" ), -1 ) );
179  mDataDefinedNames.insert( FontLetterSpacing, QPair<QString, int>( QStringLiteral( "FontLetterSpacing" ), -1 ) );
180  mDataDefinedNames.insert( FontWordSpacing, QPair<QString, int>( QStringLiteral( "FontWordSpacing" ), -1 ) );
181  mDataDefinedNames.insert( FontBlendMode, QPair<QString, int>( QStringLiteral( "FontBlendMode" ), -1 ) );
182 
183  // text formatting
184  mDataDefinedNames.insert( MultiLineWrapChar, QPair<QString, int>( QStringLiteral( "MultiLineWrapChar" ), -1 ) );
185  mDataDefinedNames.insert( MultiLineHeight, QPair<QString, int>( QStringLiteral( "MultiLineHeight" ), -1 ) );
186  mDataDefinedNames.insert( MultiLineAlignment, QPair<QString, int>( QStringLiteral( "MultiLineAlignment" ), -1 ) );
187  mDataDefinedNames.insert( DirSymbDraw, QPair<QString, int>( QStringLiteral( "DirSymbDraw" ), -1 ) );
188  mDataDefinedNames.insert( DirSymbLeft, QPair<QString, int>( QStringLiteral( "DirSymbLeft" ), -1 ) );
189  mDataDefinedNames.insert( DirSymbRight, QPair<QString, int>( QStringLiteral( "DirSymbRight" ), -1 ) );
190  mDataDefinedNames.insert( DirSymbPlacement, QPair<QString, int>( QStringLiteral( "DirSymbPlacement" ), -1 ) );
191  mDataDefinedNames.insert( DirSymbReverse, QPair<QString, int>( QStringLiteral( "DirSymbReverse" ), -1 ) );
192  mDataDefinedNames.insert( NumFormat, QPair<QString, int>( QStringLiteral( "NumFormat" ), -1 ) );
193  mDataDefinedNames.insert( NumDecimals, QPair<QString, int>( QStringLiteral( "NumDecimals" ), -1 ) );
194  mDataDefinedNames.insert( NumPlusSign, QPair<QString, int>( QStringLiteral( "NumPlusSign" ), -1 ) );
195 
196  // text buffer
197  mDataDefinedNames.insert( BufferDraw, QPair<QString, int>( QStringLiteral( "BufferDraw" ), -1 ) );
198  mDataDefinedNames.insert( BufferSize, QPair<QString, int>( QStringLiteral( "BufferSize" ), 7 ) );
199  mDataDefinedNames.insert( BufferUnit, QPair<QString, int>( QStringLiteral( "BufferUnit" ), -1 ) );
200  mDataDefinedNames.insert( BufferColor, QPair<QString, int>( QStringLiteral( "BufferColor" ), 8 ) );
201  mDataDefinedNames.insert( BufferTransp, QPair<QString, int>( QStringLiteral( "BufferTransp" ), 19 ) );
202  mDataDefinedNames.insert( BufferJoinStyle, QPair<QString, int>( QStringLiteral( "BufferJoinStyle" ), -1 ) );
203  mDataDefinedNames.insert( BufferBlendMode, QPair<QString, int>( QStringLiteral( "BufferBlendMode" ), -1 ) );
204 
205  // background
206  mDataDefinedNames.insert( ShapeDraw, QPair<QString, int>( QStringLiteral( "ShapeDraw" ), -1 ) );
207  mDataDefinedNames.insert( ShapeKind, QPair<QString, int>( QStringLiteral( "ShapeKind" ), -1 ) );
208  mDataDefinedNames.insert( ShapeSVGFile, QPair<QString, int>( QStringLiteral( "ShapeSVGFile" ), -1 ) );
209  mDataDefinedNames.insert( ShapeSizeType, QPair<QString, int>( QStringLiteral( "ShapeSizeType" ), -1 ) );
210  mDataDefinedNames.insert( ShapeSizeX, QPair<QString, int>( QStringLiteral( "ShapeSizeX" ), -1 ) );
211  mDataDefinedNames.insert( ShapeSizeY, QPair<QString, int>( QStringLiteral( "ShapeSizeY" ), -1 ) );
212  mDataDefinedNames.insert( ShapeSizeUnits, QPair<QString, int>( QStringLiteral( "ShapeSizeUnits" ), -1 ) );
213  mDataDefinedNames.insert( ShapeRotationType, QPair<QString, int>( QStringLiteral( "ShapeRotationType" ), -1 ) );
214  mDataDefinedNames.insert( ShapeRotation, QPair<QString, int>( QStringLiteral( "ShapeRotation" ), -1 ) );
215  mDataDefinedNames.insert( ShapeOffset, QPair<QString, int>( QStringLiteral( "ShapeOffset" ), -1 ) );
216  mDataDefinedNames.insert( ShapeOffsetUnits, QPair<QString, int>( QStringLiteral( "ShapeOffsetUnits" ), -1 ) );
217  mDataDefinedNames.insert( ShapeRadii, QPair<QString, int>( QStringLiteral( "ShapeRadii" ), -1 ) );
218  mDataDefinedNames.insert( ShapeRadiiUnits, QPair<QString, int>( QStringLiteral( "ShapeRadiiUnits" ), -1 ) );
219  mDataDefinedNames.insert( ShapeTransparency, QPair<QString, int>( QStringLiteral( "ShapeTransparency" ), -1 ) );
220  mDataDefinedNames.insert( ShapeBlendMode, QPair<QString, int>( QStringLiteral( "ShapeBlendMode" ), -1 ) );
221  mDataDefinedNames.insert( ShapeFillColor, QPair<QString, int>( QStringLiteral( "ShapeFillColor" ), -1 ) );
222  mDataDefinedNames.insert( ShapeBorderColor, QPair<QString, int>( QStringLiteral( "ShapeBorderColor" ), -1 ) );
223  mDataDefinedNames.insert( ShapeBorderWidth, QPair<QString, int>( QStringLiteral( "ShapeBorderWidth" ), -1 ) );
224  mDataDefinedNames.insert( ShapeBorderWidthUnits, QPair<QString, int>( QStringLiteral( "ShapeBorderWidthUnits" ), -1 ) );
225  mDataDefinedNames.insert( ShapeJoinStyle, QPair<QString, int>( QStringLiteral( "ShapeJoinStyle" ), -1 ) );
226 
227  // drop shadow
228  mDataDefinedNames.insert( ShadowDraw, QPair<QString, int>( QStringLiteral( "ShadowDraw" ), -1 ) );
229  mDataDefinedNames.insert( ShadowUnder, QPair<QString, int>( QStringLiteral( "ShadowUnder" ), -1 ) );
230  mDataDefinedNames.insert( ShadowOffsetAngle, QPair<QString, int>( QStringLiteral( "ShadowOffsetAngle" ), -1 ) );
231  mDataDefinedNames.insert( ShadowOffsetDist, QPair<QString, int>( QStringLiteral( "ShadowOffsetDist" ), -1 ) );
232  mDataDefinedNames.insert( ShadowOffsetUnits, QPair<QString, int>( QStringLiteral( "ShadowOffsetUnits" ), -1 ) );
233  mDataDefinedNames.insert( ShadowRadius, QPair<QString, int>( QStringLiteral( "ShadowRadius" ), -1 ) );
234  mDataDefinedNames.insert( ShadowRadiusUnits, QPair<QString, int>( QStringLiteral( "ShadowRadiusUnits" ), -1 ) );
235  mDataDefinedNames.insert( ShadowTransparency, QPair<QString, int>( QStringLiteral( "ShadowTransparency" ), -1 ) );
236  mDataDefinedNames.insert( ShadowScale, QPair<QString, int>( QStringLiteral( "ShadowScale" ), -1 ) );
237  mDataDefinedNames.insert( ShadowColor, QPair<QString, int>( QStringLiteral( "ShadowColor" ), -1 ) );
238  mDataDefinedNames.insert( ShadowBlendMode, QPair<QString, int>( QStringLiteral( "ShadowBlendMode" ), -1 ) );
239 
240  // placement
241  mDataDefinedNames.insert( CentroidWhole, QPair<QString, int>( QStringLiteral( "CentroidWhole" ), -1 ) );
242  mDataDefinedNames.insert( OffsetQuad, QPair<QString, int>( QStringLiteral( "OffsetQuad" ), -1 ) );
243  mDataDefinedNames.insert( OffsetXY, QPair<QString, int>( QStringLiteral( "OffsetXY" ), -1 ) );
244  mDataDefinedNames.insert( OffsetUnits, QPair<QString, int>( QStringLiteral( "OffsetUnits" ), -1 ) );
245  mDataDefinedNames.insert( LabelDistance, QPair<QString, int>( QStringLiteral( "LabelDistance" ), 13 ) );
246  mDataDefinedNames.insert( DistanceUnits, QPair<QString, int>( QStringLiteral( "DistanceUnits" ), -1 ) );
247  mDataDefinedNames.insert( OffsetRotation, QPair<QString, int>( QStringLiteral( "OffsetRotation" ), -1 ) );
248  mDataDefinedNames.insert( CurvedCharAngleInOut, QPair<QString, int>( QStringLiteral( "CurvedCharAngleInOut" ), -1 ) );
249  mDataDefinedNames.insert( RepeatDistance, QPair<QString, int>( QStringLiteral( "RepeatDistance" ), -1 ) );
250  mDataDefinedNames.insert( RepeatDistanceUnit, QPair<QString, int>( QStringLiteral( "RepeatDistanceUnit" ), -1 ) );
251  mDataDefinedNames.insert( Priority, QPair<QString, int>( QStringLiteral( "Priority" ), -1 ) );
252  mDataDefinedNames.insert( IsObstacle, QPair<QString, int>( QStringLiteral( "IsObstacle" ), -1 ) );
253  mDataDefinedNames.insert( ObstacleFactor, QPair<QString, int>( QStringLiteral( "ObstacleFactor" ), -1 ) );
254  mDataDefinedNames.insert( PredefinedPositionOrder, QPair<QString, int>( QStringLiteral( "PredefinedPositionOrder" ), -1 ) );
255 
256  // (data defined only)
257  mDataDefinedNames.insert( PositionX, QPair<QString, int>( QStringLiteral( "PositionX" ), 9 ) );
258  mDataDefinedNames.insert( PositionY, QPair<QString, int>( QStringLiteral( "PositionY" ), 10 ) );
259  mDataDefinedNames.insert( Hali, QPair<QString, int>( QStringLiteral( "Hali" ), 11 ) );
260  mDataDefinedNames.insert( Vali, QPair<QString, int>( QStringLiteral( "Vali" ), 12 ) );
261  mDataDefinedNames.insert( Rotation, QPair<QString, int>( QStringLiteral( "Rotation" ), 14 ) );
262 
263  //rendering
264  mDataDefinedNames.insert( ScaleVisibility, QPair<QString, int>( QStringLiteral( "ScaleVisibility" ), -1 ) );
265  mDataDefinedNames.insert( MinScale, QPair<QString, int>( QStringLiteral( "MinScale" ), 16 ) );
266  mDataDefinedNames.insert( MaxScale, QPair<QString, int>( QStringLiteral( "MaxScale" ), 17 ) );
267  mDataDefinedNames.insert( FontLimitPixel, QPair<QString, int>( QStringLiteral( "FontLimitPixel" ), -1 ) );
268  mDataDefinedNames.insert( FontMinPixel, QPair<QString, int>( QStringLiteral( "FontMinPixel" ), -1 ) );
269  mDataDefinedNames.insert( FontMaxPixel, QPair<QString, int>( QStringLiteral( "FontMaxPixel" ), -1 ) );
270  mDataDefinedNames.insert( ZIndex, QPair<QString, int>( QStringLiteral( "ZIndex" ), -1 ) );
271  // (data defined only)
272  mDataDefinedNames.insert( Show, QPair<QString, int>( QStringLiteral( "Show" ), 15 ) );
273  mDataDefinedNames.insert( AlwaysShow, QPair<QString, int>( QStringLiteral( "AlwaysShow" ), 20 ) );
274 
275 }
276 
278  : mCurFeat( nullptr )
279  , fieldIndex( 0 )
280  , xform( nullptr )
281  , extentGeom( nullptr )
282  , mFeaturesToLabel( 0 )
283  , mFeatsSendingToPal( 0 )
284  , mFeatsRegPal( 0 )
285  , expression( nullptr )
286 {
287  *this = s;
288 }
289 
291 {
292  if ( this == &s )
293  return *this;
294 
295  // copy only permanent stuff
296 
297  enabled = s.enabled;
299 
300  // text style
301  fieldName = s.fieldName;
306 
307  // text formatting
308  wrapChar = s.wrapChar;
316  decimals = s.decimals;
317  plusSign = s.plusSign;
318 
319  // placement
320  placement = s.placement;
327  xOffset = s.xOffset;
328  yOffset = s.yOffset;
331  dist = s.dist;
339  priority = s.priority;
343 
344  // rendering
346  scaleMin = s.scaleMin;
347  scaleMax = s.scaleMax;
353 
359  obstacle = s.obstacle;
362  zIndex = s.zIndex;
363 
364  mFormat = s.mFormat;
365 
366  // data defined
368  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator it = s.dataDefinedProperties.constBegin();
369  for ( ; it != s.dataDefinedProperties.constEnd(); ++it )
370  {
371  dataDefinedProperties.insert( it.key(), it.value() ? new QgsDataDefined( *it.value() ) : nullptr );
372  }
373  mDataDefinedNames = s.mDataDefinedNames;
374 
375  return *this;
376 }
377 
378 
380 {
381  // pal layer is deleted internally in PAL
382 
383  delete expression;
384 
385  // delete all QgsDataDefined objects (which also deletes their QgsExpression object)
387 }
388 
389 
391 {
392  QgsPalLayerSettings settings;
393  settings.readFromLayer( layer );
394  return settings;
395 }
396 
397 
399 {
400  if ( !expression )
401  {
402  expression = new QgsExpression( fieldName );
403  }
404  return expression;
405 }
406 
407 static QgsPalLayerSettings::SizeUnit _decodeUnits( const QString& str )
408 {
409  if ( str.compare( QLatin1String( "Point" ), Qt::CaseInsensitive ) == 0
410  || str.compare( QLatin1String( "Points" ), Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Points;
411  if ( str.compare( QLatin1String( "MapUnit" ), Qt::CaseInsensitive ) == 0
412  || str.compare( QLatin1String( "MapUnits" ), Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::MapUnits;
413  if ( str.compare( QLatin1String( "Percent" ), Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Percent;
414  return QgsPalLayerSettings::MM; // "MM"
415 }
416 
417 static Qt::PenJoinStyle _decodePenJoinStyle( const QString& str )
418 {
419  if ( str.compare( QLatin1String( "Miter" ), Qt::CaseInsensitive ) == 0 ) return Qt::MiterJoin;
420  if ( str.compare( QLatin1String( "Round" ), Qt::CaseInsensitive ) == 0 ) return Qt::RoundJoin;
421  return Qt::BevelJoin; // "Bevel"
422 }
423 
424 void QgsPalLayerSettings::readDataDefinedPropertyMap( QgsVectorLayer* layer, QDomElement* parentElem,
425  QMap < QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* > & propertyMap )
426 {
427  if ( !layer && !parentElem )
428  {
429  return;
430  }
431 
432  QMapIterator<QgsPalLayerSettings::DataDefinedProperties, QPair<QString, int> > i( mDataDefinedNames );
433  while ( i.hasNext() )
434  {
435  i.next();
436  if ( layer )
437  {
438  // reading from layer's custom properties (old way)
439  readDataDefinedProperty( layer, i.key(), propertyMap );
440  }
441  else if ( parentElem )
442  {
443  // reading from XML (new way)
444  QDomElement e = parentElem->firstChildElement( i.value().first );
445  if ( !e.isNull() )
446  {
447  QgsDataDefined* dd = new QgsDataDefined();
448  if ( dd->setFromXmlElement( e ) )
449  propertyMap.insert( i.key(), dd );
450  else
451  delete dd;
452  }
453  }
454  }
455 }
456 
457 void QgsPalLayerSettings::writeDataDefinedPropertyMap( QgsVectorLayer* layer, QDomElement* parentElem,
458  const QMap < QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* > & propertyMap )
459 {
460  if ( !layer && !parentElem )
461  {
462  return;
463  }
464 
465  QMapIterator<QgsPalLayerSettings::DataDefinedProperties, QPair<QString, int> > i( mDataDefinedNames );
466  while ( i.hasNext() )
467  {
468  i.next();
469  QString newPropertyName = "labeling/dataDefined/" + i.value().first;
470  QVariant propertyValue = QVariant();
471 
472  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator it = propertyMap.find( i.key() );
473  if ( it != propertyMap.constEnd() )
474  {
475  QgsDataDefined* dd = it.value();
476  if ( dd )
477  {
478  bool active = dd->isActive();
479  bool useExpr = dd->useExpression();
480  QString expr = dd->expressionString();
481  QString field = dd->field();
482 
483  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
484 
485  if ( !defaultVals )
486  {
487  // TODO: update this when project settings for labeling are migrated to better XML layout
488  QStringList values;
489  values << ( active ? "1" : "0" );
490  values << ( useExpr ? "1" : "0" );
491  values << expr;
492  values << field;
493  if ( !values.isEmpty() )
494  {
495  propertyValue = QVariant( values.join( QStringLiteral( "~~" ) ) );
496  }
497  }
498 
499  if ( parentElem )
500  {
501  // writing to XML document (instead of writing to layer)
502  QDomDocument doc = parentElem->ownerDocument();
503  QDomElement e = dd->toXmlElement( doc, i.value().first );
504  parentElem->appendChild( e );
505  }
506  }
507  }
508 
509  if ( layer )
510  {
511  // writing to layer's custom properties (old method)
512 
513  if ( propertyValue.isValid() )
514  {
515  layer->setCustomProperty( newPropertyName, propertyValue );
516  }
517  else
518  {
519  // remove unused properties
520  layer->removeCustomProperty( newPropertyName );
521  }
522 
523  if ( layer->customProperty( newPropertyName, QVariant() ).isValid() && i.value().second > -1 )
524  {
525  // remove old-style field index-based property, if still present
526  layer->removeCustomProperty( QStringLiteral( "labeling/dataDefinedProperty" ) + QString::number( i.value().second ) );
527  }
528  }
529  }
530 }
531 
532 void QgsPalLayerSettings::readDataDefinedProperty( QgsVectorLayer* layer,
534  QMap < QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* > & propertyMap )
535 {
536  QString newPropertyName = "labeling/dataDefined/" + mDataDefinedNames.value( p ).first;
537  QVariant newPropertyField = layer->customProperty( newPropertyName, QVariant() );
538 
539  QString ddString = QString();
540  if ( newPropertyField.isValid() )
541  {
542  ddString = newPropertyField.toString();
543  }
544  else // maybe working with old-style field index-based property (< QGIS 2.0)
545  {
546  int oldIndx = mDataDefinedNames.value( p ).second;
547 
548  if ( oldIndx < 0 ) // something went wrong and we are working with new-style
549  {
550  return;
551  }
552 
553  QString oldPropertyName = "labeling/dataDefinedProperty" + QString::number( oldIndx );
554  QVariant oldPropertyField = layer->customProperty( oldPropertyName, QVariant() );
555 
556  if ( !oldPropertyField.isValid() )
557  {
558  return;
559  }
560 
561  // switch from old-style field index- to name-based properties
562  bool conversionOk;
563  int indx = oldPropertyField.toInt( &conversionOk );
564 
565  if ( conversionOk )
566  {
567  // Fix to migrate from old-style vector api, where returned QMap keys possibly
568  // had 'holes' in sequence of field indices, e.g. 0,2,3
569  // QgsAttrPalIndexNameHash provides a means of access field name in sequences from
570  // providers that procuded holes (e.g. PostGIS skipped geom column), otherwise it is empty
571  QgsAttrPalIndexNameHash oldIndicesToNames = layer->dataProvider()->palAttributeIndexNames();
572 
573  if ( !oldIndicesToNames.isEmpty() )
574  {
575  ddString = oldIndicesToNames.value( indx );
576  }
577  else
578  {
579  QgsFields fields = layer->dataProvider()->fields();
580  if ( indx < fields.size() ) // in case field count has changed
581  {
582  ddString = fields.at( indx ).name();
583  }
584  }
585  }
586 
587  if ( !ddString.isEmpty() )
588  {
589  //upgrade any existing property to field name-based
590  layer->setCustomProperty( newPropertyName, QVariant( updateDataDefinedString( ddString ) ) );
591 
592  // fix for scale visibility limits triggered off of just its data defined values in the past (<2.0)
593  if ( oldIndx == 16 || oldIndx == 17 ) // old minScale and maxScale enums
594  {
595  scaleVisibility = true;
596  layer->setCustomProperty( QStringLiteral( "labeling/scaleVisibility" ), true );
597  }
598  }
599 
600  // remove old-style field index-based property
601  layer->removeCustomProperty( oldPropertyName );
602  }
603 
604  if ( !ddString.isEmpty() && ddString != QLatin1String( "0~~0~~~~" ) )
605  {
606  // TODO: update this when project settings for labeling are migrated to better XML layout
607  QString newStyleString = updateDataDefinedString( ddString );
608  QStringList ddv = newStyleString.split( QStringLiteral( "~~" ) );
609 
610  QgsDataDefined* dd = new QgsDataDefined( ddv.at( 0 ).toInt(), ddv.at( 1 ).toInt(), ddv.at( 2 ), ddv.at( 3 ) );
611  propertyMap.insert( p, dd );
612  }
613  else
614  {
615  // remove unused properties
616  layer->removeCustomProperty( newPropertyName );
617  }
618 }
619 
621 {
622  if ( layer->customProperty( QStringLiteral( "labeling" ) ).toString() != QLatin1String( "pal" ) )
623  {
624  if ( layer->geometryType() == QgsWkbTypes::PointGeometry )
626 
627  // for polygons the "over point" (over centroid) placement is better than the default
628  // "around point" (around centroid) which is more suitable for points
629  if ( layer->geometryType() == QgsWkbTypes::PolygonGeometry )
631 
632  return; // there's no information available
633  }
634 
635  // NOTE: set defaults for newly added properties, for backwards compatibility
636 
637  enabled = layer->labelsEnabled();
638  drawLabels = layer->customProperty( QStringLiteral( "labeling/drawLabels" ), true ).toBool();
639 
640  mFormat.readFromLayer( layer );
641 
642  // text style
643  fieldName = layer->customProperty( QStringLiteral( "labeling/fieldName" ) ).toString();
644  isExpression = layer->customProperty( QStringLiteral( "labeling/isExpression" ) ).toBool();
645  previewBkgrdColor = QColor( layer->customProperty( QStringLiteral( "labeling/previewBkgrdColor" ), QVariant( "#ffffff" ) ).toString() );
646  QDomDocument doc( QStringLiteral( "substitutions" ) );
647  doc.setContent( layer->customProperty( QStringLiteral( "labeling/substitutions" ) ).toString() );
648  QDomElement replacementElem = doc.firstChildElement( QStringLiteral( "substitutions" ) );
649  substitutions.readXml( replacementElem );
650  useSubstitutions = layer->customProperty( QStringLiteral( "labeling/useSubstitutions" ) ).toBool();
651 
652  // text formatting
653  wrapChar = layer->customProperty( QStringLiteral( "labeling/wrapChar" ) ).toString();
654  multilineAlign = static_cast< MultiLineAlign >( layer->customProperty( QStringLiteral( "labeling/multilineAlign" ), QVariant( MultiFollowPlacement ) ).toUInt() );
655  addDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/addDirectionSymbol" ) ).toBool();
656  leftDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/leftDirectionSymbol" ), QVariant( "<" ) ).toString();
657  rightDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/rightDirectionSymbol" ), QVariant( ">" ) ).toString();
658  reverseDirectionSymbol = layer->customProperty( QStringLiteral( "labeling/reverseDirectionSymbol" ) ).toBool();
659  placeDirectionSymbol = static_cast< DirectionSymbols >( layer->customProperty( QStringLiteral( "labeling/placeDirectionSymbol" ), QVariant( SymbolLeftRight ) ).toUInt() );
660  formatNumbers = layer->customProperty( QStringLiteral( "labeling/formatNumbers" ) ).toBool();
661  decimals = layer->customProperty( QStringLiteral( "labeling/decimals" ) ).toInt();
662  plusSign = layer->customProperty( QStringLiteral( "labeling/plussign" ) ).toBool();
663 
664  // placement
665  placement = static_cast< Placement >( layer->customProperty( QStringLiteral( "labeling/placement" ) ).toInt() );
666  placementFlags = layer->customProperty( QStringLiteral( "labeling/placementFlags" ) ).toUInt();
667  centroidWhole = layer->customProperty( QStringLiteral( "labeling/centroidWhole" ), QVariant( false ) ).toBool();
668  centroidInside = layer->customProperty( QStringLiteral( "labeling/centroidInside" ), QVariant( false ) ).toBool();
669  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( layer->customProperty( QStringLiteral( "labeling/predefinedPositionOrder" ) ).toString() );
670  if ( predefinedPositionOrder.isEmpty() )
671  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
672  fitInPolygonOnly = layer->customProperty( QStringLiteral( "labeling/fitInPolygonOnly" ), QVariant( false ) ).toBool();
673  dist = layer->customProperty( QStringLiteral( "labeling/dist" ) ).toDouble();
674  distInMapUnits = layer->customProperty( QStringLiteral( "labeling/distInMapUnits" ) ).toBool();
675  if ( layer->customProperty( QStringLiteral( "labeling/distMapUnitScale" ) ).toString().isEmpty() )
676  {
677  //fallback to older property
678  distMapUnitScale.minScale = layer->customProperty( QStringLiteral( "labeling/distMapUnitMinScale" ), 0.0 ).toDouble();
679  distMapUnitScale.maxScale = layer->customProperty( QStringLiteral( "labeling/distMapUnitMaxScale" ), 0.0 ).toDouble();
680  }
681  else
682  {
683  distMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/distMapUnitScale" ) ).toString() );
684  }
685  offsetType = static_cast< OffsetType >( layer->customProperty( QStringLiteral( "labeling/offsetType" ), QVariant( FromPoint ) ).toUInt() );
686  quadOffset = static_cast< QuadrantPosition >( layer->customProperty( QStringLiteral( "labeling/quadOffset" ), QVariant( QuadrantOver ) ).toUInt() );
687  xOffset = layer->customProperty( QStringLiteral( "labeling/xOffset" ), QVariant( 0.0 ) ).toDouble();
688  yOffset = layer->customProperty( QStringLiteral( "labeling/yOffset" ), QVariant( 0.0 ) ).toDouble();
689  labelOffsetInMapUnits = layer->customProperty( QStringLiteral( "labeling/labelOffsetInMapUnits" ), QVariant( true ) ).toBool();
690  if ( layer->customProperty( QStringLiteral( "labeling/labelOffsetMapUnitScale" ) ).toString().isEmpty() )
691  {
692  //fallback to older property
693  labelOffsetMapUnitScale.minScale = layer->customProperty( QStringLiteral( "labeling/labelOffsetMapUnitMinScale" ), 0.0 ).toDouble();
694  labelOffsetMapUnitScale.maxScale = layer->customProperty( QStringLiteral( "labeling/labelOffsetMapUnitMaxScale" ), 0.0 ).toDouble();
695  }
696  else
697  {
698  labelOffsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/labelOffsetMapUnitScale" ) ).toString() );
699  }
700  angleOffset = layer->customProperty( QStringLiteral( "labeling/angleOffset" ), QVariant( 0.0 ) ).toDouble();
701  preserveRotation = layer->customProperty( QStringLiteral( "labeling/preserveRotation" ), QVariant( true ) ).toBool();
702  maxCurvedCharAngleIn = layer->customProperty( QStringLiteral( "labeling/maxCurvedCharAngleIn" ), QVariant( 25.0 ) ).toDouble();
703  maxCurvedCharAngleOut = layer->customProperty( QStringLiteral( "labeling/maxCurvedCharAngleOut" ), QVariant( -25.0 ) ).toDouble();
704  priority = layer->customProperty( QStringLiteral( "labeling/priority" ) ).toInt();
705  repeatDistance = layer->customProperty( QStringLiteral( "labeling/repeatDistance" ), 0.0 ).toDouble();
706  repeatDistanceUnit = static_cast< SizeUnit >( layer->customProperty( QStringLiteral( "labeling/repeatDistanceUnit" ), QVariant( MM ) ).toUInt() );
707  if ( layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitScale" ) ).toString().isEmpty() )
708  {
709  //fallback to older property
710  repeatDistanceMapUnitScale.minScale = layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitMinScale" ), 0.0 ).toDouble();
711  repeatDistanceMapUnitScale.maxScale = layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitMaxScale" ), 0.0 ).toDouble();
712  }
713  else
714  {
715  repeatDistanceMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitScale" ) ).toString() );
716  }
717 
718  // rendering
719  int scalemn = layer->customProperty( QStringLiteral( "labeling/scaleMin" ), QVariant( 0 ) ).toInt();
720  int scalemx = layer->customProperty( QStringLiteral( "labeling/scaleMax" ), QVariant( 0 ) ).toInt();
721 
722  // fix for scale visibility limits being keyed off of just its values in the past (<2.0)
723  QVariant scalevis = layer->customProperty( QStringLiteral( "labeling/scaleVisibility" ), QVariant() );
724  if ( scalevis.isValid() )
725  {
726  scaleVisibility = scalevis.toBool();
727  scaleMin = scalemn;
728  scaleMax = scalemx;
729  }
730  else if ( scalemn > 0 || scalemx > 0 )
731  {
732  scaleVisibility = true;
733  scaleMin = scalemn;
734  scaleMax = scalemx;
735  }
736  else
737  {
738  // keep scaleMin and scaleMax at new 1.0 defaults (1 and 10000000, were 0 and 0)
739  scaleVisibility = false;
740  }
741 
742 
743  fontLimitPixelSize = layer->customProperty( QStringLiteral( "labeling/fontLimitPixelSize" ), QVariant( false ) ).toBool();
744  fontMinPixelSize = layer->customProperty( QStringLiteral( "labeling/fontMinPixelSize" ), QVariant( 0 ) ).toInt();
745  fontMaxPixelSize = layer->customProperty( QStringLiteral( "labeling/fontMaxPixelSize" ), QVariant( 10000 ) ).toInt();
746  displayAll = layer->customProperty( QStringLiteral( "labeling/displayAll" ), QVariant( false ) ).toBool();
747  upsidedownLabels = static_cast< UpsideDownLabels >( layer->customProperty( QStringLiteral( "labeling/upsidedownLabels" ), QVariant( Upright ) ).toUInt() );
748 
749  labelPerPart = layer->customProperty( QStringLiteral( "labeling/labelPerPart" ) ).toBool();
750  mergeLines = layer->customProperty( QStringLiteral( "labeling/mergeLines" ) ).toBool();
751  minFeatureSize = layer->customProperty( QStringLiteral( "labeling/minFeatureSize" ) ).toDouble();
752  limitNumLabels = layer->customProperty( QStringLiteral( "labeling/limitNumLabels" ), QVariant( false ) ).toBool();
753  maxNumLabels = layer->customProperty( QStringLiteral( "labeling/maxNumLabels" ), QVariant( 2000 ) ).toInt();
754  obstacle = layer->customProperty( QStringLiteral( "labeling/obstacle" ), QVariant( true ) ).toBool();
755  obstacleFactor = layer->customProperty( QStringLiteral( "labeling/obstacleFactor" ), QVariant( 1.0 ) ).toDouble();
756  obstacleType = static_cast< ObstacleType >( layer->customProperty( QStringLiteral( "labeling/obstacleType" ), QVariant( PolygonInterior ) ).toUInt() );
757  zIndex = layer->customProperty( QStringLiteral( "labeling/zIndex" ), QVariant( 0.0 ) ).toDouble();
758 
759  readDataDefinedPropertyMap( layer, nullptr, dataDefinedProperties );
760 }
761 
763 {
764  // this is a mark that labeling information is present
765  layer->setCustomProperty( QStringLiteral( "labeling" ), "pal" );
766 
767  layer->setCustomProperty( QStringLiteral( "labeling/enabled" ), enabled );
768  layer->setCustomProperty( QStringLiteral( "labeling/drawLabels" ), drawLabels );
769 
770  mFormat.writeToLayer( layer );
771 
772  // text style
773  layer->setCustomProperty( QStringLiteral( "labeling/fieldName" ), fieldName );
774  layer->setCustomProperty( QStringLiteral( "labeling/isExpression" ), isExpression );
775  layer->setCustomProperty( QStringLiteral( "labeling/previewBkgrdColor" ), previewBkgrdColor.name() );
776  QDomDocument doc( QStringLiteral( "substitutions" ) );
777  QDomElement replacementElem = doc.createElement( QStringLiteral( "substitutions" ) );
778  substitutions.writeXml( replacementElem, doc );
779  QString replacementProps;
780  QTextStream stream( &replacementProps );
781  replacementElem.save( stream, -1 );
782  layer->setCustomProperty( QStringLiteral( "labeling/substitutions" ), replacementProps );
783  layer->setCustomProperty( QStringLiteral( "labeling/useSubstitutions" ), useSubstitutions );
784 
785  // text formatting
786  layer->setCustomProperty( QStringLiteral( "labeling/wrapChar" ), wrapChar );
787  layer->setCustomProperty( QStringLiteral( "labeling/multilineAlign" ), static_cast< unsigned int >( multilineAlign ) );
788  layer->setCustomProperty( QStringLiteral( "labeling/addDirectionSymbol" ), addDirectionSymbol );
789  layer->setCustomProperty( QStringLiteral( "labeling/leftDirectionSymbol" ), leftDirectionSymbol );
790  layer->setCustomProperty( QStringLiteral( "labeling/rightDirectionSymbol" ), rightDirectionSymbol );
791  layer->setCustomProperty( QStringLiteral( "labeling/reverseDirectionSymbol" ), reverseDirectionSymbol );
792  layer->setCustomProperty( QStringLiteral( "labeling/placeDirectionSymbol" ), static_cast< unsigned int >( placeDirectionSymbol ) );
793  layer->setCustomProperty( QStringLiteral( "labeling/formatNumbers" ), formatNumbers );
794  layer->setCustomProperty( QStringLiteral( "labeling/decimals" ), decimals );
795  layer->setCustomProperty( QStringLiteral( "labeling/plussign" ), plusSign );
796 
797  // placement
798  layer->setCustomProperty( QStringLiteral( "labeling/placement" ), placement );
799  layer->setCustomProperty( QStringLiteral( "labeling/placementFlags" ), static_cast< unsigned int >( placementFlags ) );
800  layer->setCustomProperty( QStringLiteral( "labeling/centroidWhole" ), centroidWhole );
801  layer->setCustomProperty( QStringLiteral( "labeling/centroidInside" ), centroidInside );
802  layer->setCustomProperty( QStringLiteral( "labeling/predefinedPositionOrder" ), QgsLabelingUtils::encodePredefinedPositionOrder( predefinedPositionOrder ) );
803  layer->setCustomProperty( QStringLiteral( "labeling/fitInPolygonOnly" ), fitInPolygonOnly );
804  layer->setCustomProperty( QStringLiteral( "labeling/dist" ), dist );
805  layer->setCustomProperty( QStringLiteral( "labeling/distInMapUnits" ), distInMapUnits );
806  layer->setCustomProperty( QStringLiteral( "labeling/distMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( distMapUnitScale ) );
807  layer->setCustomProperty( QStringLiteral( "labeling/offsetType" ), static_cast< unsigned int >( offsetType ) );
808  layer->setCustomProperty( QStringLiteral( "labeling/quadOffset" ), static_cast< unsigned int >( quadOffset ) );
809  layer->setCustomProperty( QStringLiteral( "labeling/xOffset" ), xOffset );
810  layer->setCustomProperty( QStringLiteral( "labeling/yOffset" ), yOffset );
811  layer->setCustomProperty( QStringLiteral( "labeling/labelOffsetInMapUnits" ), labelOffsetInMapUnits );
812  layer->setCustomProperty( QStringLiteral( "labeling/labelOffsetMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( labelOffsetMapUnitScale ) );
813  layer->setCustomProperty( QStringLiteral( "labeling/angleOffset" ), angleOffset );
814  layer->setCustomProperty( QStringLiteral( "labeling/preserveRotation" ), preserveRotation );
815  layer->setCustomProperty( QStringLiteral( "labeling/maxCurvedCharAngleIn" ), maxCurvedCharAngleIn );
816  layer->setCustomProperty( QStringLiteral( "labeling/maxCurvedCharAngleOut" ), maxCurvedCharAngleOut );
817  layer->setCustomProperty( QStringLiteral( "labeling/priority" ), priority );
818  layer->setCustomProperty( QStringLiteral( "labeling/repeatDistance" ), repeatDistance );
819  layer->setCustomProperty( QStringLiteral( "labeling/repeatDistanceUnit" ), repeatDistanceUnit );
820  layer->setCustomProperty( QStringLiteral( "labeling/repeatDistanceMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( repeatDistanceMapUnitScale ) );
821 
822  // rendering
823  layer->setCustomProperty( QStringLiteral( "labeling/scaleVisibility" ), scaleVisibility );
824  layer->setCustomProperty( QStringLiteral( "labeling/scaleMin" ), scaleMin );
825  layer->setCustomProperty( QStringLiteral( "labeling/scaleMax" ), scaleMax );
826  layer->setCustomProperty( QStringLiteral( "labeling/fontLimitPixelSize" ), fontLimitPixelSize );
827  layer->setCustomProperty( QStringLiteral( "labeling/fontMinPixelSize" ), fontMinPixelSize );
828  layer->setCustomProperty( QStringLiteral( "labeling/fontMaxPixelSize" ), fontMaxPixelSize );
829  layer->setCustomProperty( QStringLiteral( "labeling/displayAll" ), displayAll );
830  layer->setCustomProperty( QStringLiteral( "labeling/upsidedownLabels" ), static_cast< unsigned int >( upsidedownLabels ) );
831 
832  layer->setCustomProperty( QStringLiteral( "labeling/labelPerPart" ), labelPerPart );
833  layer->setCustomProperty( QStringLiteral( "labeling/mergeLines" ), mergeLines );
834  layer->setCustomProperty( QStringLiteral( "labeling/minFeatureSize" ), minFeatureSize );
835  layer->setCustomProperty( QStringLiteral( "labeling/limitNumLabels" ), limitNumLabels );
836  layer->setCustomProperty( QStringLiteral( "labeling/maxNumLabels" ), maxNumLabels );
837  layer->setCustomProperty( QStringLiteral( "labeling/obstacle" ), obstacle );
838  layer->setCustomProperty( QStringLiteral( "labeling/obstacleFactor" ), obstacleFactor );
839  layer->setCustomProperty( QStringLiteral( "labeling/obstacleType" ), static_cast< unsigned int >( obstacleType ) );
840  layer->setCustomProperty( QStringLiteral( "labeling/zIndex" ), zIndex );
841 
842  writeDataDefinedPropertyMap( layer, nullptr, dataDefinedProperties );
843  layer->emitStyleChanged();
844 }
845 
846 void QgsPalLayerSettings::readXml( QDomElement& elem )
847 {
848  enabled = true;
849  drawLabels = true;
850 
851  // text style
852  QDomElement textStyleElem = elem.firstChildElement( QStringLiteral( "text-style" ) );
853  fieldName = textStyleElem.attribute( QStringLiteral( "fieldName" ) );
854  isExpression = textStyleElem.attribute( QStringLiteral( "isExpression" ) ).toInt();
855 
856  mFormat.readXml( elem );
857  previewBkgrdColor = QColor( textStyleElem.attribute( QStringLiteral( "previewBkgrdColor" ), QStringLiteral( "#ffffff" ) ) );
858  substitutions.readXml( textStyleElem.firstChildElement( QStringLiteral( "substitutions" ) ) );
859  useSubstitutions = textStyleElem.attribute( QStringLiteral( "useSubstitutions" ) ).toInt();
860 
861  // text formatting
862  QDomElement textFormatElem = elem.firstChildElement( QStringLiteral( "text-format" ) );
863  wrapChar = textFormatElem.attribute( QStringLiteral( "wrapChar" ) );
864  multilineAlign = static_cast< MultiLineAlign >( textFormatElem.attribute( QStringLiteral( "multilineAlign" ), QString::number( MultiFollowPlacement ) ).toUInt() );
865  addDirectionSymbol = textFormatElem.attribute( QStringLiteral( "addDirectionSymbol" ) ).toInt();
866  leftDirectionSymbol = textFormatElem.attribute( QStringLiteral( "leftDirectionSymbol" ), QStringLiteral( "<" ) );
867  rightDirectionSymbol = textFormatElem.attribute( QStringLiteral( "rightDirectionSymbol" ), QStringLiteral( ">" ) );
868  reverseDirectionSymbol = textFormatElem.attribute( QStringLiteral( "reverseDirectionSymbol" ) ).toInt();
869  placeDirectionSymbol = static_cast< DirectionSymbols >( textFormatElem.attribute( QStringLiteral( "placeDirectionSymbol" ), QString::number( SymbolLeftRight ) ).toUInt() );
870  formatNumbers = textFormatElem.attribute( QStringLiteral( "formatNumbers" ) ).toInt();
871  decimals = textFormatElem.attribute( QStringLiteral( "decimals" ) ).toInt();
872  plusSign = textFormatElem.attribute( QStringLiteral( "plussign" ) ).toInt();
873 
874  // placement
875  QDomElement placementElem = elem.firstChildElement( QStringLiteral( "placement" ) );
876  placement = static_cast< Placement >( placementElem.attribute( QStringLiteral( "placement" ) ).toInt() );
877  placementFlags = placementElem.attribute( QStringLiteral( "placementFlags" ) ).toUInt();
878  centroidWhole = placementElem.attribute( QStringLiteral( "centroidWhole" ), QStringLiteral( "0" ) ).toInt();
879  centroidInside = placementElem.attribute( QStringLiteral( "centroidInside" ), QStringLiteral( "0" ) ).toInt();
880  predefinedPositionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( placementElem.attribute( QStringLiteral( "predefinedPositionOrder" ) ) );
881  if ( predefinedPositionOrder.isEmpty() )
882  predefinedPositionOrder = DEFAULT_PLACEMENT_ORDER;
883  fitInPolygonOnly = placementElem.attribute( QStringLiteral( "fitInPolygonOnly" ), QStringLiteral( "0" ) ).toInt();
884  dist = placementElem.attribute( QStringLiteral( "dist" ) ).toDouble();
885  distInMapUnits = placementElem.attribute( QStringLiteral( "distInMapUnits" ) ).toInt();
886  if ( !placementElem.hasAttribute( QStringLiteral( "distMapUnitScale" ) ) )
887  {
888  //fallback to older property
889  distMapUnitScale.minScale = placementElem.attribute( QStringLiteral( "distMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
890  distMapUnitScale.maxScale = placementElem.attribute( QStringLiteral( "distMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
891  }
892  else
893  {
894  distMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( placementElem.attribute( QStringLiteral( "distMapUnitScale" ) ) );
895  }
896  offsetType = static_cast< OffsetType >( placementElem.attribute( QStringLiteral( "offsetType" ), QString::number( FromPoint ) ).toUInt() );
897  quadOffset = static_cast< QuadrantPosition >( placementElem.attribute( QStringLiteral( "quadOffset" ), QString::number( QuadrantOver ) ).toUInt() );
898  xOffset = placementElem.attribute( QStringLiteral( "xOffset" ), QStringLiteral( "0" ) ).toDouble();
899  yOffset = placementElem.attribute( QStringLiteral( "yOffset" ), QStringLiteral( "0" ) ).toDouble();
900  labelOffsetInMapUnits = placementElem.attribute( QStringLiteral( "labelOffsetInMapUnits" ), QStringLiteral( "1" ) ).toInt();
901  if ( !placementElem.hasAttribute( QStringLiteral( "labelOffsetMapUnitScale" ) ) )
902  {
903  //fallback to older property
904  labelOffsetMapUnitScale.minScale = placementElem.attribute( QStringLiteral( "labelOffsetMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
905  labelOffsetMapUnitScale.maxScale = placementElem.attribute( QStringLiteral( "labelOffsetMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
906  }
907  else
908  {
909  labelOffsetMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( placementElem.attribute( QStringLiteral( "labelOffsetMapUnitScale" ) ) );
910  }
911  angleOffset = placementElem.attribute( QStringLiteral( "angleOffset" ), QStringLiteral( "0" ) ).toDouble();
912  preserveRotation = placementElem.attribute( QStringLiteral( "preserveRotation" ), QStringLiteral( "1" ) ).toInt();
913  maxCurvedCharAngleIn = placementElem.attribute( QStringLiteral( "maxCurvedCharAngleIn" ), QStringLiteral( "25" ) ).toDouble();
914  maxCurvedCharAngleOut = placementElem.attribute( QStringLiteral( "maxCurvedCharAngleOut" ), QStringLiteral( "-25" ) ).toDouble();
915  priority = placementElem.attribute( QStringLiteral( "priority" ) ).toInt();
916  repeatDistance = placementElem.attribute( QStringLiteral( "repeatDistance" ), QStringLiteral( "0" ) ).toDouble();
917  repeatDistanceUnit = static_cast< SizeUnit >( placementElem.attribute( QStringLiteral( "repeatDistanceUnit" ), QString::number( MM ) ).toUInt() );
918  if ( !placementElem.hasAttribute( QStringLiteral( "repeatDistanceMapUnitScale" ) ) )
919  {
920  //fallback to older property
921  repeatDistanceMapUnitScale.minScale = placementElem.attribute( QStringLiteral( "repeatDistanceMapUnitMinScale" ), QStringLiteral( "0" ) ).toDouble();
922  repeatDistanceMapUnitScale.maxScale = placementElem.attribute( QStringLiteral( "repeatDistanceMapUnitMaxScale" ), QStringLiteral( "0" ) ).toDouble();
923  }
924  else
925  {
926  repeatDistanceMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( placementElem.attribute( QStringLiteral( "repeatDistanceMapUnitScale" ) ) );
927  }
928 
929  // rendering
930  QDomElement renderingElem = elem.firstChildElement( QStringLiteral( "rendering" ) );
931  scaleMin = renderingElem.attribute( QStringLiteral( "scaleMin" ), QStringLiteral( "0" ) ).toInt();
932  scaleMax = renderingElem.attribute( QStringLiteral( "scaleMax" ), QStringLiteral( "0" ) ).toInt();
933  scaleVisibility = renderingElem.attribute( QStringLiteral( "scaleVisibility" ) ).toInt();
934 
935  fontLimitPixelSize = renderingElem.attribute( QStringLiteral( "fontLimitPixelSize" ), QStringLiteral( "0" ) ).toInt();
936  fontMinPixelSize = renderingElem.attribute( QStringLiteral( "fontMinPixelSize" ), QStringLiteral( "0" ) ).toInt();
937  fontMaxPixelSize = renderingElem.attribute( QStringLiteral( "fontMaxPixelSize" ), QStringLiteral( "10000" ) ).toInt();
938  displayAll = renderingElem.attribute( QStringLiteral( "displayAll" ), QStringLiteral( "0" ) ).toInt();
939  upsidedownLabels = static_cast< UpsideDownLabels >( renderingElem.attribute( QStringLiteral( "upsidedownLabels" ), QString::number( Upright ) ).toUInt() );
940 
941  labelPerPart = renderingElem.attribute( QStringLiteral( "labelPerPart" ) ).toInt();
942  mergeLines = renderingElem.attribute( QStringLiteral( "mergeLines" ) ).toInt();
943  minFeatureSize = renderingElem.attribute( QStringLiteral( "minFeatureSize" ) ).toDouble();
944  limitNumLabels = renderingElem.attribute( QStringLiteral( "limitNumLabels" ), QStringLiteral( "0" ) ).toInt();
945  maxNumLabels = renderingElem.attribute( QStringLiteral( "maxNumLabels" ), QStringLiteral( "2000" ) ).toInt();
946  obstacle = renderingElem.attribute( QStringLiteral( "obstacle" ), QStringLiteral( "1" ) ).toInt();
947  obstacleFactor = renderingElem.attribute( QStringLiteral( "obstacleFactor" ), QStringLiteral( "1" ) ).toDouble();
948  obstacleType = static_cast< ObstacleType >( renderingElem.attribute( QStringLiteral( "obstacleType" ), QString::number( PolygonInterior ) ).toUInt() );
949  zIndex = renderingElem.attribute( QStringLiteral( "zIndex" ), QStringLiteral( "0.0" ) ).toDouble();
950 
951  QDomElement ddElem = elem.firstChildElement( QStringLiteral( "data-defined" ) );
952  readDataDefinedPropertyMap( nullptr, &ddElem, dataDefinedProperties );
953 }
954 
955 
956 
957 QDomElement QgsPalLayerSettings::writeXml( QDomDocument& doc )
958 {
959  // we assume (enabled == true && drawLabels == true) so those are not saved
960 
961  QDomElement textStyleElem = mFormat.writeXml( doc );
962 
963  // text style
964  textStyleElem.setAttribute( QStringLiteral( "fieldName" ), fieldName );
965  textStyleElem.setAttribute( QStringLiteral( "isExpression" ), isExpression );
966  textStyleElem.setAttribute( QStringLiteral( "previewBkgrdColor" ), previewBkgrdColor.name() );
967  QDomElement replacementElem = doc.createElement( QStringLiteral( "substitutions" ) );
968  substitutions.writeXml( replacementElem, doc );
969  textStyleElem.appendChild( replacementElem );
970  textStyleElem.setAttribute( QStringLiteral( "useSubstitutions" ), useSubstitutions );
971 
972  // text formatting
973  QDomElement textFormatElem = doc.createElement( QStringLiteral( "text-format" ) );
974  textFormatElem.setAttribute( QStringLiteral( "wrapChar" ), wrapChar );
975  textFormatElem.setAttribute( QStringLiteral( "multilineAlign" ), static_cast< unsigned int >( multilineAlign ) );
976  textFormatElem.setAttribute( QStringLiteral( "addDirectionSymbol" ), addDirectionSymbol );
977  textFormatElem.setAttribute( QStringLiteral( "leftDirectionSymbol" ), leftDirectionSymbol );
978  textFormatElem.setAttribute( QStringLiteral( "rightDirectionSymbol" ), rightDirectionSymbol );
979  textFormatElem.setAttribute( QStringLiteral( "reverseDirectionSymbol" ), reverseDirectionSymbol );
980  textFormatElem.setAttribute( QStringLiteral( "placeDirectionSymbol" ), static_cast< unsigned int >( placeDirectionSymbol ) );
981  textFormatElem.setAttribute( QStringLiteral( "formatNumbers" ), formatNumbers );
982  textFormatElem.setAttribute( QStringLiteral( "decimals" ), decimals );
983  textFormatElem.setAttribute( QStringLiteral( "plussign" ), plusSign );
984 
985  // placement
986  QDomElement placementElem = doc.createElement( QStringLiteral( "placement" ) );
987  placementElem.setAttribute( QStringLiteral( "placement" ), placement );
988  placementElem.setAttribute( QStringLiteral( "placementFlags" ), static_cast< unsigned int >( placementFlags ) );
989  placementElem.setAttribute( QStringLiteral( "centroidWhole" ), centroidWhole );
990  placementElem.setAttribute( QStringLiteral( "centroidInside" ), centroidInside );
991  placementElem.setAttribute( QStringLiteral( "predefinedPositionOrder" ), QgsLabelingUtils::encodePredefinedPositionOrder( predefinedPositionOrder ) );
992  placementElem.setAttribute( QStringLiteral( "fitInPolygonOnly" ), fitInPolygonOnly );
993  placementElem.setAttribute( QStringLiteral( "dist" ), dist );
994  placementElem.setAttribute( QStringLiteral( "distInMapUnits" ), distInMapUnits );
995  placementElem.setAttribute( QStringLiteral( "distMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( distMapUnitScale ) );
996  placementElem.setAttribute( QStringLiteral( "offsetType" ), static_cast< unsigned int >( offsetType ) );
997  placementElem.setAttribute( QStringLiteral( "quadOffset" ), static_cast< unsigned int >( quadOffset ) );
998  placementElem.setAttribute( QStringLiteral( "xOffset" ), xOffset );
999  placementElem.setAttribute( QStringLiteral( "yOffset" ), yOffset );
1000  placementElem.setAttribute( QStringLiteral( "labelOffsetInMapUnits" ), labelOffsetInMapUnits );
1001  placementElem.setAttribute( QStringLiteral( "labelOffsetMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( labelOffsetMapUnitScale ) );
1002  placementElem.setAttribute( QStringLiteral( "angleOffset" ), angleOffset );
1003  placementElem.setAttribute( QStringLiteral( "preserveRotation" ), preserveRotation );
1004  placementElem.setAttribute( QStringLiteral( "maxCurvedCharAngleIn" ), maxCurvedCharAngleIn );
1005  placementElem.setAttribute( QStringLiteral( "maxCurvedCharAngleOut" ), maxCurvedCharAngleOut );
1006  placementElem.setAttribute( QStringLiteral( "priority" ), priority );
1007  placementElem.setAttribute( QStringLiteral( "repeatDistance" ), repeatDistance );
1008  placementElem.setAttribute( QStringLiteral( "repeatDistanceUnit" ), repeatDistanceUnit );
1009  placementElem.setAttribute( QStringLiteral( "repeatDistanceMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( repeatDistanceMapUnitScale ) );
1010 
1011  // rendering
1012  QDomElement renderingElem = doc.createElement( QStringLiteral( "rendering" ) );
1013  renderingElem.setAttribute( QStringLiteral( "scaleVisibility" ), scaleVisibility );
1014  renderingElem.setAttribute( QStringLiteral( "scaleMin" ), scaleMin );
1015  renderingElem.setAttribute( QStringLiteral( "scaleMax" ), scaleMax );
1016  renderingElem.setAttribute( QStringLiteral( "fontLimitPixelSize" ), fontLimitPixelSize );
1017  renderingElem.setAttribute( QStringLiteral( "fontMinPixelSize" ), fontMinPixelSize );
1018  renderingElem.setAttribute( QStringLiteral( "fontMaxPixelSize" ), fontMaxPixelSize );
1019  renderingElem.setAttribute( QStringLiteral( "displayAll" ), displayAll );
1020  renderingElem.setAttribute( QStringLiteral( "upsidedownLabels" ), static_cast< unsigned int >( upsidedownLabels ) );
1021 
1022  renderingElem.setAttribute( QStringLiteral( "labelPerPart" ), labelPerPart );
1023  renderingElem.setAttribute( QStringLiteral( "mergeLines" ), mergeLines );
1024  renderingElem.setAttribute( QStringLiteral( "minFeatureSize" ), minFeatureSize );
1025  renderingElem.setAttribute( QStringLiteral( "limitNumLabels" ), limitNumLabels );
1026  renderingElem.setAttribute( QStringLiteral( "maxNumLabels" ), maxNumLabels );
1027  renderingElem.setAttribute( QStringLiteral( "obstacle" ), obstacle );
1028  renderingElem.setAttribute( QStringLiteral( "obstacleFactor" ), obstacleFactor );
1029  renderingElem.setAttribute( QStringLiteral( "obstacleType" ), static_cast< unsigned int >( obstacleType ) );
1030  renderingElem.setAttribute( QStringLiteral( "zIndex" ), zIndex );
1031 
1032  QDomElement ddElem = doc.createElement( QStringLiteral( "data-defined" ) );
1033  writeDataDefinedPropertyMap( nullptr, &ddElem, dataDefinedProperties );
1034 
1035  QDomElement elem = doc.createElement( QStringLiteral( "settings" ) );
1036  elem.appendChild( textStyleElem );
1037  elem.appendChild( textFormatElem );
1038  elem.appendChild( placementElem );
1039  elem.appendChild( renderingElem );
1040  elem.appendChild( ddElem );
1041  return elem;
1042 }
1043 
1045  bool active, bool useExpr, const QString& expr, const QString& field )
1046 {
1047  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
1048 
1049  if ( dataDefinedProperties.contains( p ) )
1050  {
1051  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.constFind( p );
1052  if ( it != dataDefinedProperties.constEnd() )
1053  {
1054  QgsDataDefined* dd = it.value();
1055  dd->setActive( active );
1056  dd->setExpressionString( expr );
1057  dd->setField( field );
1058  dd->setUseExpression( useExpr );
1059  }
1060  }
1061  else if ( !defaultVals )
1062  {
1063  QgsDataDefined* dd = new QgsDataDefined( active, useExpr, expr, field );
1064  dataDefinedProperties.insert( p, dd );
1065  }
1066 }
1067 
1069 {
1070  QMap< DataDefinedProperties, QgsDataDefined* >::iterator it = dataDefinedProperties.find( p );
1071  if ( it != dataDefinedProperties.end() )
1072  {
1073  delete( it.value() );
1074  dataDefinedProperties.erase( it );
1075  }
1076 }
1077 
1079 {
1080  qDeleteAll( dataDefinedProperties );
1081  dataDefinedProperties.clear();
1082 }
1083 
1084 QString QgsPalLayerSettings::updateDataDefinedString( const QString& value )
1085 {
1086  // TODO: update or remove this when project settings for labeling are migrated to better XML layout
1087  QString newValue = value;
1088  if ( !value.isEmpty() && !value.contains( QLatin1String( "~~" ) ) )
1089  {
1090  QStringList values;
1091  values << QStringLiteral( "1" ); // all old-style values are active if not empty
1092  values << QStringLiteral( "0" );
1093  values << QLatin1String( "" );
1094  values << value; // all old-style values are only field names
1095  newValue = values.join( QStringLiteral( "~~" ) );
1096  }
1097 
1098  return newValue;
1099 }
1100 
1102 {
1103  if ( dataDefinedProperties.isEmpty() )
1104  return nullptr;
1105 
1106  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.constFind( p );
1107  if ( it != dataDefinedProperties.constEnd() )
1108  {
1109  return it.value();
1110  }
1111  return nullptr;
1112 }
1113 
1115 {
1116  QMap<QString, QString> map;
1117  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
1118  if ( it != dataDefinedProperties.constEnd() )
1119  {
1120  return it.value()->toMap();
1121  }
1122  return map;
1123 }
1124 
1126 {
1127  if ( dataDefinedProperties.isEmpty() || !dataDefinedProperties.contains( p ) )
1128  {
1129  return QVariant();
1130  }
1131 
1132  //try to keep < 2.12 API - handle no passed expression context
1133  QScopedPointer< QgsExpressionContext > scopedEc;
1134  if ( !context )
1135  {
1136  scopedEc.reset( new QgsExpressionContext() );
1137  scopedEc->setFeature( f );
1138  scopedEc->setFields( fields );
1139  }
1140  const QgsExpressionContext* ec = context ? context : scopedEc.data();
1141 
1142  QgsDataDefined* dd = nullptr;
1143  QMap< DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
1144  if ( it != dataDefinedProperties.constEnd() )
1145  {
1146  dd = it.value();
1147  }
1148 
1149  if ( !dd )
1150  {
1151  return QVariant();
1152  }
1153 
1154  if ( !dd->isActive() )
1155  {
1156  return QVariant();
1157  }
1158 
1159  QVariant result = QVariant();
1160  bool useExpression = dd->useExpression();
1161  QString field = dd->field();
1162 
1163  //QgsDebugMsgLevel( QString( "isActive:" ) + isActive ? "1" : "0", 4 );
1164  //QgsDebugMsgLevel( QString( "useExpression:" ) + useExpression ? "1" : "0", 4 );
1165  //QgsDebugMsgLevel( QString( "expression:" ) + dd->expressionString(), 4 );
1166  //QgsDebugMsgLevel( QString( "field:" ) + field, 4 );
1167 
1168  if ( useExpression && dd->expressionIsPrepared() )
1169  {
1170  QgsExpression* expr = dd->expression();
1171  //QgsDebugMsgLevel( QString( "expr columns:" ) + expr->referencedColumns().join( "," ), 4 );
1172 
1173  result = expr->evaluate( ec );
1174  if ( expr->hasEvalError() )
1175  {
1176  QgsDebugMsgLevel( QString( "Evaluate error:" ) + expr->evalErrorString(), 4 );
1177  return QVariant();
1178  }
1179  }
1180  else if ( !useExpression && !field.isEmpty() )
1181  {
1182  // use direct attribute access instead of evaluating "field" expression (much faster)
1183  int indx = fields.indexFromName( field );
1184  if ( indx != -1 )
1185  {
1186  result = f.attribute( indx );
1187  }
1188  }
1189  return result;
1190 }
1191 
1192 bool QgsPalLayerSettings::dataDefinedEvaluate( DataDefinedProperties p, QVariant& exprVal, QgsExpressionContext *context, const QVariant& originalValue ) const
1193 {
1194  // null passed-around QVariant
1195  exprVal.clear();
1196  if ( dataDefinedProperties.isEmpty() )
1197  return false;
1198 
1199  //try to keep < 2.12 API - handle no passed expression context
1200  QScopedPointer< QgsExpressionContext > scopedEc;
1201  if ( !context )
1202  {
1203  scopedEc.reset( new QgsExpressionContext() );
1204  scopedEc->setFeature( *mCurFeat );
1205  scopedEc->setFields( mCurFields );
1206  }
1207  QgsExpressionContext* ec = context ? context : scopedEc.data();
1208 
1209  ec->setOriginalValueVariable( originalValue );
1210  QVariant result = dataDefinedValue( p, *mCurFeat, mCurFields, ec );
1211 
1212  if ( result.isValid() && !result.isNull() )
1213  {
1214  //QgsDebugMsgLevel( QString( "result type:" ) + QString( result.typeName() ), 4 );
1215  //QgsDebugMsgLevel( QString( "result string:" ) + result.toString(), 4 );
1216  exprVal = result;
1217  return true;
1218  }
1219 
1220  return false;
1221 }
1222 
1224 {
1225  if ( dataDefinedProperties.isEmpty() )
1226  return false;
1227 
1228  bool isActive = false;
1229 
1230  QMap< DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
1231  if ( it != dataDefinedProperties.constEnd() )
1232  {
1233  isActive = it.value()->isActive();
1234  }
1235 
1236  return isActive;
1237 }
1238 
1240 {
1241  if ( dataDefinedProperties.isEmpty() )
1242  return false;
1243 
1244  bool useExpression = false;
1245  QMap< DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
1246  if ( it != dataDefinedProperties.constEnd() )
1247  {
1248  useExpression = it.value()->useExpression();
1249  }
1250 
1251  return useExpression;
1252 }
1253 
1254 bool QgsPalLayerSettings::checkMinimumSizeMM( const QgsRenderContext& ct, const QgsGeometry& geom, double minSize ) const
1255 {
1256  return QgsPalLabeling::checkMinimumSizeMM( ct, &geom, minSize );
1257 }
1258 
1259 void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString text, double& labelX, double& labelY, QgsFeature* f, QgsRenderContext *context )
1260 {
1261  if ( !fm || !f )
1262  {
1263  return;
1264  }
1265 
1266  //try to keep < 2.12 API - handle no passed render context
1267  QScopedPointer< QgsRenderContext > scopedRc;
1268  if ( !context )
1269  {
1270  scopedRc.reset( new QgsRenderContext() );
1271  if ( f )
1272  scopedRc->expressionContext().setFeature( *f );
1273  }
1274  QgsRenderContext* rc = context ? context : scopedRc.data();
1275 
1276  QString wrapchr = wrapChar;
1277  double multilineH = mFormat.lineHeight();
1278 
1279  bool addDirSymb = addDirectionSymbol;
1280  QString leftDirSymb = leftDirectionSymbol;
1281  QString rightDirSymb = rightDirectionSymbol;
1283 
1284  if ( f == mCurFeat ) // called internally, use any stored data defined values
1285  {
1286  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
1287  {
1288  wrapchr = dataDefinedValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
1289  }
1290 
1291  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
1292  {
1293  multilineH = dataDefinedValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
1294  }
1295 
1296  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
1297  {
1298  addDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
1299  }
1300 
1301  if ( addDirSymb )
1302  {
1303 
1304  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
1305  {
1306  leftDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
1307  }
1308  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbRight ) )
1309  {
1310  rightDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
1311  }
1312 
1313  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
1314  {
1315  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( dataDefinedValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt() );
1316  }
1317 
1318  }
1319 
1320  }
1321  else // called externally with passed-in feature, evaluate data defined
1322  {
1325  if ( exprVal.isValid() )
1326  {
1327  wrapchr = exprVal.toString();
1328  }
1329  exprVal.clear();
1330  rc->expressionContext().setOriginalValueVariable( multilineH );
1332  if ( exprVal.isValid() )
1333  {
1334  bool ok;
1335  double size = exprVal.toDouble( &ok );
1336  if ( ok )
1337  {
1338  multilineH = size;
1339  }
1340  }
1341 
1342  exprVal.clear();
1343  rc->expressionContext().setOriginalValueVariable( addDirSymb );
1345  if ( exprVal.isValid() )
1346  {
1347  addDirSymb = exprVal.toBool();
1348  }
1349 
1350  if ( addDirSymb ) // don't do extra evaluations if not adding a direction symbol
1351  {
1352  exprVal.clear();
1353  rc->expressionContext().setOriginalValueVariable( leftDirSymb );
1355  if ( exprVal.isValid() )
1356  {
1357  leftDirSymb = exprVal.toString();
1358  }
1359  exprVal.clear();
1360  rc->expressionContext().setOriginalValueVariable( rightDirSymb );
1362  if ( exprVal.isValid() )
1363  {
1364  rightDirSymb = exprVal.toString();
1365  }
1366  exprVal.clear();
1367  rc->expressionContext().setOriginalValueVariable( static_cast< int >( placeDirSymb ) );
1369  if ( exprVal.isValid() )
1370  {
1371  bool ok;
1372  int enmint = exprVal.toInt( &ok );
1373  if ( ok )
1374  {
1375  placeDirSymb = static_cast< QgsPalLayerSettings::DirectionSymbols >( enmint );
1376  }
1377  }
1378  }
1379 
1380  }
1381 
1382  if ( wrapchr.isEmpty() )
1383  {
1384  wrapchr = QStringLiteral( "\n" ); // default to new line delimiter
1385  }
1386 
1387  //consider the space needed for the direction symbol
1388  if ( addDirSymb && placement == QgsPalLayerSettings::Line
1389  && ( !leftDirSymb.isEmpty() || !rightDirSymb.isEmpty() ) )
1390  {
1391  QString dirSym = leftDirSymb;
1392 
1393  if ( fm->width( rightDirSymb ) > fm->width( dirSym ) )
1394  dirSym = rightDirSymb;
1395 
1396  if ( placeDirSymb == QgsPalLayerSettings::SymbolLeftRight )
1397  {
1398  text.append( dirSym );
1399  }
1400  else
1401  {
1402  text.prepend( dirSym + QStringLiteral( "\n" ) ); // SymbolAbove or SymbolBelow
1403  }
1404  }
1405 
1406  double w = 0.0, h = 0.0;
1407  QStringList multiLineSplit = QgsPalLabeling::splitToLines( text, wrapchr );
1408  int lines = multiLineSplit.size();
1409 
1410  double labelHeight = fm->ascent() + fm->descent(); // ignore +1 for baseline
1411 
1412  h += fm->height() + static_cast< double >(( lines - 1 ) * labelHeight * multilineH );
1413  h /= context->rasterScaleFactor();
1414 
1415  for ( int i = 0; i < lines; ++i )
1416  {
1417  double width = fm->width( multiLineSplit.at( i ) );
1418  if ( width > w )
1419  {
1420  w = width;
1421  }
1422  }
1423  w /= context->rasterScaleFactor();
1424 
1425 #if 0 // XXX strk
1426  QgsPoint ptSize = xform->toMapCoordinatesF( w, h );
1427  labelX = qAbs( ptSize.x() - ptZero.x() );
1428  labelY = qAbs( ptSize.y() - ptZero.y() );
1429 #else
1430  double uPP = xform->mapUnitsPerPixel();
1431  labelX = w * uPP;
1432  labelY = h * uPP;
1433 #endif
1434 }
1435 
1437 {
1438  // either used in QgsPalLabeling (palLayer is set) or in QgsLabelingEngine (labelFeature is set)
1439  Q_ASSERT( labelFeature );
1440 
1441  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
1442  mCurFeat = &f;
1443 
1444  // data defined is obstacle? calculate this first, to avoid wasting time working with obstacles we don't require
1445  bool isObstacle = obstacle; // start with layer default
1447  {
1448  isObstacle = exprVal.toBool();
1449  }
1450 
1451  if ( !drawLabels )
1452  {
1453  if ( isObstacle )
1454  {
1455  registerObstacleFeature( f, context, labelFeature, obstacleGeometry );
1456  }
1457  return;
1458  }
1459 
1460 // mCurFields = &layer->pendingFields();
1461 
1462  // store data defined-derived values for later adding to label feature for use during rendering
1463  dataDefinedValues.clear();
1464 
1465  // data defined show label? defaults to show label if not 0
1467  {
1468  bool showLabel = dataDefinedEvaluate( QgsPalLayerSettings::Show, exprVal, &context.expressionContext(), true );
1469  showLabel &= exprVal.toBool();
1470  QgsDebugMsgLevel( QString( "exprVal Show:%1" ).arg( showLabel ? "true" : "false" ), 4 );
1471  if ( !showLabel )
1472  {
1473  return;
1474  }
1475  }
1476 
1477  // data defined scale visibility?
1478  bool useScaleVisibility = scaleVisibility;
1480  {
1481  QgsDebugMsgLevel( QString( "exprVal ScaleVisibility:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
1482  useScaleVisibility = exprVal.toBool();
1483  }
1484 
1485  if ( useScaleVisibility )
1486  {
1487  // data defined min scale?
1488  double minScale = scaleMin;
1490  {
1491  QgsDebugMsgLevel( QString( "exprVal MinScale:%1" ).arg( exprVal.toDouble() ), 4 );
1492  bool conversionOk;
1493  double mins = exprVal.toDouble( &conversionOk );
1494  if ( conversionOk )
1495  {
1496  minScale = mins;
1497  }
1498  }
1499 
1500  // scales closer than 1:1
1501  if ( minScale < 0 )
1502  {
1503  minScale = 1 / qAbs( minScale );
1504  }
1505 
1506  if ( !qgsDoubleNear( minScale, 0.0 ) && context.rendererScale() < minScale )
1507  {
1508  return;
1509  }
1510 
1511  // data defined max scale?
1512  double maxScale = scaleMax;
1514  {
1515  QgsDebugMsgLevel( QString( "exprVal MaxScale:%1" ).arg( exprVal.toDouble() ), 4 );
1516  bool conversionOk;
1517  double maxs = exprVal.toDouble( &conversionOk );
1518  if ( conversionOk )
1519  {
1520  maxScale = maxs;
1521  }
1522  }
1523 
1524  // scales closer than 1:1
1525  if ( maxScale < 0 )
1526  {
1527  maxScale = 1 / qAbs( maxScale );
1528  }
1529 
1530  if ( !qgsDoubleNear( maxScale, 0.0 ) && context.rendererScale() > maxScale )
1531  {
1532  return;
1533  }
1534  }
1535 
1536  QFont labelFont = mFormat.font();
1537  // labelFont will be added to label feature for use during label painting
1538 
1539  // data defined font units?
1540  QgsUnitTypes::RenderUnit fontunits = mFormat.sizeUnit();
1542  {
1543  QString units = exprVal.toString();
1544  QgsDebugMsgLevel( QString( "exprVal Font units:%1" ).arg( units ), 4 );
1545  if ( !units.isEmpty() )
1546  {
1547  bool ok;
1549  if ( ok )
1550  fontunits = res;
1551  }
1552  }
1553 
1554  //data defined label size?
1555  double fontSize = mFormat.size();
1556  if ( dataDefinedEvaluate( QgsPalLayerSettings::Size, exprVal, &context.expressionContext(), fontSize ) )
1557  {
1558  QgsDebugMsgLevel( QString( "exprVal Size:%1" ).arg( exprVal.toDouble() ), 4 );
1559  bool ok;
1560  double size = exprVal.toDouble( &ok );
1561  if ( ok )
1562  {
1563  fontSize = size;
1564  }
1565  }
1566  if ( fontSize <= 0.0 )
1567  {
1568  return;
1569  }
1570 
1571  int fontPixelSize = QgsTextRenderer::sizeToPixel( fontSize, context, fontunits, true, mFormat.sizeMapUnitScale() );
1572  // don't try to show font sizes less than 1 pixel (Qt complains)
1573  if ( fontPixelSize < 1 )
1574  {
1575  return;
1576  }
1577  labelFont.setPixelSize( fontPixelSize );
1578 
1579  // NOTE: labelFont now always has pixelSize set, so pointSize or pointSizeF might return -1
1580 
1581  // defined 'minimum/maximum pixel font size'?
1582  if ( fontunits == QgsUnitTypes::RenderMapUnits )
1583  {
1584  bool useFontLimitPixelSize = fontLimitPixelSize;
1586  {
1587  QgsDebugMsgLevel( QString( "exprVal FontLimitPixel:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
1588  useFontLimitPixelSize = exprVal.toBool();
1589  }
1590 
1591  if ( useFontLimitPixelSize )
1592  {
1593  int fontMinPixel = fontMinPixelSize;
1595  {
1596  bool ok;
1597  int sizeInt = exprVal.toInt( &ok );
1598  QgsDebugMsgLevel( QString( "exprVal FontMinPixel:%1" ).arg( sizeInt ), 4 );
1599  if ( ok )
1600  {
1601  fontMinPixel = sizeInt;
1602  }
1603  }
1604 
1605  int fontMaxPixel = fontMaxPixelSize;
1607  {
1608  bool ok;
1609  int sizeInt = exprVal.toInt( &ok );
1610  QgsDebugMsgLevel( QString( "exprVal FontMaxPixel:%1" ).arg( sizeInt ), 4 );
1611  if ( ok )
1612  {
1613  fontMaxPixel = sizeInt;
1614  }
1615  }
1616 
1617  if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
1618  {
1619  return;
1620  }
1621  }
1622  }
1623 
1624  // NOTE: the following parsing functions calculate and store any data defined values for later use in QgsPalLabeling::drawLabeling
1625  // this is done to provide clarity, and because such parsing is not directly related to PAL feature registration calculations
1626 
1627  // calculate rest of font attributes and store any data defined values
1628  // this is done here for later use in making label backgrounds part of collision management (when implemented)
1629  labelFont.setCapitalization( QFont::MixedCase ); // reset this - we don't use QFont's handling as it breaks with curved labels
1630  parseTextStyle( labelFont, fontunits, context );
1631  parseTextFormatting( context );
1632  parseTextBuffer( context );
1633  parseShapeBackground( context );
1634  parseDropShadow( context );
1635 
1636  QString labelText;
1637 
1638  // Check to see if we are a expression string.
1639  if ( isExpression )
1640  {
1642  if ( exp->hasParserError() )
1643  {
1644  QgsDebugMsgLevel( QString( "Expression parser error:%1" ).arg( exp->parserErrorString() ), 4 );
1645  return;
1646  }
1647 
1648  QVariant result = exp->evaluate( &context.expressionContext() ); // expression prepared in QgsPalLabeling::prepareLayer()
1649  if ( exp->hasEvalError() )
1650  {
1651  QgsDebugMsgLevel( QString( "Expression parser eval error:%1" ).arg( exp->evalErrorString() ), 4 );
1652  return;
1653  }
1654  labelText = result.isNull() ? QLatin1String( "" ) : result.toString();
1655  }
1656  else
1657  {
1658  const QVariant &v = f.attribute( fieldIndex );
1659  labelText = v.isNull() ? QLatin1String( "" ) : v.toString();
1660  }
1661 
1662  // apply text replacements
1663  if ( useSubstitutions )
1664  {
1665  labelText = substitutions.process( labelText );
1666  }
1667 
1668  // apply capitalization
1670  // maintain API - capitalization may have been set in textFont
1671  if ( mFormat.font().capitalization() != QFont::MixedCase )
1672  {
1673  capitalization = static_cast< QgsStringUtils::Capitalization >( mFormat.font().capitalization() );
1674  }
1675  // data defined font capitalization?
1677  {
1678  QString fcase = exprVal.toString().trimmed();
1679  QgsDebugMsgLevel( QString( "exprVal FontCase:%1" ).arg( fcase ), 4 );
1680 
1681  if ( !fcase.isEmpty() )
1682  {
1683  if ( fcase.compare( QLatin1String( "NoChange" ), Qt::CaseInsensitive ) == 0 )
1684  {
1685  capitalization = QgsStringUtils::MixedCase;
1686  }
1687  else if ( fcase.compare( QLatin1String( "Upper" ), Qt::CaseInsensitive ) == 0 )
1688  {
1689  capitalization = QgsStringUtils::AllUppercase;
1690  }
1691  else if ( fcase.compare( QLatin1String( "Lower" ), Qt::CaseInsensitive ) == 0 )
1692  {
1693  capitalization = QgsStringUtils::AllLowercase;
1694  }
1695  else if ( fcase.compare( QLatin1String( "Capitalize" ), Qt::CaseInsensitive ) == 0 )
1696  {
1698  }
1699  }
1700  }
1701  labelText = QgsStringUtils::capitalize( labelText, capitalization );
1702 
1703  // data defined format numbers?
1704  bool formatnum = formatNumbers;
1706  {
1707  formatnum = exprVal.toBool();
1708  QgsDebugMsgLevel( QString( "exprVal NumFormat:%1" ).arg( formatnum ? "true" : "false" ), 4 );
1709  }
1710 
1711  // format number if label text is coercible to a number
1712  if ( formatnum )
1713  {
1714  // data defined decimal places?
1715  int decimalPlaces = decimals;
1717  {
1718  bool ok;
1719  int dInt = exprVal.toInt( &ok );
1720  QgsDebugMsgLevel( QString( "exprVal NumDecimals:%1" ).arg( dInt ), 4 );
1721  if ( ok && dInt > 0 ) // needs to be positive
1722  {
1723  decimalPlaces = dInt;
1724  }
1725  }
1726 
1727  // data defined plus sign?
1728  bool signPlus = plusSign;
1730  {
1731  signPlus = exprVal.toBool();
1732  QgsDebugMsgLevel( QString( "exprVal NumPlusSign:%1" ).arg( signPlus ? "true" : "false" ), 4 );
1733  }
1734 
1735  QVariant textV( labelText );
1736  bool ok;
1737  double d = textV.toDouble( &ok );
1738  if ( ok )
1739  {
1740  QString numberFormat;
1741  if ( d > 0 && signPlus )
1742  {
1743  numberFormat.append( '+' );
1744  }
1745  numberFormat.append( "%1" );
1746  labelText = numberFormat.arg( d, 0, 'f', decimalPlaces );
1747  }
1748  }
1749 
1750  // NOTE: this should come AFTER any option that affects font metrics
1751  QScopedPointer<QFontMetricsF> labelFontMetrics( new QFontMetricsF( labelFont ) );
1752  double labelX, labelY; // will receive label size
1753  calculateLabelSize( labelFontMetrics.data(), labelText, labelX, labelY, mCurFeat, &context );
1754 
1755 
1756  // maximum angle between curved label characters (hardcoded defaults used in QGIS <2.0)
1757  //
1758  double maxcharanglein = 20.0; // range 20.0-60.0
1759  double maxcharangleout = -20.0; // range 20.0-95.0
1760 
1762  {
1763  maxcharanglein = maxCurvedCharAngleIn;
1764  maxcharangleout = maxCurvedCharAngleOut;
1765 
1766  //data defined maximum angle between curved label characters?
1768  {
1769  QString ptstr = exprVal.toString().trimmed();
1770  QgsDebugMsgLevel( QString( "exprVal CurvedCharAngleInOut:%1" ).arg( ptstr ), 4 );
1771 
1772  if ( !ptstr.isEmpty() )
1773  {
1774  QPointF maxcharanglePt = QgsSymbolLayerUtils::decodePoint( ptstr );
1775  maxcharanglein = qBound( 20.0, static_cast< double >( maxcharanglePt.x() ), 60.0 );
1776  maxcharangleout = qBound( 20.0, static_cast< double >( maxcharanglePt.y() ), 95.0 );
1777  }
1778  }
1779  // make sure maxcharangleout is always negative
1780  maxcharangleout = -( qAbs( maxcharangleout ) );
1781  }
1782 
1783  // data defined centroid whole or clipped?
1784  bool wholeCentroid = centroidWhole;
1786  {
1787  QString str = exprVal.toString().trimmed();
1788  QgsDebugMsgLevel( QString( "exprVal CentroidWhole:%1" ).arg( str ), 4 );
1789 
1790  if ( !str.isEmpty() )
1791  {
1792  if ( str.compare( QLatin1String( "Visible" ), Qt::CaseInsensitive ) == 0 )
1793  {
1794  wholeCentroid = false;
1795  }
1796  else if ( str.compare( QLatin1String( "Whole" ), Qt::CaseInsensitive ) == 0 )
1797  {
1798  wholeCentroid = true;
1799  }
1800  }
1801  }
1802 
1803  QgsGeometry geom = f.geometry();
1804  if ( geom.isEmpty() )
1805  {
1806  return;
1807  }
1808 
1809  // simplify?
1810  const QgsVectorSimplifyMethod &simplifyMethod = context.vectorSimplifyMethod();
1811  QScopedPointer<QgsGeometry> scopedClonedGeom;
1812  if ( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && simplifyMethod.forceLocalOptimization() )
1813  {
1814  int simplifyHints = simplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
1816  QgsGeometry g = geom;
1817  QgsMapToPixelSimplifier simplifier( simplifyHints, simplifyMethod.tolerance(), simplifyAlgorithm );
1818  geom = simplifier.simplify( geom );
1819  }
1820 
1821  // whether we're going to create a centroid for polygon
1822  bool centroidPoly = (( placement == QgsPalLayerSettings::AroundPoint
1824  && geom.type() == QgsWkbTypes::PolygonGeometry );
1825 
1826  // CLIP the geometry if it is bigger than the extent
1827  // don't clip if centroid is requested for whole feature
1828  bool doClip = false;
1829  if ( !centroidPoly || !wholeCentroid )
1830  {
1831  doClip = true;
1832  }
1833 
1834  // if using fitInPolygonOnly option, generate the permissible zone (must happen before geometry is modified - eg
1835  // as a result of using perimeter based labeling and the geometry is converted to a boundary)
1836  QgsGeometry permissibleZone;
1838  {
1839  permissibleZone = geom;
1840  if ( QgsPalLabeling::geometryRequiresPreparation( permissibleZone, context, ct, doClip ? &extentGeom : nullptr ) )
1841  {
1842  permissibleZone = QgsPalLabeling::prepareGeometry( permissibleZone, context, ct, doClip ? &extentGeom : nullptr );
1843  }
1844  }
1845 
1846  // if using perimeter based labeling for polygons, get the polygon's
1847  // linear boundary and use that for the label geometry
1848  if (( geom.type() == QgsWkbTypes::PolygonGeometry )
1849  && ( placement == Line || placement == PerimeterCurved ) )
1850  {
1851  geom = QgsGeometry( geom.geometry()->boundary() );
1852  }
1853 
1854  GEOSGeometry* geos_geom_clone = nullptr;
1855  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, doClip ? &extentGeom : nullptr ) )
1856  {
1857  geom = QgsPalLabeling::prepareGeometry( geom, context, ct, doClip ? &extentGeom : nullptr );
1858 
1859  if ( geom.isEmpty() )
1860  return;
1861  }
1862  geos_geom_clone = geom.exportToGeos();
1863 
1864  QScopedPointer<QgsGeometry> scopedObstacleGeom;
1865  if ( isObstacle )
1866  {
1867  if ( obstacleGeometry && QgsPalLabeling::geometryRequiresPreparation( *obstacleGeometry, context, ct, doClip ? &extentGeom : nullptr ) )
1868  {
1869  scopedObstacleGeom.reset( new QgsGeometry( QgsPalLabeling::prepareGeometry( *obstacleGeometry, context, ct, doClip ? &extentGeom : nullptr ) ) );
1870  obstacleGeometry = scopedObstacleGeom.data();
1871  }
1872  }
1873 
1874  if ( minFeatureSize > 0 && !checkMinimumSizeMM( context, geom, minFeatureSize ) )
1875  return;
1876 
1877  if ( !geos_geom_clone )
1878  return; // invalid geometry
1879 
1880  // likelihood exists label will be registered with PAL and may be drawn
1881  // check if max number of features to label (already registered with PAL) has been reached
1882  // Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
1883  if ( limitNumLabels )
1884  {
1885  if ( !maxNumLabels )
1886  {
1887  return;
1888  }
1889  if ( mFeatsRegPal >= maxNumLabels )
1890  {
1891  return;
1892  }
1893 
1894  int divNum = static_cast< int >(( static_cast< double >( mFeaturesToLabel ) / maxNumLabels ) + 0.5 );
1895  if ( divNum && ( mFeatsRegPal == static_cast< int >( mFeatsSendingToPal / divNum ) ) )
1896  {
1897  mFeatsSendingToPal += 1;
1898  if ( divNum && mFeatsSendingToPal % divNum )
1899  {
1900  return;
1901  }
1902  }
1903  }
1904 
1905  GEOSGeometry* geosObstacleGeomClone = nullptr;
1906  if ( obstacleGeometry )
1907  {
1908  geosObstacleGeomClone = obstacleGeometry->exportToGeos();
1909  }
1910 
1911 
1912  //data defined position / alignment / rotation?
1913  bool dataDefinedPosition = false;
1914  bool layerDefinedRotation = false;
1915  bool dataDefinedRotation = false;
1916  double xPos = 0.0, yPos = 0.0, angle = 0.0;
1917  bool ddXPos = false, ddYPos = false;
1918  double quadOffsetX = 0.0, quadOffsetY = 0.0;
1919  double offsetX = 0.0, offsetY = 0.0;
1920 
1921  //data defined quadrant offset?
1922  bool ddFixedQuad = false;
1923  QuadrantPosition quadOff = quadOffset;
1924  if ( dataDefinedEvaluate( QgsPalLayerSettings::OffsetQuad, exprVal, &context.expressionContext(), static_cast< int >( quadOff ) ) )
1925  {
1926  bool ok;
1927  int quadInt = exprVal.toInt( &ok );
1928  QgsDebugMsgLevel( QString( "exprVal OffsetQuad:%1" ).arg( quadInt ), 4 );
1929  if ( ok && 0 <= quadInt && quadInt <= 8 )
1930  {
1931  quadOff = static_cast< QuadrantPosition >( quadInt );
1932  ddFixedQuad = true;
1933  }
1934  }
1935 
1936  // adjust quadrant offset of labels
1937  switch ( quadOff )
1938  {
1939  case QuadrantAboveLeft:
1940  quadOffsetX = -1.0;
1941  quadOffsetY = 1.0;
1942  break;
1943  case QuadrantAbove:
1944  quadOffsetX = 0.0;
1945  quadOffsetY = 1.0;
1946  break;
1947  case QuadrantAboveRight:
1948  quadOffsetX = 1.0;
1949  quadOffsetY = 1.0;
1950  break;
1951  case QuadrantLeft:
1952  quadOffsetX = -1.0;
1953  quadOffsetY = 0.0;
1954  break;
1955  case QuadrantRight:
1956  quadOffsetX = 1.0;
1957  quadOffsetY = 0.0;
1958  break;
1959  case QuadrantBelowLeft:
1960  quadOffsetX = -1.0;
1961  quadOffsetY = -1.0;
1962  break;
1963  case QuadrantBelow:
1964  quadOffsetX = 0.0;
1965  quadOffsetY = -1.0;
1966  break;
1967  case QuadrantBelowRight:
1968  quadOffsetX = 1.0;
1969  quadOffsetY = -1.0;
1970  break;
1971  case QuadrantOver:
1972  default:
1973  break;
1974  }
1975 
1976  //data defined label offset?
1977  double xOff = xOffset;
1978  double yOff = yOffset;
1980  {
1981  QString ptstr = exprVal.toString().trimmed();
1982  QgsDebugMsgLevel( QString( "exprVal OffsetXY:%1" ).arg( ptstr ), 4 );
1983 
1984  if ( !ptstr.isEmpty() )
1985  {
1986  QPointF ddOffPt = QgsSymbolLayerUtils::decodePoint( ptstr );
1987  xOff = ddOffPt.x();
1988  yOff = ddOffPt.y();
1989  }
1990  }
1991 
1992  // data defined label offset units?
1993  bool offinmapunits = labelOffsetInMapUnits;
1995  {
1996  QString units = exprVal.toString().trimmed();
1997  QgsDebugMsgLevel( QString( "exprVal OffsetUnits:%1" ).arg( units ), 4 );
1998  if ( !units.isEmpty() )
1999  {
2000  offinmapunits = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2001  }
2002  }
2003 
2004  // adjust offset of labels to match chosen unit and map scale
2005  // offsets match those of symbology: -x = left, -y = up
2006  double mapUntsPerMM = labelOffsetMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor();
2007  if ( !qgsDoubleNear( xOff, 0.0 ) )
2008  {
2009  offsetX = xOff; // must be positive to match symbology offset direction
2010  if ( !offinmapunits )
2011  {
2012  offsetX *= mapUntsPerMM; //convert offset from mm to map units
2013  }
2014  }
2015  if ( !qgsDoubleNear( yOff, 0.0 ) )
2016  {
2017  offsetY = -yOff; // must be negative to match symbology offset direction
2018  if ( !offinmapunits )
2019  {
2020  offsetY *= mapUntsPerMM; //convert offset from mm to map units
2021  }
2022  }
2023 
2024  // layer defined rotation?
2025  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2027  {
2028  layerDefinedRotation = true;
2029  angle = angleOffset * M_PI / 180; // convert to radians
2030  }
2031 
2032  const QgsMapToPixel& m2p = context.mapToPixel();
2033  //data defined rotation?
2035  {
2036  bool ok;
2037  double rotD = exprVal.toDouble( &ok );
2038  QgsDebugMsgLevel( QString( "exprVal Rotation:%1" ).arg( rotD ), 4 );
2039  if ( ok )
2040  {
2041  dataDefinedRotation = true;
2042  // TODO: add setting to disable having data defined rotation follow
2043  // map rotation ?
2044  rotD -= m2p.mapRotation();
2045  angle = rotD * M_PI / 180.0;
2046  }
2047  }
2048 
2050  {
2051  if ( !exprVal.isNull() )
2052  xPos = exprVal.toDouble( &ddXPos );
2053  QgsDebugMsgLevel( QString( "exprVal PositionX:%1" ).arg( xPos ), 4 );
2054 
2056  {
2057  //data defined position. But field values could be NULL -> positions will be generated by PAL
2058  if ( !exprVal.isNull() )
2059  yPos = exprVal.toDouble( &ddYPos );
2060  QgsDebugMsgLevel( QString( "exprVal PositionY:%1" ).arg( yPos ), 4 );
2061 
2062  if ( ddXPos && ddYPos )
2063  {
2064  dataDefinedPosition = true;
2065  // layer rotation set, but don't rotate pinned labels unless data defined
2066  if ( layerDefinedRotation && !dataDefinedRotation )
2067  {
2068  angle = 0.0;
2069  }
2070 
2071  //x/y shift in case of alignment
2072  double xdiff = 0.0;
2073  double ydiff = 0.0;
2074 
2075  //horizontal alignment
2076  if ( dataDefinedEvaluate( QgsPalLayerSettings::Hali, exprVal, &context.expressionContext() ) )
2077  {
2078  QString haliString = exprVal.toString();
2079  QgsDebugMsgLevel( QString( "exprVal Hali:%1" ).arg( haliString ), 4 );
2080  if ( haliString.compare( QLatin1String( "Center" ), Qt::CaseInsensitive ) == 0 )
2081  {
2082  xdiff -= labelX / 2.0;
2083  }
2084  else if ( haliString.compare( QLatin1String( "Right" ), Qt::CaseInsensitive ) == 0 )
2085  {
2086  xdiff -= labelX;
2087  }
2088  }
2089 
2090  //vertical alignment
2091  if ( dataDefinedEvaluate( QgsPalLayerSettings::Vali, exprVal, &context.expressionContext() ) )
2092  {
2093  QString valiString = exprVal.toString();
2094  QgsDebugMsgLevel( QString( "exprVal Vali:%1" ).arg( valiString ), 4 );
2095 
2096  if ( valiString.compare( QLatin1String( "Bottom" ), Qt::CaseInsensitive ) != 0 )
2097  {
2098  if ( valiString.compare( QLatin1String( "Top" ), Qt::CaseInsensitive ) == 0 )
2099  {
2100  ydiff -= labelY;
2101  }
2102  else
2103  {
2104  double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
2105  if ( valiString.compare( QLatin1String( "Base" ), Qt::CaseInsensitive ) == 0 )
2106  {
2107  ydiff -= labelY * descentRatio;
2108  }
2109  else //'Cap' or 'Half'
2110  {
2111  double capHeightRatio = ( labelFontMetrics->boundingRect( 'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
2112  ydiff -= labelY * capHeightRatio;
2113  if ( valiString.compare( QLatin1String( "Half" ), Qt::CaseInsensitive ) == 0 )
2114  {
2115  ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
2116  }
2117  }
2118  }
2119  }
2120  }
2121 
2122  if ( dataDefinedRotation )
2123  {
2124  //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
2125  double xd = xdiff * cos( angle ) - ydiff * sin( angle );
2126  double yd = xdiff * sin( angle ) + ydiff * cos( angle );
2127  xdiff = xd;
2128  ydiff = yd;
2129  }
2130 
2131  //project xPos and yPos from layer to map CRS, handle rotation
2132  QgsGeometry ddPoint( new QgsPointV2( xPos, yPos ) );
2133  if ( QgsPalLabeling::geometryRequiresPreparation( ddPoint, context, ct ) )
2134  {
2135  ddPoint = QgsPalLabeling::prepareGeometry( ddPoint, context, ct );
2136  xPos = static_cast< QgsPointV2* >( ddPoint.geometry() )->x();
2137  yPos = static_cast< QgsPointV2* >( ddPoint.geometry() )->y();
2138  }
2139 
2140  xPos += xdiff;
2141  yPos += ydiff;
2142  }
2143  else
2144  {
2145  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2146  if ( dataDefinedRotation && placement != QgsPalLayerSettings::OverPoint )
2147  {
2148  angle = 0.0;
2149  }
2150  }
2151  }
2152  }
2153 
2154  // data defined always show?
2155  bool alwaysShow = false;
2157  {
2158  alwaysShow = exprVal.toBool();
2159  }
2160 
2161  // set repeat distance
2162  // data defined repeat distance?
2163  double repeatDist = repeatDistance;
2165  {
2166  bool ok;
2167  double distD = exprVal.toDouble( &ok );
2168  if ( ok )
2169  {
2170  repeatDist = distD;
2171  }
2172  }
2173 
2174  // data defined label-repeat distance units?
2175  bool repeatdistinmapunit = repeatDistanceUnit == QgsPalLayerSettings::MapUnits;
2177  {
2178  QString units = exprVal.toString().trimmed();
2179  QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 );
2180  if ( !units.isEmpty() )
2181  {
2182  repeatdistinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2183  }
2184  }
2185 
2186  if ( !qgsDoubleNear( repeatDist, 0.0 ) )
2187  {
2188  if ( !repeatdistinmapunit )
2189  {
2190  repeatDist *= mapUntsPerMM; //convert repeat distance from mm to map units
2191  }
2192  }
2193 
2194  // feature to the layer
2195  QgsTextLabelFeature* lf = new QgsTextLabelFeature( f.id(), geos_geom_clone, QSizeF( labelX, labelY ) );
2196  mFeatsRegPal++;
2197 
2198  *labelFeature = lf;
2199  ( *labelFeature )->setHasFixedPosition( dataDefinedPosition );
2200  ( *labelFeature )->setFixedPosition( QgsPoint( xPos, yPos ) );
2201  // use layer-level defined rotation, but not if position fixed
2202  ( *labelFeature )->setHasFixedAngle( dataDefinedRotation || ( !dataDefinedPosition && !qgsDoubleNear( angle, 0.0 ) ) );
2203  ( *labelFeature )->setFixedAngle( angle );
2204  ( *labelFeature )->setQuadOffset( QPointF( quadOffsetX, quadOffsetY ) );
2205  ( *labelFeature )->setPositionOffset( QgsPoint( offsetX, offsetY ) );
2206  ( *labelFeature )->setOffsetType( offsetType );
2207  ( *labelFeature )->setAlwaysShow( alwaysShow );
2208  ( *labelFeature )->setRepeatDistance( repeatDist );
2209  ( *labelFeature )->setLabelText( labelText );
2210  ( *labelFeature )->setPermissibleZone( permissibleZone );
2211  if ( geosObstacleGeomClone )
2212  {
2213  ( *labelFeature )->setObstacleGeometry( geosObstacleGeomClone );
2214 
2215  if ( geom.type() == QgsWkbTypes::PointGeometry )
2216  {
2217  //register symbol size
2218  ( *labelFeature )->setSymbolSize( QSizeF( obstacleGeometry->boundingBox().width(),
2219  obstacleGeometry->boundingBox().height() ) );
2220  }
2221  }
2222 
2223  //set label's visual margin so that top visual margin is the leading, and bottom margin is the font's descent
2224  //this makes labels align to the font's baseline or highest character
2225  double topMargin = qMax( 0.25 * labelFontMetrics->ascent(), 0.0 );
2226  double bottomMargin = 1.0 + labelFontMetrics->descent();
2227  QgsLabelFeature::VisualMargin vm( topMargin, 0.0, bottomMargin, 0.0 );
2228  vm *= xform->mapUnitsPerPixel() / context.rasterScaleFactor();
2229  ( *labelFeature )->setVisualMargin( vm );
2230 
2231  // store the label's calculated font for later use during painting
2232  QgsDebugMsgLevel( QString( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString(), labelFont.styleName() ), 4 );
2233  lf->setDefinedFont( labelFont );
2234 
2235  // TODO: only for placement which needs character info
2236  // account for any data defined font metrics adjustments
2238  labelFontMetrics.data(), xform, context.rasterScaleFactor(), maxcharanglein, maxcharangleout );
2239  // for labelFeature the LabelInfo is passed to feat when it is registered
2240 
2241  // TODO: allow layer-wide feature dist in PAL...?
2242 
2243  // data defined label-feature distance?
2244  double distance = dist;
2246  {
2247  bool ok;
2248  double distD = exprVal.toDouble( &ok );
2249  if ( ok )
2250  {
2251  distance = distD;
2252  }
2253  }
2254 
2255  // data defined label-feature distance units?
2256  bool distinmapunit = distInMapUnits;
2258  {
2259  QString units = exprVal.toString().trimmed();
2260  QgsDebugMsgLevel( QString( "exprVal DistanceUnits:%1" ).arg( units ), 4 );
2261  if ( !units.isEmpty() )
2262  {
2263  distinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2264  }
2265  }
2266 
2267  if ( distinmapunit ) //convert distance from mm/map units to pixels
2268  {
2269  distance /= distMapUnitScale.computeMapUnitsPerPixel( context );
2270  }
2271  else //mm
2272  {
2273  distance *= context.scaleFactor();
2274  }
2275 
2276  // when using certain placement modes, we force a tiny minimum distance. This ensures that
2277  // candidates are created just offset from a border and avoids candidates being incorrectly flagged as colliding with neighbours
2279  {
2280  distance = qMax( distance, 1.0 );
2281  }
2282 
2283  if ( !qgsDoubleNear( distance, 0.0 ) )
2284  {
2285  double d = ptOne.distance( ptZero ) * distance;
2286  ( *labelFeature )->setDistLabel( d );
2287  }
2288 
2289  if ( ddFixedQuad )
2290  {
2291  ( *labelFeature )->setHasFixedQuadrant( true );
2292  }
2293 
2294  // data defined z-index?
2295  double z = zIndex;
2297  {
2298  bool ok;
2299  double zIndexD = exprVal.toDouble( &ok );
2300  if ( ok )
2301  {
2302  z = zIndexD;
2303  }
2304  }
2305  ( *labelFeature )->setZIndex( z );
2306 
2307  // data defined priority?
2309  {
2310  bool ok;
2311  double priorityD = exprVal.toDouble( &ok );
2312  if ( ok )
2313  {
2314  priorityD = qBound( 0.0, priorityD, 10.0 );
2315  priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
2316  ( *labelFeature )->setPriority( priorityD );
2317  }
2318  }
2319 
2320  ( *labelFeature )->setIsObstacle( isObstacle );
2321 
2322  double featObstacleFactor = obstacleFactor;
2324  {
2325  bool ok;
2326  double factorD = exprVal.toDouble( &ok );
2327  if ( ok )
2328  {
2329  factorD = qBound( 0.0, factorD, 10.0 );
2330  factorD = factorD / 5.0 + 0.0001; // convert 0 -> 10 to 0.0001 -> 2.0
2331  featObstacleFactor = factorD;
2332  }
2333  }
2334  ( *labelFeature )->setObstacleFactor( featObstacleFactor );
2335 
2336  QVector< QgsPalLayerSettings::PredefinedPointPosition > positionOrder = predefinedPositionOrder;
2337  if ( positionOrder.isEmpty() )
2338  positionOrder = QgsPalLayerSettings::DEFAULT_PLACEMENT_ORDER;
2339 
2341  {
2342  QString orderD = exprVal.toString();
2343  positionOrder = QgsLabelingUtils::decodePredefinedPositionOrder( orderD );
2344  }
2345  ( *labelFeature )->setPredefinedPositionOrder( positionOrder );
2346 
2347  // add parameters for data defined labeling to label feature
2348  lf->setDataDefinedValues( dataDefinedValues );
2349 }
2350 
2351 void QgsPalLayerSettings::registerObstacleFeature( QgsFeature& f, QgsRenderContext &context, QgsLabelFeature** obstacleFeature, QgsGeometry* obstacleGeometry )
2352 {
2353  mCurFeat = &f;
2354 
2355  QgsGeometry geom;
2356  if ( obstacleGeometry )
2357  {
2358  geom = *obstacleGeometry;
2359  }
2360  else
2361  {
2362  geom = f.geometry();
2363  }
2364 
2365  if ( geom.isEmpty() )
2366  {
2367  return;
2368  }
2369 
2370  // simplify?
2371  const QgsVectorSimplifyMethod &simplifyMethod = context.vectorSimplifyMethod();
2372  QScopedPointer<QgsGeometry> scopedClonedGeom;
2373  if ( simplifyMethod.simplifyHints() != QgsVectorSimplifyMethod::NoSimplification && simplifyMethod.forceLocalOptimization() )
2374  {
2375  int simplifyHints = simplifyMethod.simplifyHints() | QgsMapToPixelSimplifier::SimplifyEnvelope;
2377  QgsMapToPixelSimplifier simplifier( simplifyHints, simplifyMethod.tolerance(), simplifyAlgorithm );
2378  geom = simplifier.simplify( geom );
2379  }
2380 
2381  GEOSGeometry* geos_geom_clone = nullptr;
2382  QScopedPointer<QgsGeometry> scopedPreparedGeom;
2383 
2384  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, &extentGeom ) )
2385  {
2386  geom = QgsPalLabeling::prepareGeometry( geom, context, ct, &extentGeom );
2387  }
2388  geos_geom_clone = geom.exportToGeos();
2389 
2390  if ( !geos_geom_clone )
2391  return; // invalid geometry
2392 
2393  // feature to the layer
2394  *obstacleFeature = new QgsLabelFeature( f.id(), geos_geom_clone, QSizeF( 0, 0 ) );
2395  ( *obstacleFeature )->setIsObstacle( true );
2396  mFeatsRegPal++;
2397 }
2398 
2399 bool QgsPalLayerSettings::dataDefinedValEval( DataDefinedValueType valType,
2401  QVariant& exprVal, QgsExpressionContext& context, const QVariant& originalValue )
2402 {
2403  if ( dataDefinedEvaluate( p, exprVal, &context, originalValue ) )
2404  {
2405 #ifdef QGISDEBUG
2406  QString dbgStr = QStringLiteral( "exprVal %1:" ).arg( mDataDefinedNames.value( p ).first ) + "%1"; // clazy:exclude=unused-non-trivial-variable
2407 #endif
2408 
2409  switch ( valType )
2410  {
2411  case DDBool:
2412  {
2413  bool bol = exprVal.toBool();
2414  QgsDebugMsgLevel( dbgStr.arg( bol ? "true" : "false" ), 4 );
2415  dataDefinedValues.insert( p, QVariant( bol ) );
2416  return true;
2417  }
2418  case DDInt:
2419  {
2420  bool ok;
2421  int size = exprVal.toInt( &ok );
2422  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2423 
2424  if ( ok )
2425  {
2426  dataDefinedValues.insert( p, QVariant( size ) );
2427  return true;
2428  }
2429  return false;
2430  }
2431  case DDIntPos:
2432  {
2433  bool ok;
2434  int size = exprVal.toInt( &ok );
2435  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2436 
2437  if ( ok && size > 0 )
2438  {
2439  dataDefinedValues.insert( p, QVariant( size ) );
2440  return true;
2441  }
2442  return false;
2443  }
2444  case DDDouble:
2445  {
2446  bool ok;
2447  double size = exprVal.toDouble( &ok );
2448  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2449 
2450  if ( ok )
2451  {
2452  dataDefinedValues.insert( p, QVariant( size ) );
2453  return true;
2454  }
2455  return false;
2456  }
2457  case DDDoublePos:
2458  {
2459  bool ok;
2460  double size = exprVal.toDouble( &ok );
2461  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2462 
2463  if ( ok && size > 0.0 )
2464  {
2465  dataDefinedValues.insert( p, QVariant( size ) );
2466  return true;
2467  }
2468  return false;
2469  }
2470  case DDRotation180:
2471  {
2472  bool ok;
2473  double rot = exprVal.toDouble( &ok );
2474  QgsDebugMsgLevel( dbgStr.arg( rot ), 4 );
2475  if ( ok )
2476  {
2477  if ( rot < -180.0 && rot >= -360 )
2478  {
2479  rot += 360;
2480  }
2481  if ( rot > 180.0 && rot <= 360 )
2482  {
2483  rot -= 360;
2484  }
2485  if ( rot >= -180 && rot <= 180 )
2486  {
2487  dataDefinedValues.insert( p, QVariant( rot ) );
2488  return true;
2489  }
2490  }
2491  return false;
2492  }
2493  case DDTransparency:
2494  {
2495  bool ok;
2496  int size = exprVal.toInt( &ok );
2497  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2498  if ( ok && size >= 0 && size <= 100 )
2499  {
2500  dataDefinedValues.insert( p, QVariant( size ) );
2501  return true;
2502  }
2503  return false;
2504  }
2505  case DDString:
2506  {
2507  QString str = exprVal.toString(); // don't trim whitespace
2508  QgsDebugMsgLevel( dbgStr.arg( str ), 4 );
2509 
2510  dataDefinedValues.insert( p, QVariant( str ) ); // let it stay empty if it is
2511  return true;
2512  }
2513  case DDUnits:
2514  {
2515  QString unitstr = exprVal.toString().trimmed();
2516  QgsDebugMsgLevel( dbgStr.arg( unitstr ), 4 );
2517 
2518  if ( !unitstr.isEmpty() )
2519  {
2520  dataDefinedValues.insert( p, QVariant( static_cast< int >( QgsUnitTypes::decodeRenderUnit( unitstr ) ) ) );
2521  return true;
2522  }
2523  return false;
2524  }
2525  case DDColor:
2526  {
2527  QString colorstr = exprVal.toString().trimmed();
2528  QgsDebugMsgLevel( dbgStr.arg( colorstr ), 4 );
2529  QColor color = QgsSymbolLayerUtils::decodeColor( colorstr );
2530 
2531  if ( color.isValid() )
2532  {
2533  dataDefinedValues.insert( p, QVariant( color ) );
2534  return true;
2535  }
2536  return false;
2537  }
2538  case DDJoinStyle:
2539  {
2540  QString joinstr = exprVal.toString().trimmed();
2541  QgsDebugMsgLevel( dbgStr.arg( joinstr ), 4 );
2542 
2543  if ( !joinstr.isEmpty() )
2544  {
2545  dataDefinedValues.insert( p, QVariant( static_cast< int >( _decodePenJoinStyle( joinstr ) ) ) );
2546  return true;
2547  }
2548  return false;
2549  }
2550  case DDBlendMode:
2551  {
2552  QString blendstr = exprVal.toString().trimmed();
2553  QgsDebugMsgLevel( dbgStr.arg( blendstr ), 4 );
2554 
2555  if ( !blendstr.isEmpty() )
2556  {
2557  dataDefinedValues.insert( p, QVariant( static_cast< int >( QgsSymbolLayerUtils::decodeBlendMode( blendstr ) ) ) );
2558  return true;
2559  }
2560  return false;
2561  }
2562  case DDPointF:
2563  {
2564  QString ptstr = exprVal.toString().trimmed();
2565  QgsDebugMsgLevel( dbgStr.arg( ptstr ), 4 );
2566 
2567  if ( !ptstr.isEmpty() )
2568  {
2569  dataDefinedValues.insert( p, QVariant( QgsSymbolLayerUtils::decodePoint( ptstr ) ) );
2570  return true;
2571  }
2572  return false;
2573  }
2574  }
2575  }
2576  return false;
2577 }
2578 
2579 void QgsPalLayerSettings::parseTextStyle( QFont& labelFont,
2580  QgsUnitTypes::RenderUnit fontunits,
2581  QgsRenderContext &context )
2582 {
2583  // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
2584 
2585  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2586 
2587  // Two ways to generate new data defined font:
2588  // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
2589  // 2) Family + named style (bold or italic is ignored)
2590 
2591  // data defined font family?
2592  QString ddFontFamily( QLatin1String( "" ) );
2593  if ( dataDefinedEvaluate( QgsPalLayerSettings::Family, exprVal, &context.expressionContext(), labelFont.family() ) )
2594  {
2595  QString family = exprVal.toString().trimmed();
2596  QgsDebugMsgLevel( QString( "exprVal Font family:%1" ).arg( family ), 4 );
2597 
2598  if ( labelFont.family() != family )
2599  {
2600  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
2601  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
2602  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
2603  {
2604  ddFontFamily = family;
2605  }
2606  }
2607  }
2608 
2609  // data defined named font style?
2610  QString ddFontStyle( QLatin1String( "" ) );
2612  {
2613  QString fontstyle = exprVal.toString().trimmed();
2614  QgsDebugMsgLevel( QString( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
2615  ddFontStyle = fontstyle;
2616  }
2617 
2618  // data defined bold font style?
2619  bool ddBold = false;
2620  if ( dataDefinedEvaluate( QgsPalLayerSettings::Bold, exprVal, &context.expressionContext(), labelFont.bold() ) )
2621  {
2622  bool bold = exprVal.toBool();
2623  QgsDebugMsgLevel( QString( "exprVal Font bold:%1" ).arg( bold ? "true" : "false" ), 4 );
2624  ddBold = bold;
2625  }
2626 
2627  // data defined italic font style?
2628  bool ddItalic = false;
2629  if ( dataDefinedEvaluate( QgsPalLayerSettings::Italic, exprVal, &context.expressionContext(), labelFont.italic() ) )
2630  {
2631  bool italic = exprVal.toBool();
2632  QgsDebugMsgLevel( QString( "exprVal Font italic:%1" ).arg( italic ? "true" : "false" ), 4 );
2633  ddItalic = italic;
2634  }
2635 
2636  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
2637  // (currently defaults to what has been read in from layer settings)
2638  QFont newFont;
2639  QFont appFont = QApplication::font();
2640  bool newFontBuilt = false;
2641  if ( ddBold || ddItalic )
2642  {
2643  // new font needs built, since existing style needs removed
2644  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
2645  newFontBuilt = true;
2646  newFont.setBold( ddBold );
2647  newFont.setItalic( ddItalic );
2648  }
2649  else if ( !ddFontStyle.isEmpty()
2650  && ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
2651  {
2652  if ( !ddFontFamily.isEmpty() )
2653  {
2654  // both family and style are different, build font from database
2655  QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
2656  if ( appFont != styledfont )
2657  {
2658  newFont = styledfont;
2659  newFontBuilt = true;
2660  }
2661  }
2662 
2663  // update the font face style
2664  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
2665  }
2666  else if ( !ddFontFamily.isEmpty() )
2667  {
2668  if ( ddFontStyle.compare( QLatin1String( "Ignore" ), Qt::CaseInsensitive ) != 0 )
2669  {
2670  // just family is different, build font from database
2671  QFont styledfont = mFontDB.font( ddFontFamily, mFormat.namedStyle(), appFont.pointSize() );
2672  if ( appFont != styledfont )
2673  {
2674  newFont = styledfont;
2675  newFontBuilt = true;
2676  }
2677  }
2678  else
2679  {
2680  newFont = QFont( ddFontFamily );
2681  newFontBuilt = true;
2682  }
2683  }
2684 
2685  if ( newFontBuilt )
2686  {
2687  // copy over existing font settings
2688  //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
2689  newFont.setPixelSize( labelFont.pixelSize() );
2690  newFont.setUnderline( labelFont.underline() );
2691  newFont.setStrikeOut( labelFont.strikeOut() );
2692  newFont.setWordSpacing( labelFont.wordSpacing() );
2693  newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
2694 
2695  labelFont = newFont;
2696  }
2697 
2698  // data defined word spacing?
2699  double wordspace = labelFont.wordSpacing();
2700  if ( dataDefinedEvaluate( QgsPalLayerSettings::FontWordSpacing, exprVal, &context.expressionContext(), wordspace ) )
2701  {
2702  bool ok;
2703  double wspacing = exprVal.toDouble( &ok );
2704  QgsDebugMsgLevel( QString( "exprVal FontWordSpacing:%1" ).arg( wspacing ), 4 );
2705  if ( ok )
2706  {
2707  wordspace = wspacing;
2708  }
2709  }
2710  labelFont.setWordSpacing( QgsTextRenderer::scaleToPixelContext( wordspace, context, fontunits, false, mFormat.sizeMapUnitScale() ) );
2711 
2712  // data defined letter spacing?
2713  double letterspace = labelFont.letterSpacing();
2714  if ( dataDefinedEvaluate( QgsPalLayerSettings::FontLetterSpacing, exprVal, &context.expressionContext(), letterspace ) )
2715  {
2716  bool ok;
2717  double lspacing = exprVal.toDouble( &ok );
2718  QgsDebugMsgLevel( QString( "exprVal FontLetterSpacing:%1" ).arg( lspacing ), 4 );
2719  if ( ok )
2720  {
2721  letterspace = lspacing;
2722  }
2723  }
2724  labelFont.setLetterSpacing( QFont::AbsoluteSpacing, QgsTextRenderer::scaleToPixelContext( letterspace, context, fontunits, false, mFormat.sizeMapUnitScale() ) );
2725 
2726  // data defined strikeout font style?
2727  if ( dataDefinedEvaluate( QgsPalLayerSettings::Strikeout, exprVal, &context.expressionContext(), labelFont.strikeOut() ) )
2728  {
2729  bool strikeout = exprVal.toBool();
2730  QgsDebugMsgLevel( QString( "exprVal Font strikeout:%1" ).arg( strikeout ? "true" : "false" ), 4 );
2731  labelFont.setStrikeOut( strikeout );
2732  }
2733 
2734  // data defined underline font style?
2735  if ( dataDefinedEvaluate( QgsPalLayerSettings::Underline, exprVal, &context.expressionContext(), labelFont.underline() ) )
2736  {
2737  bool underline = exprVal.toBool();
2738  QgsDebugMsgLevel( QString( "exprVal Font underline:%1" ).arg( underline ? "true" : "false" ), 4 );
2739  labelFont.setUnderline( underline );
2740  }
2741 
2742  // pass the rest on to QgsPalLabeling::drawLabeling
2743 
2744  // data defined font color?
2745  dataDefinedValEval( DDColor, QgsPalLayerSettings::Color, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( mFormat.color() ) );
2746 
2747  // data defined font transparency?
2748  dataDefinedValEval( DDTransparency, QgsPalLayerSettings::FontTransp, exprVal, context.expressionContext(), 100 - mFormat.opacity() * 100 );
2749 
2750  // data defined font blend mode?
2751  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::FontBlendMode, exprVal, context.expressionContext() );
2752 
2753 }
2754 
2755 void QgsPalLayerSettings::parseTextBuffer( QgsRenderContext &context )
2756 {
2757  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2758 
2759  QgsTextBufferSettings buffer = mFormat.buffer();
2760 
2761  // data defined draw buffer?
2762  bool drawBuffer = mFormat.buffer().enabled();
2763  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::BufferDraw, exprVal, context.expressionContext(), buffer.enabled() ) )
2764  {
2765  drawBuffer = exprVal.toBool();
2766  }
2767 
2768  if ( !drawBuffer )
2769  {
2770  return;
2771  }
2772 
2773  // data defined buffer size?
2774  double bufrSize = buffer.size();
2775  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::BufferSize, exprVal, context.expressionContext(), buffer.size() ) )
2776  {
2777  bufrSize = exprVal.toDouble();
2778  }
2779 
2780  // data defined buffer transparency?
2781  int bufTransp = 100 - buffer.opacity() * 100;
2782  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::BufferTransp, exprVal, context.expressionContext(), bufTransp ) )
2783  {
2784  bufTransp = exprVal.toInt();
2785  }
2786 
2787  drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufTransp < 100 );
2788 
2789  if ( !drawBuffer )
2790  {
2791  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
2792  dataDefinedValues.remove( QgsPalLayerSettings::BufferSize );
2793  dataDefinedValues.remove( QgsPalLayerSettings::BufferTransp );
2794  return; // don't bother evaluating values that won't be used
2795  }
2796 
2797  // data defined buffer units?
2798  dataDefinedValEval( DDUnits, QgsPalLayerSettings::BufferUnit, exprVal, context.expressionContext() );
2799 
2800  // data defined buffer color?
2801  dataDefinedValEval( DDColor, QgsPalLayerSettings::BufferColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( buffer.color() ) );
2802 
2803  // data defined buffer pen join style?
2804  dataDefinedValEval( DDJoinStyle, QgsPalLayerSettings::BufferJoinStyle, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodePenJoinStyle( buffer.joinStyle() ) );
2805 
2806  // data defined buffer blend mode?
2807  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::BufferBlendMode, exprVal, context.expressionContext() );
2808 }
2809 
2810 void QgsPalLayerSettings::parseTextFormatting( QgsRenderContext &context )
2811 {
2812  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2813 
2814  // data defined multiline wrap character?
2815  QString wrapchr = wrapChar;
2816  if ( dataDefinedValEval( DDString, QgsPalLayerSettings::MultiLineWrapChar, exprVal, context.expressionContext(), wrapChar ) )
2817  {
2818  wrapchr = exprVal.toString();
2819  }
2820 
2821  // data defined multiline height?
2822  dataDefinedValEval( DDDouble, QgsPalLayerSettings::MultiLineHeight, exprVal, context.expressionContext() );
2823 
2824  // data defined multiline text align?
2826  {
2827  QString str = exprVal.toString().trimmed();
2828  QgsDebugMsgLevel( QString( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
2829 
2830  if ( !str.isEmpty() )
2831  {
2832  // "Left"
2834 
2835  if ( str.compare( QLatin1String( "Center" ), Qt::CaseInsensitive ) == 0 )
2836  {
2838  }
2839  else if ( str.compare( QLatin1String( "Right" ), Qt::CaseInsensitive ) == 0 )
2840  {
2841  aligntype = QgsPalLayerSettings::MultiRight;
2842  }
2843  else if ( str.compare( QLatin1String( "Follow" ), Qt::CaseInsensitive ) == 0 )
2844  {
2846  }
2847  dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant( static_cast< int >( aligntype ) ) );
2848  }
2849  }
2850 
2851  // data defined direction symbol?
2852  bool drawDirSymb = addDirectionSymbol;
2853  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbDraw, exprVal, context.expressionContext(), addDirectionSymbol ) )
2854  {
2855  drawDirSymb = exprVal.toBool();
2856  }
2857 
2858  if ( drawDirSymb )
2859  {
2860  // data defined direction left symbol?
2861  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbLeft, exprVal, context.expressionContext(), leftDirectionSymbol );
2862 
2863  // data defined direction right symbol?
2864  dataDefinedValEval( DDString, QgsPalLayerSettings::DirSymbRight, exprVal, context.expressionContext(), rightDirectionSymbol );
2865 
2866  // data defined direction symbol placement?
2868  {
2869  QString str = exprVal.toString().trimmed();
2870  QgsDebugMsgLevel( QString( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
2871 
2872  if ( !str.isEmpty() )
2873  {
2874  // "LeftRight"
2876 
2877  if ( str.compare( QLatin1String( "Above" ), Qt::CaseInsensitive ) == 0 )
2878  {
2880  }
2881  else if ( str.compare( QLatin1String( "Below" ), Qt::CaseInsensitive ) == 0 )
2882  {
2884  }
2885  dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant( static_cast< int >( placetype ) ) );
2886  }
2887  }
2888 
2889  // data defined direction symbol reversed?
2890  dataDefinedValEval( DDBool, QgsPalLayerSettings::DirSymbReverse, exprVal, context.expressionContext(), reverseDirectionSymbol );
2891  }
2892 
2893  // formatting for numbers is inline with generation of base label text and not passed to label painting
2894 }
2895 
2896 void QgsPalLayerSettings::parseShapeBackground( QgsRenderContext &context )
2897 {
2898  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2899 
2900  QgsTextBackgroundSettings background = mFormat.background();
2901 
2902  // data defined draw shape?
2903  bool drawShape = background.enabled();
2904  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShapeDraw, exprVal, context.expressionContext(), drawShape ) )
2905  {
2906  drawShape = exprVal.toBool();
2907  }
2908 
2909  if ( !drawShape )
2910  {
2911  return;
2912  }
2913 
2914  // data defined shape transparency?
2915  int shapeTransp = 100 - background.opacity() * 100;
2916  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::ShapeTransparency, exprVal, context.expressionContext(), shapeTransp ) )
2917  {
2918  shapeTransp = exprVal.toInt();
2919  }
2920 
2921  drawShape = ( drawShape && shapeTransp < 100 ); // size is not taken into account (could be)
2922 
2923  if ( !drawShape )
2924  {
2925  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
2926  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
2927  return; // don't bother evaluating values that won't be used
2928  }
2929 
2930  // data defined shape kind?
2931  QgsTextBackgroundSettings::ShapeType shapeKind = background.type();
2933  {
2934  QString skind = exprVal.toString().trimmed();
2935  QgsDebugMsgLevel( QString( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
2936 
2937  if ( !skind.isEmpty() )
2938  {
2939  // "Rectangle"
2941 
2942  if ( skind.compare( QLatin1String( "Square" ), Qt::CaseInsensitive ) == 0 )
2943  {
2945  }
2946  else if ( skind.compare( QLatin1String( "Ellipse" ), Qt::CaseInsensitive ) == 0 )
2947  {
2949  }
2950  else if ( skind.compare( QLatin1String( "Circle" ), Qt::CaseInsensitive ) == 0 )
2951  {
2953  }
2954  else if ( skind.compare( QLatin1String( "SVG" ), Qt::CaseInsensitive ) == 0 )
2955  {
2957  }
2958  shapeKind = shpkind;
2959  dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant( static_cast< int >( shpkind ) ) );
2960  }
2961  }
2962 
2963  // data defined shape SVG path?
2964  QString svgPath = background.svgFile();
2965  if ( dataDefinedEvaluate( QgsPalLayerSettings::ShapeSVGFile, exprVal, &context.expressionContext(), svgPath ) )
2966  {
2967  QString svgfile = exprVal.toString().trimmed();
2968  QgsDebugMsgLevel( QString( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
2969 
2970  // '' empty paths are allowed
2971  svgPath = svgfile;
2972  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgfile ) );
2973  }
2974 
2975  // data defined shape size type?
2976  QgsTextBackgroundSettings::SizeType shpSizeType = background.sizeType();
2978  {
2979  QString stype = exprVal.toString().trimmed();
2980  QgsDebugMsgLevel( QString( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
2981 
2982  if ( !stype.isEmpty() )
2983  {
2984  // "Buffer"
2986 
2987  if ( stype.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
2988  {
2990  }
2991  shpSizeType = sizType;
2992  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant( static_cast< int >( sizType ) ) );
2993  }
2994  }
2995 
2996  // data defined shape size X? (SVGs only use X for sizing)
2997  double ddShpSizeX = background.size().width();
2998  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeX, exprVal, context.expressionContext(), ddShpSizeX ) )
2999  {
3000  ddShpSizeX = exprVal.toDouble();
3001  }
3002 
3003  // data defined shape size Y?
3004  double ddShpSizeY = background.size().height();
3005  if ( dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShapeSizeY, exprVal, context.expressionContext(), ddShpSizeY ) )
3006  {
3007  ddShpSizeY = exprVal.toDouble();
3008  }
3009 
3010  // don't continue under certain circumstances (e.g. size is fixed)
3011  bool skip = false;
3012  if ( shapeKind == QgsTextBackgroundSettings::ShapeSVG
3013  && ( svgPath.isEmpty()
3014  || ( !svgPath.isEmpty()
3015  && shpSizeType == QgsTextBackgroundSettings::SizeFixed
3016  && ddShpSizeX == 0.0 ) ) )
3017  {
3018  skip = true;
3019  }
3020  if ( shapeKind != QgsTextBackgroundSettings::ShapeSVG
3021  && shpSizeType == QgsTextBackgroundSettings::SizeFixed
3022  && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
3023  {
3024  skip = true;
3025  }
3026 
3027  if ( skip )
3028  {
3029  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
3030  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
3031  dataDefinedValues.remove( QgsPalLayerSettings::ShapeKind );
3032  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSVGFile );
3033  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeX );
3034  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeY );
3035  return; // don't bother evaluating values that won't be used
3036  }
3037 
3038  // data defined shape size units?
3039  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeSizeUnits, exprVal, context.expressionContext() );
3040 
3041  // data defined shape rotation type?
3043  {
3044  QString rotstr = exprVal.toString().trimmed();
3045  QgsDebugMsgLevel( QString( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
3046 
3047  if ( !rotstr.isEmpty() )
3048  {
3049  // "Sync"
3051 
3052  if ( rotstr.compare( QLatin1String( "Offset" ), Qt::CaseInsensitive ) == 0 )
3053  {
3055  }
3056  else if ( rotstr.compare( QLatin1String( "Fixed" ), Qt::CaseInsensitive ) == 0 )
3057  {
3059  }
3060  dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant( static_cast< int >( rottype ) ) );
3061  }
3062  }
3063 
3064  // data defined shape rotation?
3065  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShapeRotation, exprVal, context.expressionContext(), background.rotation() );
3066 
3067  // data defined shape offset?
3068  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeOffset, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodePoint( background.offset() ) );
3069 
3070  // data defined shape offset units?
3071  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeOffsetUnits, exprVal, context.expressionContext() );
3072 
3073  // data defined shape radii?
3074  dataDefinedValEval( DDPointF, QgsPalLayerSettings::ShapeRadii, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeSize( background.radii() ) );
3075 
3076  // data defined shape radii units?
3077  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeRadiiUnits, exprVal, context.expressionContext() );
3078 
3079  // data defined shape blend mode?
3080  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShapeBlendMode, exprVal, context.expressionContext() );
3081 
3082  // data defined shape fill color?
3083  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShapeFillColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( background.fillColor() ) );
3084 
3085  // data defined shape border color?
3086  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShapeBorderColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( background.borderColor() ) );
3087 
3088  // data defined shape border width?
3089  dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShapeBorderWidth, exprVal, context.expressionContext(), background.borderWidth() );
3090 
3091  // data defined shape border width units?
3092  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShapeBorderWidthUnits, exprVal, context.expressionContext() );
3093 
3094  // data defined shape join style?
3095  dataDefinedValEval( DDJoinStyle, QgsPalLayerSettings::ShapeJoinStyle, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodePenJoinStyle( background.joinStyle() ) );
3096 
3097 }
3098 
3099 void QgsPalLayerSettings::parseDropShadow( QgsRenderContext &context )
3100 {
3101  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3102 
3103  QgsTextShadowSettings shadow = mFormat.shadow();
3104 
3105  // data defined draw shadow?
3106  bool drawShadow = shadow.enabled();
3107  if ( dataDefinedValEval( DDBool, QgsPalLayerSettings::ShadowDraw, exprVal, context.expressionContext(), drawShadow ) )
3108  {
3109  drawShadow = exprVal.toBool();
3110  }
3111 
3112  if ( !drawShadow )
3113  {
3114  return;
3115  }
3116 
3117  // data defined shadow transparency?
3118  int shadowTransp = 100 - shadow.opacity() * 100;
3119  if ( dataDefinedValEval( DDTransparency, QgsPalLayerSettings::ShadowTransparency, exprVal, context.expressionContext(), shadowTransp ) )
3120  {
3121  shadowTransp = exprVal.toInt();
3122  }
3123 
3124  // data defined shadow offset distance?
3125  double shadowOffDist = shadow.offsetDistance();
3126  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowOffsetDist, exprVal, context.expressionContext(), shadowOffDist ) )
3127  {
3128  shadowOffDist = exprVal.toDouble();
3129  }
3130 
3131  // data defined shadow offset distance?
3132  double shadowRad = shadow.blurRadius();
3133  if ( dataDefinedValEval( DDDoublePos, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadowRad ) )
3134  {
3135  shadowRad = exprVal.toDouble();
3136  }
3137 
3138  drawShadow = ( drawShadow && shadowTransp < 100 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
3139 
3140  if ( !drawShadow )
3141  {
3142  dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
3143  dataDefinedValues.remove( QgsPalLayerSettings::ShadowTransparency );
3144  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOffsetDist );
3145  dataDefinedValues.remove( QgsPalLayerSettings::ShadowRadius );
3146  return; // don't bother evaluating values that won't be used
3147  }
3148 
3149  // data defined shadow under type?
3151  {
3152  QString str = exprVal.toString().trimmed();
3153  QgsDebugMsgLevel( QString( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
3154 
3155  if ( !str.isEmpty() )
3156  {
3157  // "Lowest"
3159 
3160  if ( str.compare( QLatin1String( "Text" ), Qt::CaseInsensitive ) == 0 )
3161  {
3163  }
3164  else if ( str.compare( QLatin1String( "Buffer" ), Qt::CaseInsensitive ) == 0 )
3165  {
3167  }
3168  else if ( str.compare( QLatin1String( "Background" ), Qt::CaseInsensitive ) == 0 )
3169  {
3171  }
3172  dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant( static_cast< int >( shdwtype ) ) );
3173  }
3174  }
3175 
3176  // data defined shadow offset angle?
3177  dataDefinedValEval( DDRotation180, QgsPalLayerSettings::ShadowOffsetAngle, exprVal, context.expressionContext(), shadow.offsetAngle() );
3178 
3179  // data defined shadow offset units?
3180  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowOffsetUnits, exprVal, context.expressionContext() );
3181 
3182  // data defined shadow radius?
3183  dataDefinedValEval( DDDouble, QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext(), shadow.blurRadius() );
3184 
3185  // data defined shadow radius units?
3186  dataDefinedValEval( DDUnits, QgsPalLayerSettings::ShadowRadiusUnits, exprVal, context.expressionContext() );
3187 
3188  // data defined shadow scale? ( gui bounds to 0-2000, no upper bound here )
3189  dataDefinedValEval( DDIntPos, QgsPalLayerSettings::ShadowScale, exprVal, context.expressionContext(), shadow.scale() );
3190 
3191  // data defined shadow color?
3192  dataDefinedValEval( DDColor, QgsPalLayerSettings::ShadowColor, exprVal, context.expressionContext(), QgsSymbolLayerUtils::encodeColor( shadow.color() ) );
3193 
3194  // data defined shadow blend mode?
3195  dataDefinedValEval( DDBlendMode, QgsPalLayerSettings::ShadowBlendMode, exprVal, context.expressionContext() );
3196 }
3197 
3198 // -------------
3199 
3201  : mEngine( new QgsLabelingEngine() )
3202 {
3203 }
3204 
3206 {
3207  delete mEngine;
3208  mEngine = nullptr;
3209 }
3210 
3212 {
3213  return staticWillUseLayer( layer );
3214 }
3215 
3216 bool QgsPalLabeling::staticWillUseLayer( const QString& layerID )
3217 {
3218  QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( layerID ) );
3219  if ( !layer )
3220  return false;
3221  return staticWillUseLayer( layer );
3222 }
3223 
3224 
3226 {
3227  // don't do QgsPalLayerSettings::readFromLayer( layer ) if not needed
3228  bool enabled = false;
3229  if ( layer->customProperty( QStringLiteral( "labeling" ) ).toString() == QLatin1String( "pal" ) )
3230  enabled = layer->labelsEnabled() || layer->diagramsEnabled();
3231  else if ( layer->labeling()->type() == QLatin1String( "rule-based" ) )
3232  return true;
3233 
3234  return enabled;
3235 }
3236 
3237 
3239 {
3240 }
3241 
3242 void QgsPalLabeling::clearActiveLayer( const QString &layerID )
3243 {
3244  Q_UNUSED( layerID );
3245 }
3246 
3247 
3248 int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QSet<QString>& attrNames, QgsRenderContext& ctx )
3249 {
3250  if ( !willUseLayer( layer ) )
3251  {
3252  return 0;
3253  }
3254 
3255  if ( !layer->labeling() )
3256  return 0;
3257 
3258  QgsVectorLayerLabelProvider* lp = layer->labeling()->provider( layer );
3259  if ( !lp )
3260  return 0;
3261 
3262  //QgsVectorLayerLabelProvider* lp = new QgsVectorLayerLabelProvider( layer, false );
3263  // need to be added before calling prepare() - uses map settings from engine
3264  mEngine->addProvider( lp );
3265  mLabelProviders[layer->id()] = lp; // fast lookup table by layer ID
3266 
3267  if ( !lp->prepare( ctx, attrNames ) )
3268  {
3269  mEngine->removeProvider( lp );
3270  return 0;
3271  }
3272 
3273  return 1; // init successful
3274 }
3275 
3276 int QgsPalLabeling::prepareDiagramLayer( QgsVectorLayer* layer, QSet<QString>& attrNames, QgsRenderContext& ctx )
3277 {
3279  // need to be added before calling prepare() - uses map settings from engine
3280  mEngine->addProvider( dp );
3281  mDiagramProviders[layer->id()] = dp; // fast lookup table by layer ID
3282 
3283  if ( !dp->prepare( ctx, attrNames ) )
3284  {
3285  mEngine->removeProvider( dp );
3286  return 0;
3287  }
3288 
3289  return 1;
3290 }
3291 
3292 void QgsPalLabeling::registerFeature( const QString& layerID, QgsFeature& f, QgsRenderContext &context )
3293 {
3294  if ( QgsVectorLayerLabelProvider* provider = mLabelProviders.value( layerID, nullptr ) )
3295  provider->registerFeature( f, context );
3296 }
3297 
3299 {
3300  if ( geometry.isEmpty() )
3301  {
3302  return false;
3303  }
3304 
3305  //requires reprojection
3306  if ( ct.isValid() && !ct.isShortCircuited() )
3307  return true;
3308 
3309  //requires rotation
3310  const QgsMapToPixel& m2p = context.mapToPixel();
3311  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
3312  return true;
3313 
3314  //requires clip
3315  if ( clipGeometry && !clipGeometry->boundingBox().contains( geometry.boundingBox() ) )
3316  return true;
3317 
3318  //requires fixing
3319  if ( geometry.type() == QgsWkbTypes::PolygonGeometry && !geometry.isGeosValid() )
3320  return true;
3321 
3322  return false;
3323 }
3324 
3325 QStringList QgsPalLabeling::splitToLines( const QString &text, const QString &wrapCharacter )
3326 {
3327  QStringList multiLineSplit;
3328  if ( !wrapCharacter.isEmpty() && wrapCharacter != QLatin1String( "\n" ) )
3329  {
3330  //wrap on both the wrapchr and new line characters
3331  Q_FOREACH ( const QString& line, text.split( wrapCharacter ) )
3332  {
3333  multiLineSplit.append( line.split( '\n' ) );
3334  }
3335  }
3336  else
3337  {
3338  multiLineSplit = text.split( '\n' );
3339  }
3340 
3341  return multiLineSplit;
3342 }
3343 
3344 QStringList QgsPalLabeling::splitToGraphemes( const QString &text )
3345 {
3346  QStringList graphemes;
3347  QTextBoundaryFinder boundaryFinder( QTextBoundaryFinder::Grapheme, text );
3348  int currentBoundary = -1;
3349  int previousBoundary = 0;
3350  while (( currentBoundary = boundaryFinder.toNextBoundary() ) > 0 )
3351  {
3352  graphemes << text.mid( previousBoundary, currentBoundary - previousBoundary );
3353  previousBoundary = currentBoundary;
3354  }
3355  return graphemes;
3356 }
3357 
3359 {
3360  if ( geometry.isEmpty() )
3361  {
3362  return QgsGeometry();
3363  }
3364 
3365  //don't modify the feature's geometry so that geometry based expressions keep working
3366  QgsGeometry geom = geometry;
3367 
3368  //reproject the geometry if necessary
3369  if ( ct.isValid() && !ct.isShortCircuited() )
3370  {
3371  try
3372  {
3373  geom.transform( ct );
3374  }
3375  catch ( QgsCsException &cse )
3376  {
3377  Q_UNUSED( cse );
3378  QgsDebugMsgLevel( QString( "Ignoring feature due to transformation exception" ), 4 );
3379  return QgsGeometry();
3380  }
3381  }
3382 
3383  // Rotate the geometry if needed, before clipping
3384  const QgsMapToPixel& m2p = context.mapToPixel();
3385  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
3386  {
3387  QgsPoint center = context.extent().center();
3388 
3389  if ( ct.isValid() && !ct.isShortCircuited() )
3390  {
3391  try
3392  {
3393  center = ct.transform( center );
3394  }
3395  catch ( QgsCsException &cse )
3396  {
3397  Q_UNUSED( cse );
3398  QgsDebugMsgLevel( QString( "Ignoring feature due to transformation exception" ), 4 );
3399  return QgsGeometry();
3400  }
3401  }
3402 
3403  if ( geom.rotate( m2p.mapRotation(), center ) )
3404  {
3405  QgsDebugMsg( QString( "Error rotating geometry" ).arg( geom.exportToWkt() ) );
3406  return QgsGeometry();
3407  }
3408  }
3409 
3410  // fix invalid polygons
3411  if ( geom.type() == QgsWkbTypes::PolygonGeometry && !geom.isGeosValid() )
3412  {
3413  QgsGeometry bufferGeom = geom.buffer( 0, 0 );
3414  if ( bufferGeom.isEmpty() )
3415  {
3416  return QgsGeometry();
3417  }
3418  geom = bufferGeom;
3419  }
3420 
3421  if ( clipGeometry &&
3422  (( qgsDoubleNear( m2p.mapRotation(), 0 ) && !clipGeometry->boundingBox().contains( geom.boundingBox() ) )
3423  || ( !qgsDoubleNear( m2p.mapRotation(), 0 ) && !clipGeometry->contains( geom ) ) ) )
3424  {
3425  QgsGeometry clipGeom = geom.intersection( *clipGeometry ); // creates new geometry
3426  if ( clipGeom.isEmpty() )
3427  {
3428  return QgsGeometry();
3429  }
3430  geom = clipGeom;
3431  }
3432 
3433  return geom;
3434 }
3435 
3436 bool QgsPalLabeling::checkMinimumSizeMM( const QgsRenderContext& context, const QgsGeometry* geom, double minSize )
3437 {
3438  if ( minSize <= 0 )
3439  {
3440  return true;
3441  }
3442 
3443  if ( !geom )
3444  {
3445  return false;
3446  }
3447 
3448  QgsWkbTypes::GeometryType featureType = geom->type();
3449  if ( featureType == QgsWkbTypes::PointGeometry ) //minimum size does not apply to point features
3450  {
3451  return true;
3452  }
3453 
3454  double mapUnitsPerMM = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
3455  if ( featureType == QgsWkbTypes::LineGeometry )
3456  {
3457  double length = geom->length();
3458  if ( length >= 0.0 )
3459  {
3460  return ( length >= ( minSize * mapUnitsPerMM ) );
3461  }
3462  }
3463  else if ( featureType == QgsWkbTypes::PolygonGeometry )
3464  {
3465  double area = geom->area();
3466  if ( area >= 0.0 )
3467  {
3468  return ( sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
3469  }
3470  }
3471  return true; //should never be reached. Return true in this case to label such geometries anyway.
3472 }
3473 
3474 void QgsPalLabeling::registerDiagramFeature( const QString& layerID, QgsFeature& feat, QgsRenderContext &context )
3475 {
3476  if ( QgsVectorLayerDiagramProvider* provider = mDiagramProviders.value( layerID, nullptr ) )
3477  provider->registerFeature( feat, context );
3478 }
3479 
3480 
3481 void QgsPalLabeling::init( const QgsMapSettings& mapSettings )
3482 {
3483  mEngine->setMapSettings( mapSettings );
3484 }
3485 
3487 {
3488  delete mEngine;
3489  mEngine = new QgsLabelingEngine();
3490 }
3491 
3493  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3494 {
3495  QgsTextFormat format = tmpLyr.format();
3496  bool changed = false;
3497 
3498  //font color
3499  if ( ddValues.contains( QgsPalLayerSettings::Color ) )
3500  {
3501  QVariant ddColor = ddValues.value( QgsPalLayerSettings::Color );
3502  format.setColor( ddColor.value<QColor>() );
3503  changed = true;
3504  }
3505 
3506  //font transparency
3507  if ( ddValues.contains( QgsPalLayerSettings::FontTransp ) )
3508  {
3509  format.setOpacity( 1.0 - ddValues.value( QgsPalLayerSettings::FontTransp ).toInt() / 100.0 );
3510  changed = true;
3511  }
3512 
3513  //font blend mode
3514  if ( ddValues.contains( QgsPalLayerSettings::FontBlendMode ) )
3515  {
3516  format.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::FontBlendMode ).toInt() ) );
3517  changed = true;
3518  }
3519 
3520  if ( changed )
3521  {
3522  tmpLyr.setFormat( format );
3523  }
3524 }
3525 
3527  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3528 {
3529  if ( ddValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
3530  {
3531  tmpLyr.wrapChar = ddValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
3532  }
3533 
3534  if ( !tmpLyr.wrapChar.isEmpty() || tmpLyr.getLabelExpression()->expression().contains( QLatin1String( "wordwrap" ) ) )
3535  {
3536 
3537  if ( ddValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
3538  {
3539  QgsTextFormat format = tmpLyr.format();
3540  format.setLineHeight( ddValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble() );
3541  tmpLyr.setFormat( format );
3542  }
3543 
3544  if ( ddValues.contains( QgsPalLayerSettings::MultiLineAlignment ) )
3545  {
3546  tmpLyr.multilineAlign = static_cast< QgsPalLayerSettings::MultiLineAlign >( ddValues.value( QgsPalLayerSettings::MultiLineAlignment ).toInt() );
3547  }
3548 
3549  }
3550 
3551  if ( ddValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
3552  {
3553  tmpLyr.addDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
3554  }
3555 
3556  if ( tmpLyr.addDirectionSymbol )
3557  {
3558 
3559  if ( ddValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
3560  {
3561  tmpLyr.leftDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
3562  }
3563  if ( ddValues.contains( QgsPalLayerSettings::DirSymbRight ) )
3564  {
3565  tmpLyr.rightDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
3566  }
3567 
3568  if ( ddValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
3569  {
3570  tmpLyr.placeDirectionSymbol = static_cast< QgsPalLayerSettings::DirectionSymbols >( ddValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt() );
3571  }
3572 
3573  if ( ddValues.contains( QgsPalLayerSettings::DirSymbReverse ) )
3574  {
3575  tmpLyr.reverseDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbReverse ).toBool();
3576  }
3577 
3578  }
3579 }
3580 
3582  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3583 {
3584  QgsTextBufferSettings buffer = tmpLyr.format().buffer();
3585  bool changed = false;
3586 
3587  //buffer draw
3588  if ( ddValues.contains( QgsPalLayerSettings::BufferDraw ) )
3589  {
3590  buffer.setEnabled( ddValues.value( QgsPalLayerSettings::BufferDraw ).toBool() );
3591  changed = true;
3592  }
3593 
3594  if ( !buffer.enabled() )
3595  {
3596  if ( changed )
3597  {
3598  QgsTextFormat format = tmpLyr.format();
3599  format.setBuffer( buffer );
3600  tmpLyr.setFormat( format );
3601  }
3602 
3603  // tmpLyr.bufferSize > 0.0 && tmpLyr.bufferTransp < 100 figured in during evaluation
3604  return; // don't continue looking for unused values
3605  }
3606 
3607  //buffer size
3608  if ( ddValues.contains( QgsPalLayerSettings::BufferSize ) )
3609  {
3610  buffer.setSize( ddValues.value( QgsPalLayerSettings::BufferSize ).toDouble() );
3611  changed = true;
3612  }
3613 
3614  //buffer transparency
3615  if ( ddValues.contains( QgsPalLayerSettings::BufferTransp ) )
3616  {
3617  buffer.setOpacity( 1.0 - ddValues.value( QgsPalLayerSettings::BufferTransp ).toInt() / 100.0 );
3618  changed = true;
3619  }
3620 
3621  //buffer size units
3622  if ( ddValues.contains( QgsPalLayerSettings::BufferUnit ) )
3623  {
3624  QgsUnitTypes::RenderUnit bufunit = static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::BufferUnit ).toInt() );
3625  buffer.setSizeUnit( bufunit );
3626  changed = true;
3627  }
3628 
3629  //buffer color
3630  if ( ddValues.contains( QgsPalLayerSettings::BufferColor ) )
3631  {
3632  QVariant ddColor = ddValues.value( QgsPalLayerSettings::BufferColor );
3633  buffer.setColor( ddColor.value<QColor>() );
3634  changed = true;
3635  }
3636 
3637  //buffer pen join style
3638  if ( ddValues.contains( QgsPalLayerSettings::BufferJoinStyle ) )
3639  {
3640  buffer.setJoinStyle( static_cast< Qt::PenJoinStyle >( ddValues.value( QgsPalLayerSettings::BufferJoinStyle ).toInt() ) );
3641  changed = true;
3642  }
3643 
3644  //buffer blend mode
3645  if ( ddValues.contains( QgsPalLayerSettings::BufferBlendMode ) )
3646  {
3647  buffer.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::BufferBlendMode ).toInt() ) );
3648  changed = true;
3649  }
3650 
3651  if ( changed )
3652  {
3653  QgsTextFormat format = tmpLyr.format();
3654  format.setBuffer( buffer );
3655  tmpLyr.setFormat( format );
3656  }
3657 }
3658 
3660  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3661 {
3662  QgsTextBackgroundSettings background = tmpLyr.format().background();
3663  bool changed = false;
3664 
3665  //shape draw
3666  if ( ddValues.contains( QgsPalLayerSettings::ShapeDraw ) )
3667  {
3668  background.setEnabled( ddValues.value( QgsPalLayerSettings::ShapeDraw ).toBool() );
3669  changed = true;
3670  }
3671 
3672  if ( !background.enabled() )
3673  {
3674  if ( changed )
3675  {
3676  QgsTextFormat format = tmpLyr.format();
3677  format.setBackground( background );
3678  tmpLyr.setFormat( format );
3679  }
3680  return; // don't continue looking for unused values
3681  }
3682 
3683  if ( ddValues.contains( QgsPalLayerSettings::ShapeKind ) )
3684  {
3685  background.setType( static_cast< QgsTextBackgroundSettings::ShapeType >( ddValues.value( QgsPalLayerSettings::ShapeKind ).toInt() ) );
3686  changed = true;
3687  }
3688 
3689  if ( ddValues.contains( QgsPalLayerSettings::ShapeSVGFile ) )
3690  {
3691  background.setSvgFile( ddValues.value( QgsPalLayerSettings::ShapeSVGFile ).toString() );
3692  changed = true;
3693  }
3694 
3695  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeType ) )
3696  {
3697  background.setSizeType( static_cast< QgsTextBackgroundSettings::SizeType >( ddValues.value( QgsPalLayerSettings::ShapeSizeType ).toInt() ) );
3698  changed = true;
3699  }
3700 
3701  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeX ) )
3702  {
3703  QSizeF size = background.size();
3704  size.setWidth( ddValues.value( QgsPalLayerSettings::ShapeSizeX ).toDouble() );
3705  background.setSize( size );
3706  changed = true;
3707  }
3708  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeY ) )
3709  {
3710  QSizeF size = background.size();
3711  size.setHeight( ddValues.value( QgsPalLayerSettings::ShapeSizeY ).toDouble() );
3712  background.setSize( size );
3713  changed = true;
3714  }
3715 
3716  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeUnits ) )
3717  {
3718  background.setSizeUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeSizeUnits ).toInt() ) );
3719  changed = true;
3720  }
3721 
3722  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotationType ) )
3723  {
3724  background.setRotationType( static_cast< QgsTextBackgroundSettings::RotationType >( ddValues.value( QgsPalLayerSettings::ShapeRotationType ).toInt() ) );
3725  changed = true;
3726  }
3727 
3728  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotation ) )
3729  {
3730  background.setRotation( ddValues.value( QgsPalLayerSettings::ShapeRotation ).toDouble() );
3731  changed = true;
3732  }
3733 
3734  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffset ) )
3735  {
3736  background.setOffset( ddValues.value( QgsPalLayerSettings::ShapeOffset ).toPointF() );
3737  changed = true;
3738  }
3739 
3740  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffsetUnits ) )
3741  {
3742  background.setOffsetUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeOffsetUnits ).toInt() ) );
3743  changed = true;
3744  }
3745 
3746  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadii ) )
3747  {
3748  background.setRadii( ddValues.value( QgsPalLayerSettings::ShapeRadii ).toSizeF() );
3749  changed = true;
3750  }
3751 
3752  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadiiUnits ) )
3753  {
3754  background.setRadiiUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeRadiiUnits ).toInt() ) );
3755  changed = true;
3756  }
3757 
3758  if ( ddValues.contains( QgsPalLayerSettings::ShapeBlendMode ) )
3759  {
3760  background.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::ShapeBlendMode ).toInt() ) );
3761  changed = true;
3762  }
3763 
3764  if ( ddValues.contains( QgsPalLayerSettings::ShapeFillColor ) )
3765  {
3766  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeFillColor );
3767  background.setFillColor( ddColor.value<QColor>() );
3768  changed = true;
3769  }
3770 
3771  if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderColor ) )
3772  {
3773  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeBorderColor );
3774  background.setBorderColor( ddColor.value<QColor>() );
3775  changed = true;
3776  }
3777 
3778  if ( ddValues.contains( QgsPalLayerSettings::ShapeTransparency ) )
3779  {
3780  background.setOpacity( 1.0 - ddValues.value( QgsPalLayerSettings::ShapeTransparency ).toInt() / 100.0 );
3781  changed = true;
3782  }
3783 
3784  if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderWidth ) )
3785  {
3786  background.setBorderWidth( ddValues.value( QgsPalLayerSettings::ShapeBorderWidth ).toDouble() );
3787  changed = true;
3788  }
3789 
3790  if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderWidthUnits ) )
3791  {
3792  background.setBorderWidthUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShapeBorderWidthUnits ).toInt() ) );
3793  changed = true;
3794  }
3795 
3796  if ( ddValues.contains( QgsPalLayerSettings::ShapeJoinStyle ) )
3797  {
3798  background.setJoinStyle( static_cast< Qt::PenJoinStyle >( ddValues.value( QgsPalLayerSettings::ShapeJoinStyle ).toInt() ) );
3799  changed = true;
3800  }
3801 
3802  if ( changed )
3803  {
3804  QgsTextFormat format = tmpLyr.format();
3805  format.setBackground( background );
3806  tmpLyr.setFormat( format );
3807  }
3808 }
3809 
3811  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3812 {
3813  QgsTextShadowSettings shadow = tmpLyr.format().shadow();
3814  bool changed = false;
3815 
3816  //shadow draw
3817  if ( ddValues.contains( QgsPalLayerSettings::ShadowDraw ) )
3818  {
3819  shadow.setEnabled( ddValues.value( QgsPalLayerSettings::ShadowDraw ).toBool() );
3820  changed = true;
3821  }
3822 
3823  if ( !shadow.enabled() )
3824  {
3825  if ( changed )
3826  {
3827  QgsTextFormat format = tmpLyr.format();
3828  format.setShadow( shadow );
3829  tmpLyr.setFormat( format );
3830  }
3831  return; // don't continue looking for unused values
3832  }
3833 
3834  if ( ddValues.contains( QgsPalLayerSettings::ShadowUnder ) )
3835  {
3836  shadow.setShadowPlacement( static_cast< QgsTextShadowSettings::ShadowPlacement >( ddValues.value( QgsPalLayerSettings::ShadowUnder ).toInt() ) );
3837  changed = true;
3838  }
3839 
3840  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetAngle ) )
3841  {
3842  shadow.setOffsetAngle( ddValues.value( QgsPalLayerSettings::ShadowOffsetAngle ).toInt() );
3843  changed = true;
3844  }
3845 
3846  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetDist ) )
3847  {
3848  shadow.setOffsetDistance( ddValues.value( QgsPalLayerSettings::ShadowOffsetDist ).toDouble() );
3849  changed = true;
3850  }
3851 
3852  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetUnits ) )
3853  {
3854  shadow.setOffsetUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShadowOffsetUnits ).toInt() ) );
3855  changed = true;
3856  }
3857 
3858  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadius ) )
3859  {
3860  shadow.setBlurRadius( ddValues.value( QgsPalLayerSettings::ShadowRadius ).toDouble() );
3861  changed = true;
3862  }
3863 
3864  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadiusUnits ) )
3865  {
3866  shadow.setBlurRadiusUnit( static_cast< QgsUnitTypes::RenderUnit >( ddValues.value( QgsPalLayerSettings::ShadowRadiusUnits ).toInt() ) );
3867  changed = true;
3868  }
3869 
3870  if ( ddValues.contains( QgsPalLayerSettings::ShadowColor ) )
3871  {
3872  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShadowColor );
3873  shadow.setColor( ddColor.value<QColor>() );
3874  changed = true;
3875  }
3876 
3877  if ( ddValues.contains( QgsPalLayerSettings::ShadowTransparency ) )
3878  {
3879  shadow.setOpacity( 1.0 - ddValues.value( QgsPalLayerSettings::ShadowTransparency ).toInt() / 100.0 );
3880  changed = true;
3881  }
3882 
3883  if ( ddValues.contains( QgsPalLayerSettings::ShadowScale ) )
3884  {
3885  shadow.setScale( ddValues.value( QgsPalLayerSettings::ShadowScale ).toInt() );
3886  changed = true;
3887  }
3888 
3889 
3890  if ( ddValues.contains( QgsPalLayerSettings::ShadowBlendMode ) )
3891  {
3892  shadow.setBlendMode( static_cast< QPainter::CompositionMode >( ddValues.value( QgsPalLayerSettings::ShadowBlendMode ).toInt() ) );
3893  changed = true;
3894  }
3895 
3896  if ( changed )
3897  {
3898  QgsTextFormat format = tmpLyr.format();
3899  format.setShadow( shadow );
3900  tmpLyr.setFormat( format );
3901  }
3902 }
3903 
3904 
3905 
3907 {
3908  mEngine->run( context );
3909 }
3910 
3912 {
3913 }
3914 
3916 {
3917  return mEngine->takeResults();
3918 }
3919 
3920 void QgsPalLabeling::numCandidatePositions( int& candPoint, int& candLine, int& candPolygon )
3921 {
3922  mEngine->numCandidatePositions( candPoint, candLine, candPolygon );
3923 }
3924 
3925 void QgsPalLabeling::setNumCandidatePositions( int candPoint, int candLine, int candPolygon )
3926 {
3927  mEngine->setNumCandidatePositions( candPoint, candLine, candPolygon );
3928 }
3929 
3931 {
3932  mEngine->setSearchMethod( s );
3933 }
3934 
3936 {
3937  return mEngine->searchMethod();
3938 }
3939 
3941 {
3943 }
3944 
3946 {
3948 }
3949 
3951 {
3953 }
3954 
3956 {
3958 }
3959 
3961 {
3963 }
3964 
3966 {
3968 }
3969 
3971 {
3973 }
3974 
3976 {
3978 }
3979 
3981 {
3983 }
3984 
3986 {
3988 }
3989 
3990 void QgsPalLabeling::drawLabelCandidateRect( pal::LabelPosition* lp, QPainter* painter, const QgsMapToPixel* xform, QList<QgsLabelCandidate>* candidates )
3991 {
3992  QgsPoint outPt = xform->transform( lp->getX(), lp->getY() );
3993 
3994  painter->save();
3995 
3996 #if 0 // TODO: generalize some of this
3997  double w = lp->getWidth();
3998  double h = lp->getHeight();
3999  double cx = lp->getX() + w / 2.0;
4000  double cy = lp->getY() + h / 2.0;
4001  double scale = 1.0 / xform->mapUnitsPerPixel();
4002  double rotation = xform->mapRotation();
4003  double sw = w * scale;
4004  double sh = h * scale;
4005  QRectF rect( -sw / 2, -sh / 2, sw, sh );
4006 
4007  painter->translate( xform->transform( QPointF( cx, cy ) ).toQPointF() );
4008  if ( rotation )
4009  {
4010  // Only if not horizontal
4011  if ( lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT &&
4012  lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT_OVER &&
4013  lp->getFeaturePart()->getLayer()->getArrangement() != P_HORIZ )
4014  {
4015  painter->rotate( rotation );
4016  }
4017  }
4018  painter->translate( rect.bottomLeft() );
4019  painter->rotate( -lp->getAlpha() * 180 / M_PI );
4020  painter->translate( -rect.bottomLeft() );
4021 #else
4022  QgsPoint outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
4023  QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
4024  painter->translate( QPointF( outPt.x(), outPt.y() ) );
4025  painter->rotate( -lp->getAlpha() * 180 / M_PI );
4026 #endif
4027 
4028  if ( lp->conflictsWithObstacle() )
4029  {
4030  painter->setPen( QColor( 255, 0, 0, 64 ) );
4031  }
4032  else
4033  {
4034  painter->setPen( QColor( 0, 0, 0, 64 ) );
4035  }
4036  painter->drawRect( rect );
4037  painter->restore();
4038 
4039  // save the rect
4040  rect.moveTo( outPt.x(), outPt.y() );
4041  if ( candidates )
4042  candidates->append( QgsLabelCandidate( rect, lp->cost() * 1000 ) );
4043 
4044  // show all parts of the multipart label
4045  if ( lp->getNextPart() )
4046  drawLabelCandidateRect( lp->getNextPart(), painter, xform, candidates );
4047 }
4048 
4050 {
4052 }
4053 
4055 {
4057 }
4058 
4060 {
4061  QgsProject::instance()->removeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/SearchMethod" ) );
4062  QgsProject::instance()->removeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/CandidatesPoint" ) );
4063  QgsProject::instance()->removeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/CandidatesLine" ) );
4064  QgsProject::instance()->removeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/CandidatesPolygon" ) );
4065  QgsProject::instance()->removeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/ShowingCandidates" ) );
4066  QgsProject::instance()->removeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/ShowingAllLabels" ) );
4067  QgsProject::instance()->removeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/ShowingPartialsLabels" ) );
4068  QgsProject::instance()->removeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/DrawOutlineLabels" ) );
4069 }
4070 
4072 {
4073  QgsPalLabeling* lbl = new QgsPalLabeling();
4079  return lbl;
4080 }
4081 
4082 
4084 {
4085  mLabelSearchTree = new QgsLabelSearchTree();
4086 }
4087 
4089 {
4090  delete mLabelSearchTree;
4091  mLabelSearchTree = nullptr;
4092 }
4093 
4094 QList<QgsLabelPosition> QgsLabelingResults::labelsAtPosition( const QgsPoint& p ) const
4095 {
4096  QList<QgsLabelPosition> positions;
4097 
4098  QList<QgsLabelPosition*> positionPointers;
4099  if ( mLabelSearchTree )
4100  {
4101  mLabelSearchTree->label( p, positionPointers );
4102  QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
4103  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
4104  {
4105  positions.push_back( QgsLabelPosition( **pointerIt ) );
4106  }
4107  }
4108 
4109  return positions;
4110 }
4111 
4112 QList<QgsLabelPosition> QgsLabelingResults::labelsWithinRect( const QgsRectangle& r ) const
4113 {
4114  QList<QgsLabelPosition> positions;
4115 
4116  QList<QgsLabelPosition*> positionPointers;
4117  if ( mLabelSearchTree )
4118  {
4119  mLabelSearchTree->labelsInRect( r, positionPointers );
4120  QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
4121  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
4122  {
4123  positions.push_back( QgsLabelPosition( **pointerIt ) );
4124  }
4125  }
4126 
4127  return positions;
4128 }
virtual void registerDiagramFeature(const QString &layerID, QgsFeature &feat, QgsRenderContext &context) override
called for every diagram feature
Label below point, slightly right of center.
bool labelsEnabled() const
Returns whether the layer contains labels which are enabled and should be drawn.
Class for parsing and evaluation of expressions (formerly called "search strings").
void setShowingCandidates(bool showing)
QgsFeatureId id
Definition: qgsfeature.h:139
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
void setSearchMethod(QgsPalLabeling::Search s)
Set which search method to use for removal collisions between labels.
bool contains(const QgsRectangle &rect) const
return true when rectangle contains other rectangle
void setActive(bool active)
void setScale(int scale)
Sets the scaling used for the drop shadow (in percentage of original size).
Shape size is determined by adding a buffer margin around text.
void setLineHeight(double height)
Sets the line height for text.
void setRadiiUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s radii.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
double rendererScale() const
Label on bottom-left of point.
virtual void registerFeature(const QString &layerID, QgsFeature &feat, QgsRenderContext &context) override
Register a feature for labelling.
double y
Definition: qgspoint.h:116
RotationType
Methods for determining the rotation of the background shape.
QDomElement writeXml(QDomDocument &doc) const
Write settings into a DOM element.
QgsPoint center() const
Center point of the rectangle.
Definition: qgsrectangle.h:221
QHash< QString, QgsVectorLayerLabelProvider * > mLabelProviders
hashtable of label providers, being filled during labeling (key = layer ID)
static void dataDefinedShapeBackground(QgsPalLayerSettings &tmpLyr, const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > &ddValues)
void setOpacity(double opacity)
Sets the text&#39;s opacity.
void setOffsetUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shadow&#39;s offset.
bool isShowingPartialsLabels() const
void addProvider(QgsAbstractLabelProvider *provider)
Add provider of label features. Takes ownership of the provider.
int size() const
Return number of items.
Definition: qgsfields.cpp:122
QSizeF radii() const
Returns the radii used for rounding the corners of shapes.
QSizeF size() const
Returns the size of the background shape.
double opacity() const
Returns the text&#39;s opacity.
static void dataDefinedTextStyle(QgsPalLayerSettings &tmpLyr, const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > &ddValues)
bool drawLabelRectOnly() const
Returns whether the engine will only draw the outline rectangles of labels, not the label contents th...
A container class for data source field mapping or expression.
const QgsVectorSimplifyMethod & vectorSimplifyMethod() const
Added in QGIS v2.4.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the shape&#39;s size.
virtual int prepareDiagramLayer(QgsVectorLayer *layer, QSet< QString > &attrNames, QgsRenderContext &ctx) override
adds a diagram layer to the labeling engine
QString name
Definition: qgsfield.h:55
virtual bool willUseLayer(QgsVectorLayer *layer) override
called to find out whether the layer is used for labeling
QPointF offset() const
Returns the offset used for drawing the background shape.
QColor fillColor() const
Returns the color used for filing the background shape.
void removeProvider(QgsAbstractLabelProvider *provider)
Remove provider if the provider&#39;s initialization failed. Provider instance is deleted.
void setNumCandidatePositions(int candPoint, int candLine, int candPolygon)
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspoint.cpp:124
bool expressionIsPrepared() const
Returns whether the data defined object&#39;s expression is prepared.
static QString encodeSize(QSizeF size)
Encodes a QSizeF to a string.
QgsLabelingResults * takeResults()
Return pointer to recently computed results (in drawLabeling()) and pass the ownership of results to ...
bool testFlag(Flag f) const
Test whether a particular flag is enabled.
A class to query the labeling structure at a given point (small wraper around pal RTree class) ...
QMap< QString, QString > dataDefinedMap(QgsPalLayerSettings::DataDefinedProperties p) const
Get property value as separate values split into Qmap.
Draw shadow under buffer.
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.
SimplifyAlgorithm simplifyAlgorithm() const
Gets the local simplification algorithm of the vector layer managed.
ShadowPlacement
Placement positions for text shadow.
Place direction symbols on below label.
void setShowingPartialsLabels(bool showing)
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setSearchMethod(Search s)
QgsTextShadowSettings & shadow()
Returns a reference to the text drop shadow settings.
void loadEngineSettings()
load/save engine settings to project file
double distance(double x, double y) const
Returns the distance between this point and a specified x, y coordinate.
Definition: qgspoint.cpp:358
QgsMapLayer * mapLayer(const QString &theLayerId) const
Retrieve a pointer to a registered layer by layer ID.
double blurRadius() const
Returns the blur radius for the shadow.
static void drawLabelCandidateRect(pal::LabelPosition *lp, QPainter *painter, const QgsMapToPixel *xform, QList< QgsLabelCandidate > *candidates=nullptr)
double getY(int i=0) const
get the down-left y coordinate
double opacity() const
Returns the background shape&#39;s opacity.
void setSize(double size)
Sets the size of the buffer.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units used for the buffer size.
void emitStyleChanged()
Triggers an emission of the styleChanged() signal.
double offsetDistance() const
Returns the distance for offsetting the position of the shadow from the text.
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
void setJoinStyle(Qt::PenJoinStyle style)
Sets the join style used for drawing the buffer.
bool dataDefinedUseExpression(QgsPalLayerSettings::DataDefinedProperties p) const
Whether data definition is set to use an expression.
void writeSettingsToProject()
Write configuration of the labeling engine to the current project file.
QVariant evaluate()
Evaluate the feature and return the result.
QDomElement toXmlElement(QDomDocument &document, const QString &elementName) const
Returns a DOM element containing the properties of the data defined container.
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
Candidates are placed in predefined positions around a point. Peference is given to positions with gr...
QuadrantPosition quadOffset
void setOffsetUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s offset.
QColor color() const
Returns the color that text will be rendered in.
Class that adds extra information to QgsLabelFeature for text labels.
QString evalErrorString() const
Returns evaluation error.
void numCandidatePositions(int &candPoint, int &candLine, int &candPolygon)
double computeMapUnitsPerPixel(const QgsRenderContext &c) const
Computes a map units per pixel scaling factor, respecting the minimum and maximum scales set for the ...
void setBlurRadius(double blurRadius)
Sets the blur radius for the shadow.
bool dataDefinedIsActive(QgsPalLayerSettings::DataDefinedProperties p) const
Whether data definition is active.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
void setDefinedFont(const QFont &f)
Set font to be used for rendering.
QgsExpression * expression()
void setOpacity(double opacity)
Sets the shadow&#39;s opacity.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
Container of fields for a vector layer.
Definition: qgsfields.h:36
Label on top of point, slightly right of center.
static QgsPalLayerSettings fromLayer(QgsVectorLayer *layer)
The QgsVectorLayerLabelProvider class implements a label provider for vector layers.
void setMapSettings(const QgsMapSettings &mapSettings)
Associate map settings instance.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:78
bool drawLabels
Whether to draw labels for this layer.
QHash< QString, QgsVectorLayerDiagramProvider * > mDiagramProviders
hashtable of diagram providers (key = layer ID)
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...
void setBackground(const QgsTextBackgroundSettings &backgroundSettings)
Sets the text&#39;s background settings.
void readFromLayer(QgsVectorLayer *layer)
void setNumCandidatePositions(int candPoint, int candLine, int candPolygon)
Set number of candidate positions that will be generated for each label feature.
static void dataDefinedDropShadow(QgsPalLayerSettings &tmpLyr, const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > &ddValues)
void setJoinStyle(Qt::PenJoinStyle style)
Sets the join style used for drawing the background shape.
QgsMapUnitScale repeatDistanceMapUnitScale
MultiLineAlign multilineAlign
virtual QgsAbstractGeometry * boundary() const =0
Returns the closure of the combinatorial boundary of the geometry (ie the topological boundary of the...
void removeDataDefinedProperty(QgsPalLayerSettings::DataDefinedProperties p)
Set a property to static instead data defined.
FeaturePart * getFeaturePart()
return the feature corresponding to this labelposition
QgsGeometry intersection(const QgsGeometry &geometry) const
Returns a geometry representing the points shared by this geometry and other.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:135
Container for settings relating to a text background object.
bool contains(const QgsPoint *p) const
Test for containment of a point (uses GEOS)
virtual void init(const QgsMapSettings &mapSettings) override
called when we&#39;re going to start with rendering
void setBlurRadiusUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shadow&#39;s blur radius.
QVector< PredefinedPointPosition > predefinedPositionOrder
Ordered list of predefined label positions for points.
SimplifyAlgorithm
Types of simplification algorithms that can be used.
static QStringList splitToLines(const QString &text, const QString &wrapCharacter)
Splits a text string to a list of separate lines, using a specified wrap character.
QString parserErrorString() const
Returns parser error.
QgsCoordinateTransform ct
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:196
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
double maxScale
The maximum scale, or 0.0 if unset.
virtual bool prepare(const QgsRenderContext &context, QSet< QString > &attributeNames)
Prepare for registration of features.
bool isGeosValid() const
Checks validity of the geometry using GEOS.
ShapeType type() const
Returns the type of background shape (eg square, ellipse, SVG).
void setFormat(const QgsTextFormat &format)
Sets the label text formatting settings, eg font settings, buffer settings, etc.
double zIndex
Z-Index of label, where labels with a higher z-index are rendered on top of labels with a lower z-ind...
double mapRotation() const
Return current map rotation in degrees.
void setHasFixedPosition(bool enabled)
Set whether the label should use a fixed position instead of being automatically placed.
QgsStringReplacementCollection substitutions
Substitution collection for automatic text substitution with labels.
Qt::PenJoinStyle joinStyle() const
Returns the buffer join style.
bool useExpression() const
Returns if the field or the expression part is active.
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:137
The QgsMapSettings class contains configuration for rendering of the map.
static QString encodeColor(const QColor &color)
static bool staticWillUseLayer(QgsVectorLayer *layer)
called to find out whether the layer is used for labeling
void writeXml(QDomElement &elem, QDomDocument &doc) const
Writes the collection state to an XML element.
virtual void clearActiveLayer(const QString &layerID) override
clears data defined objects from PAL layer settings for a registered layer
QgsPoint transform(const QgsPoint &p) const
Transform the point from map (world) coordinates to device coordinates.
void setSize(const QSizeF &size)
Sets the size of the background shape.
Place direction symbols on left/right of label.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:33
void setUseExpression(bool use)
Controls if the field or the expression part is active.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the background shape.
QgsPalLabeling::Search searchMethod() const
Which search method to use for removal collisions between labels.
Shape rotation is a fixed angle.
double cost() const
Returns the candidate label position&#39;s geographical cost.
No simplification can be applied.
static double scaleToPixelContext(double size, const QgsRenderContext &c, QgsUnitTypes::RenderUnit unit, bool rasterfactor=false, const QgsMapUnitScale &mapUnitScale=QgsMapUnitScale())
Calculates size (considering output size should be in pixel or map units, scale factors and optionall...
ObstacleType obstacleType
Controls how features act as obstacles for labels.
int indexFromName(const QString &fieldName) const
Get the field index from the field name.
Definition: qgsfields.cpp:176
bool isDrawingOutlineLabels() const
static void dataDefinedTextBuffer(QgsPalLayerSettings &tmpLyr, const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > &ddValues)
QColor color() const
Returns the color of the drop shadow.
void setIsObstacle(bool enabled)
Sets whether the feature will act as an obstacle for labels.
void setOffsetDistance(double distance)
Sets the distance for offsetting the position of the shadow from the text.
bool setFromXmlElement(const QDomElement &element)
Sets the properties of the data defined container from an XML element.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsMapLayerRegistry.
Label on left of point.
virtual bool prepare(const QgsRenderContext &context, QSet< QString > &attributeNames)
Prepare for registration of features.
static QString capitalize(const QString &string, Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
QColor borderColor() const
Returns the color used for outlining the background shape.
QgsPoint transform(const QgsPoint &point, TransformDirection direction=ForwardTransform) const
Transform the point from the source CRS to the destination CRS.
Offset distance applies from point geometry.
virtual int prepareLayer(QgsVectorLayer *layer, QSet< QString > &attrNames, QgsRenderContext &ctx) override
hook called when drawing layer before issuing select()
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
double tolerance() const
Gets the tolerance of simplification in map units. Represents the maximum distance in map units betwe...
const QgsRectangle & extent() const
static bool fontFamilyOnSystem(const QString &family)
Check whether font family is on system in a quick manner, which does not compare [foundry].
Labels can be placed above a line feature.
static QgsGeometry prepareGeometry(const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, QgsGeometry *clipGeometry=nullptr)
Prepares a geometry for registration with PAL.
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent...
void setField(const QString &field)
Set the field name which this QgsDataDefined represents.
The QgsVectorLayerDiagramProvider class implements support for diagrams within the labeling engine...
QString updateDataDefinedString(const QString &value)
Convert old property value to new one as delimited values.
The geometries can be fully simplified by its BoundingBox.
double getHeight() const
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the drop shadow.
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:211
QString expressionString() const
Returns the expression string of this QgsDataDefined.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspointv2.h:35
void setColor(const QColor &color)
Sets the color for the drop shadow.
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
void setBorderWidth(double width)
Sets the width of the shape&#39;s border (outline).
void setEnabled(bool enabled)
Sets whether the text shadow will be drawn.
virtual QgsFields fields() const =0
Returns the fields associated with this data provider.
void setEnabled(bool enabled)
Sets whether the text buffer will be drawn.
#define M_PI
Arranges candidates in a circle around a point (or centroid of a polygon). Applies to point or polygo...
LabelPosition * getNextPart() const
const QgsMapToPixel * xform
void setColor(const QColor &color)
Sets the color that text will be rendered in.
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.
Draw shadow under text.
QString svgFile() const
Returns the path to the background SVG file, if set.
Qt::PenJoinStyle joinStyle() const
Returns the join style used for drawing the background shape.
void readXml(QDomElement &elem)
Read settings from a DOM element.
bool isEmpty() const
Returns true if the geometry is empty (ie, contains no underlying geometry accessible via geometry)...
QgsTextBackgroundSettings & background()
Returns a reference to the text background settings.
QgsTextBufferSettings & buffer()
Returns a reference to the text buffer settings.
bool removeEntry(const QString &scope, const QString &key)
Remove the given key.
Upside-down labels (90 <= angle < 270) are shown upright.
SizeType
Methods for determining the background shape size.
QString exportToWkt(int precision=17) const
Exports the geometry to WKT.
double opacity() const
Returns the buffer opacity.
OffsetType
Behaviour modifier for label offset and distance, only applies in some label placement modes...
QgsFeature * mCurFeat
bool isShowingAllLabels() const
static QPainter::CompositionMode decodeBlendMode(const QString &s)
void calculateLabelSize(const QFontMetricsF *fm, QString text, double &labelX, double &labelY, QgsFeature *f=nullptr, QgsRenderContext *context=nullptr)
void registerFeature(QgsFeature &f, QgsRenderContext &context, QgsLabelFeature **labelFeature=nullptr, QgsGeometry *obstacleGeometry=nullptr)
Register a feature for labelling.
void removeCustomProperty(const QString &key)
Remove a custom property from layer.
const QgsAbstractVectorLayerLabeling * labeling() const
Access to labeling configuration.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:113
double mapUnitsPerPixel() const
Return current map units per pixel.
static int sizeToPixel(double size, const QgsRenderContext &c, QgsUnitTypes::RenderUnit unit, bool rasterfactor=false, const QgsMapUnitScale &mapUnitScale=QgsMapUnitScale())
Calculates pixel size (considering output size should be in pixel or map units, scale factors and opt...
QgsDataDefined * dataDefinedProperty(QgsPalLayerSettings::DataDefinedProperties p)
Get a data defined property pointer.
Stores visual margins for labels (left, right, top and bottom)
SizeUnit
Units used for option sizes, before being converted to rendered sizes.
Implementation of GeometrySimplifier using the "MapToPixel" algorithm.
A class to represent a point.
Definition: qgspoint.h:111
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.
QgsWkbTypes::GeometryType type() const
Returns type of the geometry as a QgsWkbTypes::GeometryType.
static Qt::PenJoinStyle _decodePenJoinStyle(const QString &str)
Convert just the first letter of each word to uppercase, leave the rest untouched.
virtual QgsPalLabeling * clone() override
called when passing engine among map renderers
double length() const
Returns the length of geometry using GEOS.
Convert all characters to uppercase.
void setSizeType(SizeType type)
Sets the method used to determine the size of the background shape (eg fixed size or buffer around te...
const QgsTextFormat & format() const
Returns the label text formatting settings, eg font settings, buffer settings, etc.
QList< QgsLabelPosition > labelsWithinRect(const QgsRectangle &r) const
return infos about labels within a given (map) rectangle
Capitalization
Capitalization options.
void setType(ShapeType type)
Sets the type of background shape to draw (eg square, ellipse, SVG).
Place direction symbols on above label.
Draw shadow below all text components.
QString expression() const
Return the original, unmodified expression string.
QgsExpressionContext & expressionContext()
Gets the expression context.
QString field() const
Get the field which this QgsDataDefined represents.
double rotation() const
Returns the rotation for the background shape.
double lineHeight() const
Returns the line height for text.
void setEnabled(bool enabled)
Sets whether the text background will be drawn.
unsigned int placementFlags
int offsetAngle() const
Returns the angle for offsetting the position of the shadow from the text.
ShapeType
Background shape types.
QgsPalLayerSettings & operator=(const QgsPalLayerSettings &s)
copy operator - only copies the permanent members
Placement
Placement modes which determine how label candidates are generated for a feature. ...
bool isShowingCandidates() const
static QString encodePredefinedPositionOrder(const QVector< QgsPalLayerSettings::PredefinedPointPosition > &positions)
Encodes an ordered list of predefined point label positions to a string.
The QgsLabelingEngine class provides map labeling functionality.
QgsGeometry simplify(double tolerance) const
Returns a simplified version of this geometry using a specified tolerance value.
void run(QgsRenderContext &context)
compute the labeling with given map settings and providers
QgsExpression * getLabelExpression()
Returns the QgsExpression for this label settings.
Arranges candidates over a point (or centroid of a polygon), or at a preset offset from the point...
void setDrawingOutlineLabels(bool outline)
This class contains information how to simplify geometries fetched from a vector layer.
Contains information about the context of a rendering operation.
Shape rotation is offset from text rotation.
void setDataDefinedProperty(QgsPalLayerSettings::DataDefinedProperties p, bool active, bool useExpr, const QString &expr, const QString &field)
Set a property as data defined.
QgsMapUnitScale sizeMapUnitScale() const
Returns the map unit scale object for the size.
Whether to draw all labels even if there would be collisions.
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
The QgsLabelFeature class describes a feature that should be used within the labeling engine...
const QgsMapToPixel & mapToPixel() const
QgsGeometry extentGeom
double getAlpha() const
get alpha
static QgsPalLayerSettings::SizeUnit _decodeUnits(const QString &str)
bool enabled() const
Returns whether the shadow is enabled.
Mixed case, ie no change.
QgsMapUnitScale distMapUnitScale
void setBorderWidthUnit(QgsUnitTypes::RenderUnit units)
Sets the units used for the shape&#39;s border width.
void setRadii(const QSizeF &radii)
Sets the radii used for rounding the corners of shapes.
bool fitInPolygonOnly
True if only labels which completely fit within a polygon are allowed.
double getWidth() const
Container for settings relating to a text shadow.
QColor color() const
Returns the color of the buffer.
bool conflictsWithObstacle() const
Returns whether the position is marked as conflicting with an obstacle feature.
double getX(int i=0) const
get the down-left x coordinate
double size() const
Returns the size of the buffer.
QgsLabelingResults * takeResults()
Return pointer to recently computed results and pass the ownership of results to the caller...
GEOSGeometry * exportToGeos(double precision=0) const
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
QgsLabelingEngine * mEngine
New labeling engine to interface with PAL.
Search searchMethod() const
Container for settings relating to a text buffer.
bool forceLocalOptimization() const
Gets where the simplification executes, after fetch the geometries from provider, or when supported...
void numCandidatePositions(int &candPoint, int &candLine, int &candPolygon)
Get number of candidate positions that will be generated for each label feature (default to 8) ...
void setFillColor(const QColor &color)
Sets the color used for filing the background shape.
int rotate(double rotation, const QgsPoint &center)
Rotate this geometry around the Z axis.
void setBorderColor(const QColor &color)
Sets the color used for outlining the background shape.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
OffsetType offsetType
Offset type for layer (only applies in certain placement modes)
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:348
double size() const
Returns the size for rendered text.
QgsRectangle boundingBox() const
Returns the bounding box of this feature.
void writeToLayer(QgsVectorLayer *layer) const
Writes settings to a layer&#39;s custom properties.
bool useSubstitutions
True if substitutions should be applied.
virtual void clearActiveLayers() override
clears all PAL layer settings for registered layers
bool enabled() const
Returns whether the background is enabled.
Whether to only draw the label rect and not the actual label text (used for unit tests) ...
static QStringList splitToGraphemes(const QString &text)
Splits a text string to a list of graphemes, which are the smallest allowable character divisions in ...
static Q_INVOKABLE RenderUnit decodeRenderUnit(const QString &string, bool *ok=0)
Decodes a render unit from a string.
SimplifyHints simplifyHints() const
Gets the simplification hints of the vector layer managed.
Whether to draw rectangles of generated candidates (good for debugging)
Class for doing transforms between two map coordinate systems.
SizeType sizeType() const
Returns the method used to determine the size of the background shape (eg fixed size or buffer around...
LabelPosition is a candidate feature label position.
Definition: labelposition.h:51
double borderWidth() const
Returns the width of the shape&#39;s border (outline).
int transform(const QgsCoordinateTransform &ct)
Transform this geometry as described by CoordinateTransform ct.
QDomElement writeXml(QDomDocument &doc)
Write settings into a DOM element.
bool enabled() const
Returns whether the buffer is enabled.
Whether to render labels as text or outlines.
int scale() const
Returns the scaling used for the drop shadow (in percentage of original size).
Shape rotation is synced with text rotation.
void writeToLayer(QgsVectorLayer *layer)
void setShadowPlacement(QgsTextShadowSettings::ShadowPlacement placement)
Sets the placement for the drop shadow.
Label on right of point.
Whether to use also label candidates that are partially outside of the map view.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the text.
void removeAllDataDefinedProperties()
Clear all data-defined properties.
void setBuffer(const QgsTextBufferSettings &bufferSettings)
Sets the text&#39;s buffer settings.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
Draw shadow under background shape.
Signifies that the AboveLine and BelowLine flags should respect the map&#39;s orientation rather than the...
bool isExpression
Is this label made from a expression string eg FieldName || &#39;mm&#39;.
Convert all characters to lowercase.
Class that stores computed placement from labeling engine.
void setShadow(const QgsTextShadowSettings &shadowSettings)
Sets the text&#39;s drop shadow settings.
void setShowingAllLabels(bool showing)
virtual QString type() const =0
Unique type string of the labeling configuration implementation.
void readXml(const QDomElement &elem)
Reads the collection state from an XML element.
Custom exception class for Coordinate Reference System related exceptions.
QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
static QVector< QgsPalLayerSettings::PredefinedPointPosition > decodePredefinedPositionOrder(const QString &positionString)
Decodes a string to an ordered list of predefined point label positions.
QString process(const QString &input) const
Processes a given input string, applying any valid replacements which should be made using QgsStringR...
void readSettingsFromProject()
Read configuration of the labeling engine from the current project file.
void setFlag(Flag f, bool enabled=true)
Set whether a particual flag is enabled.
double area() const
Returns the area of the geometry using GEOS.
QgsVectorDataProvider * dataProvider()
Returns the data provider.
static bool geometryRequiresPreparation(const QgsGeometry &geometry, QgsRenderContext &context, const QgsCoordinateTransform &ct, QgsGeometry *clipGeometry=nullptr)
Checks whether a geometry requires preparation before registration with PAL.
double rasterScaleFactor() const
Container for all settings relating to text rendering.
QgsPoint toMapCoordinatesF(double x, double y) const
Transform device coordinates to map (world) coordinates.
virtual QgsAttrPalIndexNameHash palAttributeIndexNames() const
Return list of indexes to names for QgsPalLabeling fix.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the size of rendered text.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the buffer.
void setSvgFile(const QString &file)
Sets the path to the background SVG file.
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
static bool checkMinimumSizeMM(const QgsRenderContext &context, const QgsGeometry *geom, double minSize)
Checks whether a geometry exceeds the minimum required size for a geometry to be labeled.
double scaleFactor() const
Represents a vector layer which manages a vector based data sets.
double minScale
The minimum scale, or 0.0 if unset.
void setOpacity(double opacity)
Sets the buffer opacity.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:277
Square - buffered sizes only.
void setDrawLabelRectOnly(bool drawRect)
Sets whether the engine should only draw the outline rectangles of labels, not the label contents the...
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
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)
Definition: MathUtils.cc:857
QString namedStyle() const
Returns the named style for the font used for rendering text (eg "bold").
void setExpressionString(const QString &expr)
Sets the expression for this QgsDataDefined.
QFont font() const
Returns the font used for rendering text.
QgsAbstractGeometry * geometry() const
Returns the underlying geometry store.
void setRotationType(RotationType type)
Sets the method used for rotating the background shape.