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