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