QGIS API Documentation  2.2.0-Valmiera
 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 using namespace pal;
62 
63 #if 0
64 class QgsPalGeometry : public PalGeometry
65 {
66  public:
67  QgsPalGeometry( QgsFeatureId id, QString text, GEOSGeometry* g,
68  qreal ltrSpacing = 0.0, qreal wordSpacing = 0.0, bool curvedLabeling = false )
69  : mG( g )
70  , mText( text )
71  , mId( id )
72  , mInfo( NULL )
73  , mIsDiagram( false )
74  , mIsPinned( false )
75  , mFontMetrics( NULL )
76  , mLetterSpacing( ltrSpacing )
77  , mWordSpacing( wordSpacing )
78  , mCurvedLabeling( curvedLabeling )
79  {
80  mStrId = FID_TO_STRING( mId ).toAscii();
81  mDefinedFont = QFont();
82  }
83 
85  {
86  if ( mG )
87  GEOSGeom_destroy( mG );
88  delete mInfo;
89  delete mFontMetrics;
90  }
91 
92  // getGeosGeometry + releaseGeosGeometry is called twice: once when adding, second time when labeling
93 
94  const GEOSGeometry* getGeosGeometry()
95  {
96  return mG;
97  }
98  void releaseGeosGeometry( const GEOSGeometry* /*geom*/ )
99  {
100  // nothing here - we'll delete the geometry in destructor
101  }
102 
103  const char* strId() { return mStrId.data(); }
104  QString text() { return mText; }
105 
106  pal::LabelInfo* info( QFontMetricsF* fm, const QgsMapToPixel* xform, double fontScale, double maxinangle, double maxoutangle )
107  {
108  if ( mInfo )
109  return mInfo;
110 
111  mFontMetrics = new QFontMetricsF( *fm ); // duplicate metrics for when drawing label
112 
113  // max angle between curved label characters (20.0/-20.0 was default in QGIS <= 1.8)
114  if ( maxinangle < 20.0 )
115  maxinangle = 20.0;
116  if ( 60.0 < maxinangle )
117  maxinangle = 60.0;
118  if ( maxoutangle > -20.0 )
119  maxoutangle = -20.0;
120  if ( -95.0 > maxoutangle )
121  maxoutangle = -95.0;
122 
123  // create label info!
124  QgsPoint ptZero = xform->toMapCoordinates( 0, 0 );
125  QgsPoint ptSize = xform->toMapCoordinatesF( 0.0, -fm->height() / fontScale );
126 
127  // mLetterSpacing/mWordSpacing = 0.0 is default for non-curved labels
128  // (non-curved spacings handled by Qt in QgsPalLayerSettings/QgsPalLabeling)
129  qreal charWidth;
130  qreal wordSpaceFix;
131  mInfo = new pal::LabelInfo( mText.count(), ptSize.y() - ptZero.y(), maxinangle, maxoutangle );
132  for ( int i = 0; i < mText.count(); i++ )
133  {
134  mInfo->char_info[i].chr = mText[i].unicode();
135 
136  // reconstruct how Qt creates word spacing, then adjust per individual stored character
137  // this will allow PAL to create each candidate width = character width + correct spacing
138  charWidth = fm->width( mText[i] );
139  if ( mCurvedLabeling )
140  {
141  wordSpaceFix = qreal( 0.0 );
142  if ( mText[i] == QString( " " )[0] )
143  {
144  // word spacing only gets added once at end of consecutive run of spaces, see QTextEngine::shapeText()
145  int nxt = i + 1;
146  wordSpaceFix = ( nxt < mText.count() && mText[nxt] != QString( " " )[0] ) ? mWordSpacing : qreal( 0.0 );
147  }
148  if ( fm->width( QString( mText[i] ) ) - fm->width( mText[i] ) - mLetterSpacing != qreal( 0.0 ) )
149  {
150  // word spacing applied when it shouldn't be
151  wordSpaceFix -= mWordSpacing;
152  }
153  charWidth = fm->width( QString( mText[i] ) ) + wordSpaceFix;
154  }
155 
156  ptSize = xform->toMapCoordinatesF((( double ) charWidth ) / fontScale , 0.0 );
157  mInfo->char_info[i].width = ptSize.x() - ptZero.x();
158  }
159  return mInfo;
160  }
161 
162  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& dataDefinedValues() const { return mDataDefinedValues; }
163  void addDataDefinedValue( QgsPalLayerSettings::DataDefinedProperties p, QVariant v ) { mDataDefinedValues.insert( p, v ); }
164 
165  void setIsDiagram( bool d ) { mIsDiagram = d; }
166  bool isDiagram() const { return mIsDiagram; }
167 
168  void setIsPinned( bool f ) { mIsPinned = f; }
169  bool isPinned() const { return mIsPinned; }
170 
171  void setDefinedFont( QFont f ) { mDefinedFont = QFont( f ); }
172  QFont definedFont() { return mDefinedFont; }
173 
174  QFontMetricsF* getLabelFontMetrics() { return mFontMetrics; }
175 
176  void setDiagramAttributes( const QgsAttributes& attrs, const QgsFields* fields ) { mDiagramAttributes = attrs; mDiagramFields = fields; }
177  const QgsAttributes& diagramAttributes() { return mDiagramAttributes; }
178 
179  void feature( QgsFeature& feature )
180  {
181  feature.setFeatureId( mId );
182  feature.setFields( mDiagramFields, false );
183  feature.setAttributes( mDiagramAttributes );
184  feature.setValid( true );
185  }
186 
187  protected:
188  GEOSGeometry* mG;
189  QString mText;
190  QByteArray mStrId;
191  QgsFeatureId mId;
192  LabelInfo* mInfo;
193  bool mIsDiagram;
194  bool mIsPinned;
195  QFont mDefinedFont;
196  QFontMetricsF* mFontMetrics;
197  qreal mLetterSpacing; // for use with curved labels
198  qreal mWordSpacing; // for use with curved labels
199  bool mCurvedLabeling; // whether the geometry is to be used for curved labeling placement
201  QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > mDataDefinedValues;
202 
204  QgsAttributes mDiagramAttributes;
205  const QgsFields* mDiagramFields;
206 };
207 #endif //0
208 
209 // -------------
210 
212  : palLayer( NULL )
213  , mCurFeat( 0 )
214  , mCurFields( 0 )
215  , ct( NULL )
216  , extentGeom( NULL )
217  , mFeaturesToLabel( 0 )
218  , mFeatsSendingToPal( 0 )
219  , mFeatsRegPal( 0 )
220  , expression( NULL )
221 {
222  enabled = false;
223 
224  // text style
225  textFont = QApplication::font();
226  textNamedStyle = QString( "" );
227  fontSizeInMapUnits = false;
228  textColor = Qt::black;
229  textTransp = 0;
230  blendMode = QPainter::CompositionMode_SourceOver;
231  previewBkgrdColor = Qt::white;
232  // font processing info
233  mTextFontFound = true;
234  mTextFontFamily = QApplication::font().family();
235 
236  // text formatting
237  wrapChar = "";
238  multilineHeight = 1.0;
240  addDirectionSymbol = false;
241  leftDirectionSymbol = QString( "<" );
242  rightDirectionSymbol = QString( ">" );
243  reverseDirectionSymbol = false;
245  formatNumbers = false;
246  decimals = 3;
247  plusSign = false;
248 
249  // text buffer
250  bufferDraw = false;
251  bufferSize = 1.0;
252  bufferSizeInMapUnits = false;
253  bufferColor = Qt::white;
254  bufferTransp = 0;
255  bufferNoFill = false;
256  bufferJoinStyle = Qt::BevelJoin;
257  bufferBlendMode = QPainter::CompositionMode_SourceOver;
258 
259  // shape background
260  shapeDraw = false;
262  shapeSVGFile = QString();
264  shapeSize = QPointF( 0.0, 0.0 );
265  shapeSizeUnits = MM;
267  shapeRotation = 0.0;
268  shapeOffset = QPointF( 0.0, 0.0 );
270  shapeRadii = QPointF( 0.0, 0.0 );
272  shapeFillColor = Qt::white;
273  shapeBorderColor = Qt::darkGray;
274  shapeBorderWidth = 0.0;
276  shapeJoinStyle = Qt::BevelJoin;
277  shapeTransparency = 0;
278  shapeBlendMode = QPainter::CompositionMode_SourceOver;
279 
280  // drop shadow
281  shadowDraw = false;
283  shadowOffsetAngle = 135;
284  shadowOffsetDist = 1.0;
286  shadowOffsetGlobal = true;
287  shadowRadius = 1.5;
289  shadowRadiusAlphaOnly = false;
290  shadowTransparency = 30;
291  shadowScale = 100;
292  shadowColor = Qt::black;
293  shadowBlendMode = QPainter::CompositionMode_Multiply;
294 
295  // placement
297  placementFlags = 0;
298  centroidWhole = false;
300  xOffset = 0;
301  yOffset = 0;
302  labelOffsetInMapUnits = true;
303  dist = 0;
304  distInMapUnits = false;
305  angleOffset = 0;
306  preserveRotation = true;
307  maxCurvedCharAngleIn = 20.0;
308  maxCurvedCharAngleOut = -20.0;
309  priority = 5;
310 
311  // rendering
312  scaleVisibility = false;
313  scaleMin = 1;
314  scaleMax = 10000000;
315  fontLimitPixelSize = false;
316  fontMinPixelSize = 0; //trigger to turn it on by default for map unit labels
317  fontMaxPixelSize = 10000;
318  displayAll = false;
320 
321  labelPerPart = false;
322  mergeLines = false;
323  minFeatureSize = 0.0;
324  limitNumLabels = false;
325  maxNumLabels = 2000;
326  obstacle = true;
327 
328  // scale factors
329  vectorScaleFactor = 1.0;
330  rasterCompressFactor = 1.0;
331 
332  // data defined string and old-style index values
333  // NOTE: in QPair use -1 for second value (other values are for old-style layer properties migration)
334 
335  // text style
336  mDataDefinedNames.insert( Size, QPair<QString, int>( "Size", 0 ) );
337  mDataDefinedNames.insert( Bold, QPair<QString, int>( "Bold", 1 ) );
338  mDataDefinedNames.insert( Italic, QPair<QString, int>( "Italic", 2 ) );
339  mDataDefinedNames.insert( Underline, QPair<QString, int>( "Underline", 3 ) );
340  mDataDefinedNames.insert( Color, QPair<QString, int>( "Color", 4 ) );
341  mDataDefinedNames.insert( Strikeout, QPair<QString, int>( "Strikeout", 5 ) );
342  mDataDefinedNames.insert( Family, QPair<QString, int>( "Family", 6 ) );
343  mDataDefinedNames.insert( FontStyle, QPair<QString, int>( "FontStyle", -1 ) );
344  mDataDefinedNames.insert( FontSizeUnit, QPair<QString, int>( "FontSizeUnit", -1 ) );
345  mDataDefinedNames.insert( FontTransp, QPair<QString, int>( "FontTransp", 18 ) );
346  mDataDefinedNames.insert( FontCase, QPair<QString, int>( "FontCase", -1 ) );
347  mDataDefinedNames.insert( FontLetterSpacing, QPair<QString, int>( "FontLetterSpacing", -1 ) );
348  mDataDefinedNames.insert( FontWordSpacing, QPair<QString, int>( "FontWordSpacing", -1 ) );
349  mDataDefinedNames.insert( FontBlendMode, QPair<QString, int>( "FontBlendMode", -1 ) );
350 
351  // text formatting
352  mDataDefinedNames.insert( MultiLineWrapChar, QPair<QString, int>( "MultiLineWrapChar", -1 ) );
353  mDataDefinedNames.insert( MultiLineHeight, QPair<QString, int>( "MultiLineHeight", -1 ) );
354  mDataDefinedNames.insert( MultiLineAlignment, QPair<QString, int>( "MultiLineAlignment", -1 ) );
355  mDataDefinedNames.insert( DirSymbDraw, QPair<QString, int>( "DirSymbDraw", -1 ) );
356  mDataDefinedNames.insert( DirSymbLeft, QPair<QString, int>( "DirSymbLeft", -1 ) );
357  mDataDefinedNames.insert( DirSymbRight, QPair<QString, int>( "DirSymbRight", -1 ) );
358  mDataDefinedNames.insert( DirSymbPlacement, QPair<QString, int>( "DirSymbPlacement", -1 ) );
359  mDataDefinedNames.insert( DirSymbReverse, QPair<QString, int>( "DirSymbReverse", -1 ) );
360  mDataDefinedNames.insert( NumFormat, QPair<QString, int>( "NumFormat", -1 ) );
361  mDataDefinedNames.insert( NumDecimals, QPair<QString, int>( "NumDecimals", -1 ) );
362  mDataDefinedNames.insert( NumPlusSign, QPair<QString, int>( "NumPlusSign", -1 ) );
363 
364  // text buffer
365  mDataDefinedNames.insert( BufferDraw, QPair<QString, int>( "BufferDraw", -1 ) );
366  mDataDefinedNames.insert( BufferSize, QPair<QString, int>( "BufferSize", 7 ) );
367  mDataDefinedNames.insert( BufferUnit, QPair<QString, int>( "BufferUnit", -1 ) );
368  mDataDefinedNames.insert( BufferColor, QPair<QString, int>( "BufferColor", 8 ) );
369  mDataDefinedNames.insert( BufferTransp, QPair<QString, int>( "BufferTransp", 19 ) );
370  mDataDefinedNames.insert( BufferJoinStyle, QPair<QString, int>( "BufferJoinStyle", -1 ) );
371  mDataDefinedNames.insert( BufferBlendMode, QPair<QString, int>( "BufferBlendMode", -1 ) );
372 
373  // background
374  mDataDefinedNames.insert( ShapeDraw, QPair<QString, int>( "ShapeDraw", -1 ) );
375  mDataDefinedNames.insert( ShapeKind, QPair<QString, int>( "ShapeKind", -1 ) );
376  mDataDefinedNames.insert( ShapeSVGFile, QPair<QString, int>( "ShapeSVGFile", -1 ) );
377  mDataDefinedNames.insert( ShapeSizeType, QPair<QString, int>( "ShapeSizeType", -1 ) );
378  mDataDefinedNames.insert( ShapeSizeX, QPair<QString, int>( "ShapeSizeX", -1 ) );
379  mDataDefinedNames.insert( ShapeSizeY, QPair<QString, int>( "ShapeSizeY", -1 ) );
380  mDataDefinedNames.insert( ShapeSizeUnits, QPair<QString, int>( "ShapeSizeUnits", -1 ) );
381  mDataDefinedNames.insert( ShapeRotationType, QPair<QString, int>( "ShapeRotationType", -1 ) );
382  mDataDefinedNames.insert( ShapeRotation, QPair<QString, int>( "ShapeRotation", -1 ) );
383  mDataDefinedNames.insert( ShapeOffset, QPair<QString, int>( "ShapeOffset", -1 ) );
384  mDataDefinedNames.insert( ShapeOffsetUnits, QPair<QString, int>( "ShapeOffsetUnits", -1 ) );
385  mDataDefinedNames.insert( ShapeRadii, QPair<QString, int>( "ShapeRadii", -1 ) );
386  mDataDefinedNames.insert( ShapeRadiiUnits, QPair<QString, int>( "ShapeRadiiUnits", -1 ) );
387  mDataDefinedNames.insert( ShapeTransparency, QPair<QString, int>( "ShapeTransparency", -1 ) );
388  mDataDefinedNames.insert( ShapeBlendMode, QPair<QString, int>( "ShapeBlendMode", -1 ) );
389  mDataDefinedNames.insert( ShapeFillColor, QPair<QString, int>( "ShapeFillColor", -1 ) );
390  mDataDefinedNames.insert( ShapeBorderColor, QPair<QString, int>( "ShapeBorderColor", -1 ) );
391  mDataDefinedNames.insert( ShapeBorderWidth, QPair<QString, int>( "ShapeBorderWidth", -1 ) );
392  mDataDefinedNames.insert( ShapeBorderWidthUnits, QPair<QString, int>( "ShapeBorderWidthUnits", -1 ) );
393  mDataDefinedNames.insert( ShapeJoinStyle, QPair<QString, int>( "ShapeJoinStyle", -1 ) );
394 
395  // drop shadow
396  mDataDefinedNames.insert( ShadowDraw, QPair<QString, int>( "ShadowDraw", -1 ) );
397  mDataDefinedNames.insert( ShadowUnder, QPair<QString, int>( "ShadowUnder", -1 ) );
398  mDataDefinedNames.insert( ShadowOffsetAngle, QPair<QString, int>( "ShadowOffsetAngle", -1 ) );
399  mDataDefinedNames.insert( ShadowOffsetDist, QPair<QString, int>( "ShadowOffsetDist", -1 ) );
400  mDataDefinedNames.insert( ShadowOffsetUnits, QPair<QString, int>( "ShadowOffsetUnits", -1 ) );
401  mDataDefinedNames.insert( ShadowRadius, QPair<QString, int>( "ShadowRadius", -1 ) );
402  mDataDefinedNames.insert( ShadowRadiusUnits, QPair<QString, int>( "ShadowRadiusUnits", -1 ) );
403  mDataDefinedNames.insert( ShadowTransparency, QPair<QString, int>( "ShadowTransparency", -1 ) );
404  mDataDefinedNames.insert( ShadowScale, QPair<QString, int>( "ShadowScale", -1 ) );
405  mDataDefinedNames.insert( ShadowColor, QPair<QString, int>( "ShadowColor", -1 ) );
406  mDataDefinedNames.insert( ShadowBlendMode, QPair<QString, int>( "ShadowBlendMode", -1 ) );
407 
408  // placement
409  mDataDefinedNames.insert( CentroidWhole, QPair<QString, int>( "CentroidWhole", -1 ) );
410  mDataDefinedNames.insert( OffsetQuad, QPair<QString, int>( "OffsetQuad", -1 ) );
411  mDataDefinedNames.insert( OffsetXY, QPair<QString, int>( "OffsetXY", -1 ) );
412  mDataDefinedNames.insert( OffsetUnits, QPair<QString, int>( "OffsetUnits", -1 ) );
413  mDataDefinedNames.insert( LabelDistance, QPair<QString, int>( "LabelDistance", 13 ) );
414  mDataDefinedNames.insert( DistanceUnits, QPair<QString, int>( "DistanceUnits", -1 ) );
415  mDataDefinedNames.insert( OffsetRotation, QPair<QString, int>( "OffsetRotation", -1 ) );
416  mDataDefinedNames.insert( CurvedCharAngleInOut, QPair<QString, int>( "CurvedCharAngleInOut", -1 ) );
417  // (data defined only)
418  mDataDefinedNames.insert( PositionX, QPair<QString, int>( "PositionX", 9 ) );
419  mDataDefinedNames.insert( PositionY, QPair<QString, int>( "PositionY", 10 ) );
420  mDataDefinedNames.insert( Hali, QPair<QString, int>( "Hali", 11 ) );
421  mDataDefinedNames.insert( Vali, QPair<QString, int>( "Vali", 12 ) );
422  mDataDefinedNames.insert( Rotation, QPair<QString, int>( "Rotation", 14 ) );
423 
424  //rendering
425  mDataDefinedNames.insert( ScaleVisibility, QPair<QString, int>( "ScaleVisibility", -1 ) );
426  mDataDefinedNames.insert( MinScale, QPair<QString, int>( "MinScale", 16 ) );
427  mDataDefinedNames.insert( MaxScale, QPair<QString, int>( "MaxScale", 17 ) );
428  mDataDefinedNames.insert( FontLimitPixel, QPair<QString, int>( "FontLimitPixel", -1 ) );
429  mDataDefinedNames.insert( FontMinPixel, QPair<QString, int>( "FontMinPixel", -1 ) );
430  mDataDefinedNames.insert( FontMaxPixel, QPair<QString, int>( "FontMaxPixel", -1 ) );
431  // (data defined only)
432  mDataDefinedNames.insert( Show, QPair<QString, int>( "Show", 15 ) );
433  mDataDefinedNames.insert( AlwaysShow, QPair<QString, int>( "AlwaysShow", 20 ) );
434 
435  // temp stuff for when drawing label components (don't copy)
436  showingShadowRects = false;
437 }
438 
440 {
441  // copy only permanent stuff
442 
443  enabled = s.enabled;
444 
445  // text style
446  fieldName = s.fieldName;
448  textFont = s.textFont;
451  textColor = s.textColor;
453  blendMode = s.blendMode;
455  // font processing info
458 
459  // text formatting
460  wrapChar = s.wrapChar;
469  decimals = s.decimals;
470  plusSign = s.plusSign;
471 
472  // text buffer
481 
482  // placement
483  placement = s.placement;
487  xOffset = s.xOffset;
488  yOffset = s.yOffset;
490  dist = s.dist;
496  priority = s.priority;
497 
498  // rendering
500  scaleMin = s.scaleMin;
501  scaleMax = s.scaleMax;
507 
513  obstacle = s.obstacle;
514 
515  // shape background
516  shapeDraw = s.shapeDraw;
517  shapeType = s.shapeType;
520  shapeSize = s.shapeSize;
535 
536  // drop shadow
550 
551  // data defined
554 
555  // scale factors
558 
559  ct = NULL;
560  extentGeom = NULL;
561  expression = NULL;
562 }
563 
564 
566 {
567  // pal layer is deleted internally in PAL
568 
569  delete ct;
570  delete expression;
571  delete extentGeom;
572 
573  // clear pointers to QgsDataDefined objects
574  dataDefinedProperties.clear();
575 }
576 
578 {
579  if ( expression == NULL )
580  {
582  }
583  return expression;
584 }
585 
586 static QColor _readColor( QgsVectorLayer* layer, QString property, QColor defaultColor = Qt::black, bool withAlpha = true )
587 {
588  int r = layer->customProperty( property + "R", QVariant( defaultColor.red() ) ).toInt();
589  int g = layer->customProperty( property + "G", QVariant( defaultColor.green() ) ).toInt();
590  int b = layer->customProperty( property + "B", QVariant( defaultColor.blue() ) ).toInt();
591  int a = withAlpha ? layer->customProperty( property + "A", QVariant( defaultColor.alpha() ) ).toInt() : 255;
592  return QColor( r, g, b, a );
593 }
594 
595 static void _writeColor( QgsVectorLayer* layer, QString property, QColor color, bool withAlpha = true )
596 {
597  layer->setCustomProperty( property + "R", color.red() );
598  layer->setCustomProperty( property + "G", color.green() );
599  layer->setCustomProperty( property + "B", color.blue() );
600  if ( withAlpha )
601  layer->setCustomProperty( property + "A", color.alpha() );
602 }
603 
604 static QgsPalLayerSettings::SizeUnit _decodeUnits( const QString& str )
605 {
606  if ( str.compare( "Point", Qt::CaseInsensitive ) == 0
607  || str.compare( "Points", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Points;
608  if ( str.compare( "MapUnit", Qt::CaseInsensitive ) == 0
609  || str.compare( "MapUnits", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::MapUnits;
610  if ( str.compare( "Percent", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Percent;
611  return QgsPalLayerSettings::MM; // "MM"
612 }
613 
614 static QPainter::CompositionMode _decodeBlendMode( const QString& str )
615 {
616  if ( str.compare( "Lighten", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Lighten;
617  if ( str.compare( "Screen", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Screen;
618  if ( str.compare( "Dodge", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_ColorDodge;
619  if ( str.compare( "Addition", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Plus;
620  if ( str.compare( "Darken", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Darken;
621  if ( str.compare( "Multiply", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Multiply;
622  if ( str.compare( "Burn", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_ColorBurn;
623  if ( str.compare( "Overlay", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Overlay;
624  if ( str.compare( "SoftLight", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_SoftLight;
625  if ( str.compare( "HardLight", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_HardLight;
626  if ( str.compare( "Difference", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Difference;
627  if ( str.compare( "Subtract", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Exclusion;
628  return QPainter::CompositionMode_SourceOver; // "Normal"
629 }
630 
631 static Qt::PenJoinStyle _decodePenJoinStyle( const QString& str )
632 {
633  if ( str.compare( "Miter", Qt::CaseInsensitive ) == 0 ) return Qt::MiterJoin;
634  if ( str.compare( "Round", Qt::CaseInsensitive ) == 0 ) return Qt::RoundJoin;
635  return Qt::BevelJoin; // "Bevel"
636 }
637 
639  QMap < QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* > & propertyMap )
640 {
641  if ( !layer )
642  {
643  return;
644  }
645 
646  QMapIterator<QgsPalLayerSettings::DataDefinedProperties, QPair<QString, int> > i( mDataDefinedNames );
647  while ( i.hasNext() )
648  {
649  i.next();
650  readDataDefinedProperty( layer, i.key(), propertyMap );
651  }
652 }
653 
655  const QMap < QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* > & propertyMap )
656 {
657  if ( !layer )
658  {
659  return;
660  }
661 
662  QMapIterator<QgsPalLayerSettings::DataDefinedProperties, QPair<QString, int> > i( mDataDefinedNames );
663  while ( i.hasNext() )
664  {
665  i.next();
666  QString newPropertyName = "labeling/dataDefined/" + i.value().first;
667  QVariant propertyValue = QVariant();
668 
669  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator it = propertyMap.find( i.key() );
670  if ( it != propertyMap.constEnd() )
671  {
672  QgsDataDefined* dd = it.value();
673  if ( dd )
674  {
675  bool active = dd->isActive();
676  bool useExpr = dd->useExpression();
677  QString expr = dd->expressionString();
678  QString field = dd->field();
679 
680  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
681 
682  if ( !defaultVals )
683  {
684  // TODO: update this when project settings for labeling are migrated to better XML layout
685  QStringList values;
686  values << ( active ? "1" : "0" );
687  values << ( useExpr ? "1" : "0" );
688  values << expr;
689  values << field;
690  if ( !values.isEmpty() )
691  {
692  propertyValue = QVariant( values.join( "~~" ) );
693  }
694  }
695  }
696  }
697 
698  if ( propertyValue.isValid() )
699  {
700  layer->setCustomProperty( newPropertyName, propertyValue );
701  }
702  else
703  {
704  // remove unused properties
705  layer->removeCustomProperty( newPropertyName );
706  }
707 
708  if ( layer->customProperty( newPropertyName, QVariant() ).isValid() && i.value().second > -1 )
709  {
710  // remove old-style field index-based property, if still present
711  layer->removeCustomProperty( QString( "labeling/dataDefinedProperty" ) + QString::number( i.value().second ) );
712  }
713  }
714 }
715 
718  QMap < QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* > & propertyMap )
719 {
720  QString newPropertyName = "labeling/dataDefined/" + mDataDefinedNames.value( p ).first;
721  QVariant newPropertyField = layer->customProperty( newPropertyName, QVariant() );
722 
723  QString ddString = QString();
724  if ( newPropertyField.isValid() )
725  {
726  ddString = newPropertyField.toString();
727  }
728  else // maybe working with old-style field index-based property (< QGIS 2.0)
729  {
730  int oldIndx = mDataDefinedNames.value( p ).second;
731 
732  if ( oldIndx < 0 ) // something went wrong and we are working with new-style
733  {
734  return;
735  }
736 
737  QString oldPropertyName = "labeling/dataDefinedProperty" + QString::number( oldIndx );
738  QVariant oldPropertyField = layer->customProperty( oldPropertyName, QVariant() );
739 
740  if ( !oldPropertyField.isValid() )
741  {
742  return;
743  }
744 
745  // switch from old-style field index- to name-based properties
746  bool conversionOk;
747  int indx = oldPropertyField.toInt( &conversionOk );
748 
749  if ( conversionOk )
750  {
751  // Fix to migrate from old-style vector api, where returned QMap keys possibly
752  // had 'holes' in sequence of field indices, e.g. 0,2,3
753  // QgsAttrPalIndexNameHash provides a means of access field name in sequences from
754  // providers that procuded holes (e.g. PostGIS skipped geom column), otherwise it is empty
755  QgsAttrPalIndexNameHash oldIndicesToNames = layer->dataProvider()->palAttributeIndexNames();
756 
757  if ( !oldIndicesToNames.isEmpty() )
758  {
759  ddString = oldIndicesToNames.value( indx );
760  }
761  else
762  {
763  QgsFields fields = layer->dataProvider()->fields();
764  if ( indx < fields.size() ) // in case field count has changed
765  {
766  ddString = fields.at( indx ).name();
767  }
768  }
769  }
770 
771  if ( !ddString.isEmpty() )
772  {
773  //upgrade any existing property to field name-based
774  layer->setCustomProperty( newPropertyName, QVariant( updateDataDefinedString( ddString ) ) );
775 
776  // fix for buffer drawing triggered off of just its data defined size in the past (<2.0)
777  if ( oldIndx == 7 ) // old bufferSize enum
778  {
779  bufferDraw = true;
780  layer->setCustomProperty( "labeling/bufferDraw", true );
781  }
782 
783  // fix for scale visibility limits triggered off of just its data defined values in the past (<2.0)
784  if ( oldIndx == 16 || oldIndx == 17 ) // old minScale and maxScale enums
785  {
786  scaleVisibility = true;
787  layer->setCustomProperty( "labeling/scaleVisibility", true );
788  }
789  }
790 
791  // remove old-style field index-based property
792  layer->removeCustomProperty( oldPropertyName );
793  }
794 
795  if ( !ddString.isEmpty() && ddString != QString( "0~~0~~~~" ) )
796  {
797  // TODO: update this when project settings for labeling are migrated to better XML layout
798  QString newStyleString = updateDataDefinedString( ddString );
799  QStringList ddv = newStyleString.split( "~~" );
800 
801  QgsDataDefined* dd = new QgsDataDefined( ddv.at( 0 ).toInt(), ddv.at( 1 ).toInt(), ddv.at( 2 ), ddv.at( 3 ) );
802  propertyMap.insert( p, dd );
803  }
804  else
805  {
806  // remove unused properties
807  layer->removeCustomProperty( newPropertyName );
808  }
809 }
810 
812 {
813  if ( layer->customProperty( "labeling" ).toString() != QString( "pal" ) )
814  return; // there's no information available
815 
816  // NOTE: set defaults for newly added properties, for backwards compatibility
817 
818  enabled = layer->customProperty( "labeling/enabled" ).toBool();
819 
820  // text style
821  fieldName = layer->customProperty( "labeling/fieldName" ).toString();
822  isExpression = layer->customProperty( "labeling/isExpression" ).toBool();
823  QFont appFont = QApplication::font();
824  mTextFontFamily = layer->customProperty( "labeling/fontFamily", QVariant( appFont.family() ) ).toString();
825  QString fontFamily = mTextFontFamily;
827  {
828  // trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
829  mTextFontFound = false;
830 
831  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
832  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
833 
834  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
835  fontFamily = appFont.family();
836  }
837 
838  double fontSize = layer->customProperty( "labeling/fontSize" ).toDouble();
839  fontSizeInMapUnits = layer->customProperty( "labeling/fontSizeInMapUnits" ).toBool();
840  int fontWeight = layer->customProperty( "labeling/fontWeight" ).toInt();
841  bool fontItalic = layer->customProperty( "labeling/fontItalic" ).toBool();
842  textFont = QFont( fontFamily, fontSize, fontWeight, fontItalic );
843  textFont.setPointSizeF( fontSize ); //double precision needed because of map units
844  textNamedStyle = layer->customProperty( "labeling/namedStyle", QVariant( "" ) ).toString();
845  QgsFontUtils::updateFontViaStyle( textFont, textNamedStyle ); // must come after textFont.setPointSizeF()
846  textFont.setCapitalization(( QFont::Capitalization )layer->customProperty( "labeling/fontCapitals", QVariant( 0 ) ).toUInt() );
847  textFont.setUnderline( layer->customProperty( "labeling/fontUnderline" ).toBool() );
848  textFont.setStrikeOut( layer->customProperty( "labeling/fontStrikeout" ).toBool() );
849  textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( "labeling/fontLetterSpacing", QVariant( 0.0 ) ).toDouble() );
850  textFont.setWordSpacing( layer->customProperty( "labeling/fontWordSpacing", QVariant( 0.0 ) ).toDouble() );
851  textColor = _readColor( layer, "labeling/textColor", Qt::black, false );
852  textTransp = layer->customProperty( "labeling/textTransp" ).toInt();
854  ( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/blendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() );
855  previewBkgrdColor = QColor( layer->customProperty( "labeling/previewBkgrdColor", QVariant( "#ffffff" ) ).toString() );
856 
857 
858  // text formatting
859  wrapChar = layer->customProperty( "labeling/wrapChar" ).toString();
860  multilineHeight = layer->customProperty( "labeling/multilineHeight", QVariant( 1.0 ) ).toDouble();
861  multilineAlign = ( MultiLineAlign )layer->customProperty( "labeling/multilineAlign", QVariant( MultiLeft ) ).toUInt();
862  addDirectionSymbol = layer->customProperty( "labeling/addDirectionSymbol" ).toBool();
863  leftDirectionSymbol = layer->customProperty( "labeling/leftDirectionSymbol", QVariant( "<" ) ).toString();
864  rightDirectionSymbol = layer->customProperty( "labeling/rightDirectionSymbol", QVariant( ">" ) ).toString();
865  reverseDirectionSymbol = layer->customProperty( "labeling/reverseDirectionSymbol" ).toBool();
866  placeDirectionSymbol = ( DirectionSymbols )layer->customProperty( "labeling/placeDirectionSymbol", QVariant( SymbolLeftRight ) ).toUInt();
867  formatNumbers = layer->customProperty( "labeling/formatNumbers" ).toBool();
868  decimals = layer->customProperty( "labeling/decimals" ).toInt();
869  plusSign = layer->customProperty( "labeling/plussign" ).toInt();
870 
871  // text buffer
872  double bufSize = layer->customProperty( "labeling/bufferSize", QVariant( 0.0 ) ).toDouble();
873 
874  // fix for buffer being keyed off of just its size in the past (<2.0)
875  QVariant drawBuffer = layer->customProperty( "labeling/bufferDraw", QVariant() );
876  if ( drawBuffer.isValid() )
877  {
878  bufferDraw = drawBuffer.toBool();
879  bufferSize = bufSize;
880  }
881  else if ( bufSize != 0.0 )
882  {
883  bufferDraw = true;
884  bufferSize = bufSize;
885  }
886  else
887  {
888  // keep bufferSize at new 1.0 default
889  bufferDraw = false;
890  }
891 
892  bufferSizeInMapUnits = layer->customProperty( "labeling/bufferSizeInMapUnits" ).toBool();
893  bufferColor = _readColor( layer, "labeling/bufferColor", Qt::white, false );
894  bufferTransp = layer->customProperty( "labeling/bufferTransp" ).toInt();
896  ( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/bufferBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() );
897  bufferJoinStyle = ( Qt::PenJoinStyle )layer->customProperty( "labeling/bufferJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt();
898  bufferNoFill = layer->customProperty( "labeling/bufferNoFill", QVariant( false ) ).toBool();
899 
900  // background
901  shapeDraw = layer->customProperty( "labeling/shapeDraw", QVariant( false ) ).toBool();
902  shapeType = ( ShapeType )layer->customProperty( "labeling/shapeType", QVariant( ShapeRectangle ) ).toUInt();
903  shapeSVGFile = layer->customProperty( "labeling/shapeSVGFile", QVariant( "" ) ).toString();
904  shapeSizeType = ( SizeType )layer->customProperty( "labeling/shapeSizeType", QVariant( SizeBuffer ) ).toUInt();
905  shapeSize = QPointF( layer->customProperty( "labeling/shapeSizeX", QVariant( 0.0 ) ).toDouble(),
906  layer->customProperty( "labeling/shapeSizeY", QVariant( 0.0 ) ).toDouble() );
907  shapeSizeUnits = ( SizeUnit )layer->customProperty( "labeling/shapeSizeUnits", QVariant( MM ) ).toUInt();
908  shapeRotationType = ( RotationType )layer->customProperty( "labeling/shapeRotationType", QVariant( RotationSync ) ).toUInt();
909  shapeRotation = layer->customProperty( "labeling/shapeRotation", QVariant( 0.0 ) ).toDouble();
910  shapeOffset = QPointF( layer->customProperty( "labeling/shapeOffsetX", QVariant( 0.0 ) ).toDouble(),
911  layer->customProperty( "labeling/shapeOffsetY", QVariant( 0.0 ) ).toDouble() );
912  shapeOffsetUnits = ( SizeUnit )layer->customProperty( "labeling/shapeOffsetUnits", QVariant( MM ) ).toUInt();
913  shapeRadii = QPointF( layer->customProperty( "labeling/shapeRadiiX", QVariant( 0.0 ) ).toDouble(),
914  layer->customProperty( "labeling/shapeRadiiY", QVariant( 0.0 ) ).toDouble() );
915  shapeRadiiUnits = ( SizeUnit )layer->customProperty( "labeling/shapeRadiiUnits", QVariant( MM ) ).toUInt();
916  shapeFillColor = _readColor( layer, "labeling/shapeFillColor", Qt::white, true );
917  shapeBorderColor = _readColor( layer, "labeling/shapeBorderColor", Qt::darkGray, true );
918  shapeBorderWidth = layer->customProperty( "labeling/shapeBorderWidth", QVariant( .0 ) ).toDouble();
919  shapeBorderWidthUnits = ( SizeUnit )layer->customProperty( "labeling/shapeBorderWidthUnits", QVariant( MM ) ).toUInt();
920  shapeJoinStyle = ( Qt::PenJoinStyle )layer->customProperty( "labeling/shapeJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt();
921  shapeTransparency = layer->customProperty( "labeling/shapeTransparency", QVariant( 0 ) ).toInt();
923  ( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/shapeBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() );
924 
925  // drop shadow
926  shadowDraw = layer->customProperty( "labeling/shadowDraw", QVariant( false ) ).toBool();
927  shadowUnder = ( ShadowType )layer->customProperty( "labeling/shadowUnder", QVariant( ShadowLowest ) ).toUInt();//ShadowLowest;
928  shadowOffsetAngle = layer->customProperty( "labeling/shadowOffsetAngle", QVariant( 135 ) ).toInt();
929  shadowOffsetDist = layer->customProperty( "labeling/shadowOffsetDist", QVariant( 1.0 ) ).toDouble();
930  shadowOffsetUnits = ( SizeUnit )layer->customProperty( "labeling/shadowOffsetUnits", QVariant( MM ) ).toUInt();
931  shadowOffsetGlobal = layer->customProperty( "labeling/shadowOffsetGlobal", QVariant( true ) ).toBool();
932  shadowRadius = layer->customProperty( "labeling/shadowRadius", QVariant( 1.5 ) ).toDouble();
933  shadowRadiusUnits = ( SizeUnit )layer->customProperty( "labeling/shadowRadiusUnits", QVariant( MM ) ).toUInt();
934  shadowRadiusAlphaOnly = layer->customProperty( "labeling/shadowRadiusAlphaOnly", QVariant( false ) ).toBool();
935  shadowTransparency = layer->customProperty( "labeling/shadowTransparency", QVariant( 30 ) ).toInt();
936  shadowScale = layer->customProperty( "labeling/shadowScale", QVariant( 100 ) ).toInt();
937  shadowColor = _readColor( layer, "labeling/shadowColor", Qt::black, false );
939  ( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/shadowBlendMode", QVariant( QgsMapRenderer::BlendMultiply ) ).toUInt() );
940 
941  // placement
942  placement = ( Placement )layer->customProperty( "labeling/placement" ).toInt();
943  placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt();
944  centroidWhole = layer->customProperty( "labeling/centroidWhole", QVariant( false ) ).toBool();
945  dist = layer->customProperty( "labeling/dist" ).toDouble();
946  distInMapUnits = layer->customProperty( "labeling/distInMapUnits" ).toBool();
947  quadOffset = ( QuadrantPosition )layer->customProperty( "labeling/quadOffset", QVariant( QuadrantOver ) ).toUInt();
948  xOffset = layer->customProperty( "labeling/xOffset", QVariant( 0.0 ) ).toDouble();
949  yOffset = layer->customProperty( "labeling/yOffset", QVariant( 0.0 ) ).toDouble();
950  labelOffsetInMapUnits = layer->customProperty( "labeling/labelOffsetInMapUnits", QVariant( true ) ).toBool();
951  angleOffset = layer->customProperty( "labeling/angleOffset", QVariant( 0.0 ) ).toDouble();
952  preserveRotation = layer->customProperty( "labeling/preserveRotation", QVariant( true ) ).toBool();
953  maxCurvedCharAngleIn = layer->customProperty( "labeling/maxCurvedCharAngleIn", QVariant( 20.0 ) ).toDouble();
954  maxCurvedCharAngleOut = layer->customProperty( "labeling/maxCurvedCharAngleOut", QVariant( -20.0 ) ).toDouble();
955  priority = layer->customProperty( "labeling/priority" ).toInt();
956 
957  // rendering
958  int scalemn = layer->customProperty( "labeling/scaleMin", QVariant( 0 ) ).toInt();
959  int scalemx = layer->customProperty( "labeling/scaleMax", QVariant( 0 ) ).toInt();
960 
961  // fix for scale visibility limits being keyed off of just its values in the past (<2.0)
962  QVariant scalevis = layer->customProperty( "labeling/scaleVisibility", QVariant() );
963  if ( scalevis.isValid() )
964  {
965  scaleVisibility = scalevis.toBool();
966  scaleMin = scalemn;
967  scaleMax = scalemx;
968  }
969  else if ( scalemn > 0 || scalemx > 0 )
970  {
971  scaleVisibility = true;
972  scaleMin = scalemn;
973  scaleMax = scalemx;
974  }
975  else
976  {
977  // keep scaleMin and scaleMax at new 1.0 defaults (1 and 10000000, were 0 and 0)
978  scaleVisibility = false;
979  }
980 
981 
982  fontLimitPixelSize = layer->customProperty( "labeling/fontLimitPixelSize", QVariant( false ) ).toBool();
983  fontMinPixelSize = layer->customProperty( "labeling/fontMinPixelSize", QVariant( 0 ) ).toInt();
984  fontMaxPixelSize = layer->customProperty( "labeling/fontMaxPixelSize", QVariant( 10000 ) ).toInt();
985  displayAll = layer->customProperty( "labeling/displayAll", QVariant( false ) ).toBool();
986  upsidedownLabels = ( UpsideDownLabels )layer->customProperty( "labeling/upsidedownLabels", QVariant( Upright ) ).toUInt();
987 
988  labelPerPart = layer->customProperty( "labeling/labelPerPart" ).toBool();
989  mergeLines = layer->customProperty( "labeling/mergeLines" ).toBool();
990  minFeatureSize = layer->customProperty( "labeling/minFeatureSize" ).toDouble();
991  limitNumLabels = layer->customProperty( "labeling/limitNumLabels", QVariant( false ) ).toBool();
992  maxNumLabels = layer->customProperty( "labeling/maxNumLabels", QVariant( 2000 ) ).toInt();
993  obstacle = layer->customProperty( "labeling/obstacle", QVariant( true ) ).toBool();
994 
996 }
997 
999 {
1000  // this is a mark that labeling information is present
1001  layer->setCustomProperty( "labeling", "pal" );
1002 
1003  layer->setCustomProperty( "labeling/enabled", enabled );
1004 
1005  // text style
1006  layer->setCustomProperty( "labeling/fieldName", fieldName );
1007  layer->setCustomProperty( "labeling/isExpression", isExpression );
1008  layer->setCustomProperty( "labeling/fontFamily", textFont.family() );
1009  layer->setCustomProperty( "labeling/namedStyle", textNamedStyle );
1010  layer->setCustomProperty( "labeling/fontSize", textFont.pointSizeF() );
1011  layer->setCustomProperty( "labeling/fontSizeInMapUnits", fontSizeInMapUnits );
1012  layer->setCustomProperty( "labeling/fontWeight", textFont.weight() );
1013  layer->setCustomProperty( "labeling/fontItalic", textFont.italic() );
1014  layer->setCustomProperty( "labeling/fontBold", textFont.bold() );
1015  layer->setCustomProperty( "labeling/fontStrikeout", textFont.strikeOut() );
1016  layer->setCustomProperty( "labeling/fontUnderline", textFont.underline() );
1017  _writeColor( layer, "labeling/textColor", textColor );
1018  layer->setCustomProperty( "labeling/fontCapitals", ( unsigned int )textFont.capitalization() );
1019  layer->setCustomProperty( "labeling/fontLetterSpacing", textFont.letterSpacing() );
1020  layer->setCustomProperty( "labeling/fontWordSpacing", textFont.wordSpacing() );
1021  layer->setCustomProperty( "labeling/textTransp", textTransp );
1022  layer->setCustomProperty( "labeling/blendMode", QgsMapRenderer::getBlendModeEnum( blendMode ) );
1023  layer->setCustomProperty( "labeling/previewBkgrdColor", previewBkgrdColor.name() );
1024 
1025  // text formatting
1026  layer->setCustomProperty( "labeling/wrapChar", wrapChar );
1027  layer->setCustomProperty( "labeling/multilineHeight", multilineHeight );
1028  layer->setCustomProperty( "labeling/multilineAlign", ( unsigned int )multilineAlign );
1029  layer->setCustomProperty( "labeling/addDirectionSymbol", addDirectionSymbol );
1030  layer->setCustomProperty( "labeling/leftDirectionSymbol", leftDirectionSymbol );
1031  layer->setCustomProperty( "labeling/rightDirectionSymbol", rightDirectionSymbol );
1032  layer->setCustomProperty( "labeling/reverseDirectionSymbol", reverseDirectionSymbol );
1033  layer->setCustomProperty( "labeling/placeDirectionSymbol", ( unsigned int )placeDirectionSymbol );
1034  layer->setCustomProperty( "labeling/formatNumbers", formatNumbers );
1035  layer->setCustomProperty( "labeling/decimals", decimals );
1036  layer->setCustomProperty( "labeling/plussign", plusSign );
1037 
1038  // text buffer
1039  layer->setCustomProperty( "labeling/bufferDraw", bufferDraw );
1040  layer->setCustomProperty( "labeling/bufferSize", bufferSize );
1041  layer->setCustomProperty( "labeling/bufferSizeInMapUnits", bufferSizeInMapUnits );
1042  _writeColor( layer, "labeling/bufferColor", bufferColor );
1043  layer->setCustomProperty( "labeling/bufferNoFill", bufferNoFill );
1044  layer->setCustomProperty( "labeling/bufferTransp", bufferTransp );
1045  layer->setCustomProperty( "labeling/bufferJoinStyle", ( unsigned int )bufferJoinStyle );
1046  layer->setCustomProperty( "labeling/bufferBlendMode", QgsMapRenderer::getBlendModeEnum( bufferBlendMode ) );
1047 
1048  // background
1049  layer->setCustomProperty( "labeling/shapeDraw", shapeDraw );
1050  layer->setCustomProperty( "labeling/shapeType", ( unsigned int )shapeType );
1051  layer->setCustomProperty( "labeling/shapeSVGFile", shapeSVGFile );
1052  layer->setCustomProperty( "labeling/shapeSizeType", ( unsigned int )shapeSizeType );
1053  layer->setCustomProperty( "labeling/shapeSizeX", shapeSize.x() );
1054  layer->setCustomProperty( "labeling/shapeSizeY", shapeSize.y() );
1055  layer->setCustomProperty( "labeling/shapeSizeUnits", ( unsigned int )shapeSizeUnits );
1056  layer->setCustomProperty( "labeling/shapeRotationType", ( unsigned int )shapeRotationType );
1057  layer->setCustomProperty( "labeling/shapeRotation", shapeRotation );
1058  layer->setCustomProperty( "labeling/shapeOffsetX", shapeOffset.x() );
1059  layer->setCustomProperty( "labeling/shapeOffsetY", shapeOffset.y() );
1060  layer->setCustomProperty( "labeling/shapeOffsetUnits", ( unsigned int )shapeOffsetUnits );
1061  layer->setCustomProperty( "labeling/shapeRadiiX", shapeRadii.x() );
1062  layer->setCustomProperty( "labeling/shapeRadiiY", shapeRadii.y() );
1063  layer->setCustomProperty( "labeling/shapeRadiiUnits", ( unsigned int )shapeRadiiUnits );
1064  _writeColor( layer, "labeling/shapeFillColor", shapeFillColor, true );
1065  _writeColor( layer, "labeling/shapeBorderColor", shapeBorderColor, true );
1066  layer->setCustomProperty( "labeling/shapeBorderWidth", shapeBorderWidth );
1067  layer->setCustomProperty( "labeling/shapeBorderWidthUnits", ( unsigned int )shapeBorderWidthUnits );
1068  layer->setCustomProperty( "labeling/shapeJoinStyle", ( unsigned int )shapeJoinStyle );
1069  layer->setCustomProperty( "labeling/shapeTransparency", shapeTransparency );
1070  layer->setCustomProperty( "labeling/shapeBlendMode", QgsMapRenderer::getBlendModeEnum( shapeBlendMode ) );
1071 
1072  // drop shadow
1073  layer->setCustomProperty( "labeling/shadowDraw", shadowDraw );
1074  layer->setCustomProperty( "labeling/shadowUnder", ( unsigned int )shadowUnder );
1075  layer->setCustomProperty( "labeling/shadowOffsetAngle", shadowOffsetAngle );
1076  layer->setCustomProperty( "labeling/shadowOffsetDist", shadowOffsetDist );
1077  layer->setCustomProperty( "labeling/shadowOffsetUnits", ( unsigned int )shadowOffsetUnits );
1078  layer->setCustomProperty( "labeling/shadowOffsetGlobal", shadowOffsetGlobal );
1079  layer->setCustomProperty( "labeling/shadowRadius", shadowRadius );
1080  layer->setCustomProperty( "labeling/shadowRadiusUnits", ( unsigned int )shadowRadiusUnits );
1081  layer->setCustomProperty( "labeling/shadowRadiusAlphaOnly", shadowRadiusAlphaOnly );
1082  layer->setCustomProperty( "labeling/shadowTransparency", shadowTransparency );
1083  layer->setCustomProperty( "labeling/shadowScale", shadowScale );
1084  _writeColor( layer, "labeling/shadowColor", shadowColor, false );
1085  layer->setCustomProperty( "labeling/shadowBlendMode", QgsMapRenderer::getBlendModeEnum( shadowBlendMode ) );
1086 
1087  // placement
1088  layer->setCustomProperty( "labeling/placement", placement );
1089  layer->setCustomProperty( "labeling/placementFlags", ( unsigned int )placementFlags );
1090  layer->setCustomProperty( "labeling/centroidWhole", centroidWhole );
1091  layer->setCustomProperty( "labeling/dist", dist );
1092  layer->setCustomProperty( "labeling/distInMapUnits", distInMapUnits );
1093  layer->setCustomProperty( "labeling/quadOffset", ( unsigned int )quadOffset );
1094  layer->setCustomProperty( "labeling/xOffset", xOffset );
1095  layer->setCustomProperty( "labeling/yOffset", yOffset );
1096  layer->setCustomProperty( "labeling/labelOffsetInMapUnits", labelOffsetInMapUnits );
1097  layer->setCustomProperty( "labeling/angleOffset", angleOffset );
1098  layer->setCustomProperty( "labeling/preserveRotation", preserveRotation );
1099  layer->setCustomProperty( "labeling/maxCurvedCharAngleIn", maxCurvedCharAngleIn );
1100  layer->setCustomProperty( "labeling/maxCurvedCharAngleOut", maxCurvedCharAngleOut );
1101  layer->setCustomProperty( "labeling/priority", priority );
1102 
1103  // rendering
1104  layer->setCustomProperty( "labeling/scaleVisibility", scaleVisibility );
1105  layer->setCustomProperty( "labeling/scaleMin", scaleMin );
1106  layer->setCustomProperty( "labeling/scaleMax", scaleMax );
1107  layer->setCustomProperty( "labeling/fontLimitPixelSize", fontLimitPixelSize );
1108  layer->setCustomProperty( "labeling/fontMinPixelSize", fontMinPixelSize );
1109  layer->setCustomProperty( "labeling/fontMaxPixelSize", fontMaxPixelSize );
1110  layer->setCustomProperty( "labeling/displayAll", displayAll );
1111  layer->setCustomProperty( "labeling/upsidedownLabels", ( unsigned int )upsidedownLabels );
1112 
1113  layer->setCustomProperty( "labeling/labelPerPart", labelPerPart );
1114  layer->setCustomProperty( "labeling/mergeLines", mergeLines );
1115  layer->setCustomProperty( "labeling/minFeatureSize", minFeatureSize );
1116  layer->setCustomProperty( "labeling/limitNumLabels", limitNumLabels );
1117  layer->setCustomProperty( "labeling/maxNumLabels", maxNumLabels );
1118  layer->setCustomProperty( "labeling/obstacle", obstacle );
1119 
1121 }
1122 
1124  bool active, bool useExpr, const QString& expr, const QString& field )
1125 {
1126  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
1127 
1128  if ( dataDefinedProperties.contains( p ) )
1129  {
1130  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
1131  if ( it != dataDefinedProperties.constEnd() )
1132  {
1133  QgsDataDefined* dd = it.value();
1134  dd->setActive( active );
1135  dd->setUseExpression( useExpr );
1136  dd->setExpressionString( expr );
1137  dd->setField( field );
1138  }
1139  }
1140  else if ( !defaultVals )
1141  {
1142  QgsDataDefined* dd = new QgsDataDefined( active, useExpr, expr, field );
1143  dataDefinedProperties.insert( p, dd );
1144  }
1145 }
1146 
1148 {
1149  QMap< DataDefinedProperties, QgsDataDefined* >::iterator it = dataDefinedProperties.find( p );
1150  if ( it != dataDefinedProperties.end() )
1151  {
1152  delete( it.value() );
1153  dataDefinedProperties.erase( it );
1154  }
1155 }
1156 
1157 QString QgsPalLayerSettings::updateDataDefinedString( const QString& value )
1158 {
1159  // TODO: update or remove this when project settings for labeling are migrated to better XML layout
1160  QString newValue = value;
1161  if ( !value.isEmpty() && !value.contains( "~~" ) )
1162  {
1163  QStringList values;
1164  values << "1"; // all old-style values are active if not empty
1165  values << "0";
1166  values << "";
1167  values << value; // all old-style values are only field names
1168  newValue = values.join( "~~" );
1169  }
1170 
1171  return newValue;
1172 }
1173 
1175 {
1176  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
1177  if ( it != dataDefinedProperties.constEnd() )
1178  {
1179  return it.value();
1180  }
1181  return 0;
1182 }
1183 
1185 {
1186  QMap<QString, QString> map;
1187  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
1188  if ( it != dataDefinedProperties.constEnd() )
1189  {
1190  return it.value()->toMap();
1191  }
1192  return map;
1193 }
1194 
1196 {
1197  if ( !dataDefinedProperties.contains( p ) )
1198  {
1199  return QVariant();
1200  }
1201 
1202  QgsDataDefined* dd = 0;
1203  QMap< DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
1204  if ( it != dataDefinedProperties.constEnd() )
1205  {
1206  dd = it.value();
1207  }
1208 
1209  if ( !dd )
1210  {
1211  return QVariant();
1212  }
1213 
1214  if ( !dd->isActive() )
1215  {
1216  return QVariant();
1217  }
1218 
1219  QVariant result = QVariant();
1220  bool useExpression = dd->useExpression();
1221  QString field = dd->field();
1222 
1223  //QgsDebugMsgLevel( QString( "isActive:" ) + isActive ? "1" : "0", 4 );
1224  //QgsDebugMsgLevel( QString( "useExpression:" ) + useExpression ? "1" : "0", 4 );
1225  //QgsDebugMsgLevel( QString( "expression:" ) + dd->expressionString(), 4 );
1226  //QgsDebugMsgLevel( QString( "field:" ) + field, 4 );
1227 
1228  if ( useExpression && dd->expressionIsPrepared() )
1229  {
1230  QgsExpression* expr = dd->expression();
1231  //QgsDebugMsgLevel( QString( "expr columns:" ) + expr->referencedColumns().join( "," ), 4 );
1232 
1233  result = expr->evaluate( &f );
1234  if ( expr->hasEvalError() )
1235  {
1236  QgsDebugMsgLevel( QString( "Evaluate error:" ) + expr->evalErrorString(), 4 );
1237  return QVariant();
1238  }
1239  }
1240  else if ( !useExpression && !field.isEmpty() )
1241  {
1242  // use direct attribute access instead of evaluating "field" expression (much faster)
1243  int indx = fields.indexFromName( field );
1244  if ( indx != -1 )
1245  {
1246  result = f.attribute( indx );
1247  }
1248  }
1249  return result;
1250 }
1251 
1253 {
1254  // null passed-around QVariant
1255  exprVal.clear();
1256 
1257  QVariant result = dataDefinedValue( p, *mCurFeat, *mCurFields );
1258 
1259  if ( result.isValid() ) // filter NULL values? i.e. && !result.isNull()
1260  {
1261  //QgsDebugMsgLevel( QString( "result type:" ) + QString( result.typeName() ), 4 );
1262  //QgsDebugMsgLevel( QString( "result string:" ) + result.toString(), 4 );
1263  exprVal = result;
1264  return true;
1265  }
1266 
1267  return false;
1268 }
1269 
1271 {
1272  bool isActive = false;
1273  QMap< DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
1274  if ( it != dataDefinedProperties.constEnd() )
1275  {
1276  isActive = it.value()->isActive();
1277  }
1278 
1279  return isActive;
1280 }
1281 
1283 {
1284  bool useExpression = false;
1285  QMap< DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
1286  if ( it != dataDefinedProperties.constEnd() )
1287  {
1288  useExpression = it.value()->useExpression();
1289  }
1290 
1291  return useExpression;
1292 }
1293 
1294 bool QgsPalLayerSettings::checkMinimumSizeMM( const QgsRenderContext& ct, QgsGeometry* geom, double minSize ) const
1295 {
1296  if ( minSize <= 0 )
1297  {
1298  return true;
1299  }
1300 
1301  if ( !geom )
1302  {
1303  return false;
1304  }
1305 
1306  QGis::GeometryType featureType = geom->type();
1307  if ( featureType == QGis::Point ) //minimum size does not apply to point features
1308  {
1309  return true;
1310  }
1311 
1312  double mapUnitsPerMM = ct.mapToPixel().mapUnitsPerPixel() * ct.scaleFactor();
1313  if ( featureType == QGis::Line )
1314  {
1315  double length = geom->length();
1316  if ( length >= 0.0 )
1317  {
1318  return ( length >= ( minSize * mapUnitsPerMM ) );
1319  }
1320  }
1321  else if ( featureType == QGis::Polygon )
1322  {
1323  double area = geom->area();
1324  if ( area >= 0.0 )
1325  {
1326  return ( sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
1327  }
1328  }
1329  return true; //should never be reached. Return true in this case to label such geometries anyway.
1330 }
1331 
1332 void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString text, double& labelX, double& labelY, QgsFeature* f )
1333 {
1334  if ( !fm || !f )
1335  {
1336  return;
1337  }
1338 
1339  QString wrapchr = wrapChar;
1340  double multilineH = multilineHeight;
1341 
1342  bool addDirSymb = addDirectionSymbol;
1343  QString leftDirSymb = leftDirectionSymbol;
1344  QString rightDirSymb = rightDirectionSymbol;
1346 
1347  if ( f == mCurFeat ) // called internally, use any stored data defined values
1348  {
1350  {
1351  wrapchr = dataDefinedValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
1352  }
1353 
1355  {
1356  multilineH = dataDefinedValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
1357  }
1358 
1360  {
1361  addDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
1362  }
1363 
1364  if ( addDirSymb )
1365  {
1366 
1368  {
1369  leftDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
1370  }
1372  {
1373  rightDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
1374  }
1375 
1377  {
1379  }
1380 
1381  }
1382 
1383  }
1384  else // called externally with passed-in feature, evaluate data defined
1385  {
1387  if ( exprVal.isValid() )
1388  {
1389  wrapchr = exprVal.toString();
1390  }
1391  exprVal.clear();
1393  if ( exprVal.isValid() )
1394  {
1395  bool ok;
1396  double size = exprVal.toDouble( &ok );
1397  if ( ok )
1398  {
1399  multilineH = size;
1400  }
1401  }
1402 
1403  exprVal.clear();
1405  if ( exprVal.isValid() )
1406  {
1407  addDirSymb = exprVal.toBool();
1408  }
1409 
1410  if ( addDirSymb ) // don't do extra evaluations if not adding a direction symbol
1411  {
1412  exprVal.clear();
1414  if ( exprVal.isValid() )
1415  {
1416  leftDirSymb = exprVal.toString();
1417  }
1418  exprVal.clear();
1420  if ( exprVal.isValid() )
1421  {
1422  rightDirSymb = exprVal.toString();
1423  }
1424  exprVal.clear();
1426  if ( exprVal.isValid() )
1427  {
1428  bool ok;
1429  int enmint = exprVal.toInt( &ok );
1430  if ( ok )
1431  {
1432  placeDirSymb = ( QgsPalLayerSettings::DirectionSymbols )enmint;
1433  }
1434  }
1435  }
1436 
1437  }
1438 
1439  if ( wrapchr.isEmpty() )
1440  {
1441  wrapchr = QString( "\n" ); // default to new line delimiter
1442  }
1443 
1444  //consider the space needed for the direction symbol
1445  if ( addDirSymb && placement == QgsPalLayerSettings::Line
1446  && ( !leftDirSymb.isEmpty() || !rightDirSymb.isEmpty() ) )
1447  {
1448  QString dirSym = leftDirSymb;
1449 
1450  if ( fm->width( rightDirSymb ) > fm->width( dirSym ) )
1451  dirSym = rightDirSymb;
1452 
1453  if ( placeDirSymb == QgsPalLayerSettings::SymbolLeftRight )
1454  {
1455  text.append( dirSym );
1456  }
1457  else
1458  {
1459  text.prepend( dirSym + wrapchr ); // SymbolAbove or SymbolBelow
1460  }
1461  }
1462 
1463  double w = 0.0, h = 0.0;
1464  QStringList multiLineSplit = text.split( wrapchr );
1465  int lines = multiLineSplit.size();
1466 
1467  double labelHeight = fm->ascent() + fm->descent(); // ignore +1 for baseline
1468 
1469  h += fm->height() + ( double )(( lines - 1 ) * labelHeight * multilineH );
1470  h /= rasterCompressFactor;
1471 
1472  for ( int i = 0; i < lines; ++i )
1473  {
1474  double width = fm->width( multiLineSplit.at( i ) );
1475  if ( width > w )
1476  {
1477  w = width;
1478  }
1479  }
1480  w /= rasterCompressFactor;
1481  QgsPoint ptSize = xform->toMapCoordinatesF( w, h );
1482 
1483  labelX = qAbs( ptSize.x() - ptZero.x() );
1484  labelY = qAbs( ptSize.y() - ptZero.y() );
1485 }
1486 
1488 {
1489  Q_UNUSED( layer );
1490 
1491  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
1492  mCurFeat = &f;
1493 // mCurFields = &layer->pendingFields();
1494 
1495  // store data defined-derived values for later adding to QgsPalGeometry for use during rendering
1496  dataDefinedValues.clear();
1497 
1498 
1499  // data defined show label? defaults to show label if not 0
1501  {
1502  QgsDebugMsgLevel( QString( "exprVal Show:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
1503  if ( !exprVal.toBool() )
1504  {
1505  return;
1506  }
1507  }
1508 
1509  // data defined scale visibility?
1510  bool useScaleVisibility = scaleVisibility;
1512  {
1513  QgsDebugMsgLevel( QString( "exprVal ScaleVisibility:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
1514  useScaleVisibility = exprVal.toBool();
1515  }
1516 
1517  if ( useScaleVisibility )
1518  {
1519  // data defined min scale?
1520  double minScale = scaleMin;
1522  {
1523  QgsDebugMsgLevel( QString( "exprVal MinScale:%1" ).arg( exprVal.toDouble() ), 4 );
1524  bool conversionOk;
1525  double mins = exprVal.toDouble( &conversionOk );
1526  if ( conversionOk )
1527  {
1528  minScale = mins;
1529  }
1530  }
1531 
1532  // scales closer than 1:1
1533  if ( minScale < 0 )
1534  {
1535  minScale = 1 / qAbs( minScale );
1536  }
1537 
1538  if ( minScale != 0 && context.rendererScale() < minScale )
1539  {
1540  return;
1541  }
1542 
1543  // data defined max scale?
1544  double maxScale = scaleMax;
1546  {
1547  QgsDebugMsgLevel( QString( "exprVal MaxScale:%1" ).arg( exprVal.toDouble() ), 4 );
1548  bool conversionOk;
1549  double maxs = exprVal.toDouble( &conversionOk );
1550  if ( conversionOk )
1551  {
1552  maxScale = maxs;
1553  }
1554  }
1555 
1556  // scales closer than 1:1
1557  if ( maxScale < 0 )
1558  {
1559  maxScale = 1 / qAbs( maxScale );
1560  }
1561 
1562  if ( maxScale != 0 && context.rendererScale() > maxScale )
1563  {
1564  return;
1565  }
1566  }
1567 
1568  QFont labelFont = textFont;
1569  // labelFont will be added to label's QgsPalGeometry for use during label painting
1570 
1571  // data defined font units?
1574  {
1575  QString units = exprVal.toString().trimmed();
1576  QgsDebugMsgLevel( QString( "exprVal Font units:%1" ).arg( units ), 4 );
1577  if ( !units.isEmpty() )
1578  {
1579  fontunits = _decodeUnits( units );
1580  }
1581  }
1582 
1583  //data defined label size?
1584  double fontSize = labelFont.pointSizeF(); // font size doesn't have its own class data member
1586  {
1587  QgsDebugMsgLevel( QString( "exprVal Size:%1" ).arg( exprVal.toDouble() ), 4 );
1588  bool ok;
1589  double size = exprVal.toDouble( &ok );
1590  if ( ok )
1591  {
1592  fontSize = size;
1593  }
1594  }
1595  if ( fontSize <= 0.0 )
1596  {
1597  return;
1598  }
1599 
1600  int fontPixelSize = sizeToPixel( fontSize, context, fontunits, true );
1601  // don't try to show font sizes less than 1 pixel (Qt complains)
1602  if ( fontPixelSize < 1 )
1603  {
1604  return;
1605  }
1606  labelFont.setPixelSize( fontPixelSize );
1607 
1608  // NOTE: labelFont now always has pixelSize set, so pointSize or pointSizeF might return -1
1609 
1610  // defined 'minimum/maximum pixel font size'?
1611  if ( fontunits == QgsPalLayerSettings::MapUnits )
1612  {
1613  bool useFontLimitPixelSize = fontLimitPixelSize;
1615  {
1616  QgsDebugMsgLevel( QString( "exprVal FontLimitPixel:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
1617  useFontLimitPixelSize = exprVal.toBool();
1618  }
1619 
1620  if ( useFontLimitPixelSize )
1621  {
1622  int fontMinPixel = fontMinPixelSize;
1624  {
1625  bool ok;
1626  int sizeInt = exprVal.toInt( &ok );
1627  QgsDebugMsgLevel( QString( "exprVal FontMinPixel:%1" ).arg( sizeInt ), 4 );
1628  if ( ok )
1629  {
1630  fontMinPixel = sizeInt;
1631  }
1632  }
1633 
1634  int fontMaxPixel = fontMaxPixelSize;
1636  {
1637  bool ok;
1638  int sizeInt = exprVal.toInt( &ok );
1639  QgsDebugMsgLevel( QString( "exprVal FontMaxPixel:%1" ).arg( sizeInt ), 4 );
1640  if ( ok )
1641  {
1642  fontMaxPixel = sizeInt;
1643  }
1644  }
1645 
1646  if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
1647  {
1648  return;
1649  }
1650  }
1651  }
1652 
1653  // NOTE: the following parsing functions calculate and store any data defined values for later use in QgsPalLabeling::drawLabeling
1654  // this is done to provide clarity, and because such parsing is not directly related to PAL feature registration calculations
1655 
1656  // calculate rest of font attributes and store any data defined values
1657  // this is done here for later use in making label backgrounds part of collision management (when implemented)
1658  parseTextStyle( labelFont, fontunits, context );
1660  parseTextBuffer();
1662  parseDropShadow();
1663 
1664  QString labelText;
1665 
1666  // Check to see if we are a expression string.
1667  if ( isExpression )
1668  {
1670  if ( exp->hasParserError() )
1671  {
1672  QgsDebugMsgLevel( QString( "Expression parser error:%1" ).arg( exp->parserErrorString() ), 4 );
1673  return;
1674  }
1675  exp->setScale( context.rendererScale() );
1676 // QVariant result = exp->evaluate( &f, layer->pendingFields() );
1677  QVariant result = exp->evaluate( &f ); // expression prepared in QgsPalLabeling::prepareLayer()
1678  if ( exp->hasEvalError() )
1679  {
1680  QgsDebugMsgLevel( QString( "Expression parser eval error:%1" ).arg( exp->evalErrorString() ), 4 );
1681  return;
1682  }
1683  labelText = result.toString();
1684  }
1685  else
1686  {
1687  labelText = f.attribute( fieldIndex ).toString();
1688  }
1689 
1690  // data defined format numbers?
1691  bool formatnum = formatNumbers;
1693  {
1694  formatnum = exprVal.toBool();
1695  QgsDebugMsgLevel( QString( "exprVal NumFormat:%1" ).arg( formatnum ? "true" : "false" ), 4 );
1696  }
1697 
1698  // format number if label text is coercible to a number
1699  if ( formatnum )
1700  {
1701  // data defined decimal places?
1702  int decimalPlaces = decimals;
1704  {
1705  bool ok;
1706  int dInt = exprVal.toInt( &ok );
1707  QgsDebugMsgLevel( QString( "exprVal NumDecimals:%1" ).arg( dInt ), 4 );
1708  if ( ok && dInt > 0 ) // needs to be positive
1709  {
1710  decimalPlaces = dInt;
1711  }
1712  }
1713 
1714  // data defined plus sign?
1715  bool signPlus = plusSign;
1717  {
1718  signPlus = exprVal.toBool();
1719  QgsDebugMsgLevel( QString( "exprVal NumPlusSign:%1" ).arg( signPlus ? "true" : "false" ), 4 );
1720  }
1721 
1722  QVariant textV = QVariant( labelText );
1723  bool ok;
1724  double d = textV.toDouble( &ok );
1725  if ( ok )
1726  {
1727  QString numberFormat;
1728  if ( d > 0 && signPlus )
1729  {
1730  numberFormat.append( "+" );
1731  }
1732  numberFormat.append( "%1" );
1733  labelText = numberFormat.arg( d, 0, 'f', decimalPlaces );
1734  }
1735  }
1736 
1737 
1738  // NOTE: this should come AFTER any option that affects font metrics
1739  QFontMetricsF* labelFontMetrics = new QFontMetricsF( labelFont );
1740  double labelX, labelY; // will receive label size
1741  calculateLabelSize( labelFontMetrics, labelText, labelX, labelY, mCurFeat );
1742 
1743 
1744  // maximum angle between curved label characters (hardcoded defaults used in QGIS <2.0)
1745  //
1746  double maxcharanglein = 20.0; // range 20.0-60.0
1747  double maxcharangleout = -20.0; // range 20.0-95.0
1748 
1750  {
1751  maxcharanglein = maxCurvedCharAngleIn;
1752  maxcharangleout = maxCurvedCharAngleOut;
1753 
1754  //data defined maximum angle between curved label characters?
1756  {
1757  QString ptstr = exprVal.toString().trimmed();
1758  QgsDebugMsgLevel( QString( "exprVal CurvedCharAngleInOut:%1" ).arg( ptstr ), 4 );
1759 
1760  if ( !ptstr.isEmpty() )
1761  {
1762  QPointF maxcharanglePt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
1763  maxcharanglein = qBound( 20.0, ( double )maxcharanglePt.x(), 60.0 );
1764  maxcharangleout = qBound( 20.0, ( double )maxcharanglePt.y(), 95.0 );
1765  }
1766  }
1767  // make sure maxcharangleout is always negative
1768  maxcharangleout = -( qAbs( maxcharangleout ) );
1769  }
1770 
1771  QgsGeometry* geom = f.geometry();
1772  if ( !geom )
1773  {
1774  return;
1775  }
1776 
1777  if ( ct ) // reproject the geometry if necessary
1778  {
1779  try
1780  {
1781  geom->transform( *ct );
1782  }
1783  catch ( QgsCsException &cse )
1784  {
1785  Q_UNUSED( cse );
1786  QgsDebugMsgLevel( QString( "Ignoring feature %1 due transformation exception" ).arg( f.id() ), 4 );
1787  return;
1788  }
1789  }
1790 
1791  if ( !checkMinimumSizeMM( context, geom, minFeatureSize ) )
1792  {
1793  return;
1794  }
1795 
1796  // whether we're going to create a centroid for polygon
1797  bool centroidPoly = (( placement == QgsPalLayerSettings::AroundPoint
1799  && geom->type() == QGis::Polygon );
1800 
1801  // data defined centroid whole or clipped?
1802  bool wholeCentroid = centroidWhole;
1804  {
1805  QString str = exprVal.toString().trimmed();
1806  QgsDebugMsgLevel( QString( "exprVal CentroidWhole:%1" ).arg( str ), 4 );
1807 
1808  if ( !str.isEmpty() )
1809  {
1810  if ( str.compare( "Visible", Qt::CaseInsensitive ) == 0 )
1811  {
1812  wholeCentroid = false;
1813  }
1814  else if ( str.compare( "Whole", Qt::CaseInsensitive ) == 0 )
1815  {
1816  wholeCentroid = true;
1817  }
1818  }
1819  }
1820 
1821  // CLIP the geometry if it is bigger than the extent
1822  // don't clip if centroid is requested for whole feature
1823  bool do_clip = false;
1824  if ( !centroidPoly || ( centroidPoly && !wholeCentroid ) )
1825  {
1826  do_clip = !extentGeom->contains( geom );
1827  if ( do_clip )
1828  {
1829  geom = geom->intersection( extentGeom ); // creates new geometry
1830  if ( !geom )
1831  {
1832  return;
1833  }
1834  }
1835  }
1836 
1837  const GEOSGeometry* geos_geom = geom->asGeos();
1838 
1839  if ( geos_geom == NULL )
1840  return; // invalid geometry
1841 
1842  // likelihood exists label will be registered with PAL and may be drawn
1843  // check if max number of features to label (already registered with PAL) has been reached
1844  // Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
1845  if ( limitNumLabels )
1846  {
1847  if ( !maxNumLabels )
1848  {
1849  return;
1850  }
1851  mFeatsRegPal = palLayer->getNbFeatures();
1852  if ( mFeatsRegPal >= maxNumLabels )
1853  {
1854  return;
1855  }
1856 
1857  int divNum = ( int )(( mFeaturesToLabel / maxNumLabels ) + 0.5 );
1858  if ( divNum && ( mFeatsRegPal == ( int )( mFeatsSendingToPal / divNum ) ) )
1859  {
1860  mFeatsSendingToPal += 1;
1861  if ( divNum && mFeatsSendingToPal % divNum )
1862  {
1863  return;
1864  }
1865  }
1866  }
1867 
1868  GEOSGeometry* geos_geom_clone = GEOSGeom_clone( geos_geom );
1869 
1870  //data defined position / alignment / rotation?
1871  bool dataDefinedPosition = false;
1872  bool labelIsPinned = false;
1873  bool layerDefinedRotation = false;
1874  bool dataDefinedRotation = false;
1875  double xPos = 0.0, yPos = 0.0, angle = 0.0;
1876  bool ddXPos = false, ddYPos = false;
1877  double quadOffsetX = 0.0, quadOffsetY = 0.0;
1878  double offsetX = 0.0, offsetY = 0.0;
1879 
1880  //data defined quadrant offset?
1881  QuadrantPosition quadOff = quadOffset;
1883  {
1884  bool ok;
1885  int quadInt = exprVal.toInt( &ok );
1886  QgsDebugMsgLevel( QString( "exprVal OffsetQuad:%1" ).arg( quadInt ), 4 );
1887  if ( ok && 0 <= quadInt && quadInt <= 8 )
1888  {
1889  quadOff = ( QuadrantPosition )quadInt;
1890  }
1891  }
1892 
1893  // adjust quadrant offset of labels
1894  switch ( quadOff )
1895  {
1896  case QuadrantAboveLeft:
1897  quadOffsetX = -1.0;
1898  quadOffsetY = 1.0;
1899  break;
1900  case QuadrantAbove:
1901  quadOffsetX = 0.0;
1902  quadOffsetY = 1.0;
1903  break;
1904  case QuadrantAboveRight:
1905  quadOffsetX = 1.0;
1906  quadOffsetY = 1.0;
1907  break;
1908  case QuadrantLeft:
1909  quadOffsetX = -1.0;
1910  quadOffsetY = 0.0;
1911  break;
1912  case QuadrantRight:
1913  quadOffsetX = 1.0;
1914  quadOffsetY = 0.0;
1915  break;
1916  case QuadrantBelowLeft:
1917  quadOffsetX = -1.0;
1918  quadOffsetY = -1.0;
1919  break;
1920  case QuadrantBelow:
1921  quadOffsetX = 0.0;
1922  quadOffsetY = -1.0;
1923  break;
1924  case QuadrantBelowRight:
1925  quadOffsetX = 1.0;
1926  quadOffsetY = -1.0;
1927  break;
1928  case QuadrantOver:
1929  default:
1930  break;
1931  }
1932 
1933  //data defined label offset?
1934  double xOff = xOffset;
1935  double yOff = yOffset;
1937  {
1938  QString ptstr = exprVal.toString().trimmed();
1939  QgsDebugMsgLevel( QString( "exprVal OffsetXY:%1" ).arg( ptstr ), 4 );
1940 
1941  if ( !ptstr.isEmpty() )
1942  {
1943  QPointF ddOffPt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
1944  xOff = ddOffPt.x();
1945  yOff = ddOffPt.y();
1946  }
1947  }
1948 
1949  // data defined label offset units?
1950  bool offinmapunits = labelOffsetInMapUnits;
1952  {
1953  QString units = exprVal.toString().trimmed();
1954  QgsDebugMsgLevel( QString( "exprVal OffsetUnits:%1" ).arg( units ), 4 );
1955  if ( !units.isEmpty() )
1956  {
1957  offinmapunits = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
1958  }
1959  }
1960 
1961  // adjust offset of labels to match chosen unit and map scale
1962  // offsets match those of symbology: -x = left, -y = up
1963  double mapUntsPerMM = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
1964  if ( xOff != 0 )
1965  {
1966  offsetX = xOff; // must be positive to match symbology offset direction
1967  if ( !offinmapunits )
1968  {
1969  offsetX *= mapUntsPerMM; //convert offset from mm to map units
1970  }
1971  }
1972  if ( yOff != 0 )
1973  {
1974  offsetY = -yOff; // must be negative to match symbology offset direction
1975  if ( !offinmapunits )
1976  {
1977  offsetY *= mapUntsPerMM; //convert offset from mm to map units
1978  }
1979  }
1980 
1981  // layer defined rotation?
1982  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
1984  {
1985  layerDefinedRotation = true;
1986  angle = angleOffset * M_PI / 180; // convert to radians
1987  }
1988 
1989  //data defined rotation?
1991  {
1992  bool ok;
1993  double rotD = exprVal.toDouble( &ok );
1994  QgsDebugMsgLevel( QString( "exprVal Rotation:%1" ).arg( rotD ), 4 );
1995  if ( ok )
1996  {
1997  dataDefinedRotation = true;
1998  angle = rotD * M_PI / 180.0;
1999  }
2000  }
2001 
2003  {
2004  xPos = exprVal.toDouble( &ddXPos );
2005  QgsDebugMsgLevel( QString( "exprVal PositionX:%1" ).arg( xPos ), 4 );
2006 
2008  {
2009  //data defined position. But field values could be NULL -> positions will be generated by PAL
2010  yPos = exprVal.toDouble( &ddYPos );
2011  QgsDebugMsgLevel( QString( "exprVal PositionY:%1" ).arg( yPos ), 4 );
2012 
2013  if ( ddXPos && ddYPos )
2014  {
2015  dataDefinedPosition = true;
2016  labelIsPinned = true;
2017  // layer rotation set, but don't rotate pinned labels unless data defined
2018  if ( layerDefinedRotation && !dataDefinedRotation )
2019  {
2020  angle = 0.0;
2021  }
2022 
2023  //x/y shift in case of alignment
2024  double xdiff = 0.0;
2025  double ydiff = 0.0;
2026 
2027  //horizontal alignment
2029  {
2030  QString haliString = exprVal.toString();
2031  QgsDebugMsgLevel( QString( "exprVal Hali:%1" ).arg( haliString ), 4 );
2032  if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
2033  {
2034  xdiff -= labelX / 2.0;
2035  }
2036  else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
2037  {
2038  xdiff -= labelX;
2039  }
2040  }
2041 
2042  //vertical alignment
2044  {
2045  QString valiString = exprVal.toString();
2046  QgsDebugMsgLevel( QString( "exprVal Vali:%1" ).arg( valiString ), 4 );
2047 
2048  if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
2049  {
2050  if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 )
2051  {
2052  ydiff -= labelY;
2053  }
2054  else
2055  {
2056  double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
2057  if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
2058  {
2059  ydiff -= labelY * descentRatio;
2060  }
2061  else //'Cap' or 'Half'
2062  {
2063  double capHeightRatio = ( labelFontMetrics->boundingRect( 'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
2064  ydiff -= labelY * capHeightRatio;
2065  if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
2066  {
2067  ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
2068  }
2069  }
2070  }
2071  }
2072  }
2073 
2074  if ( dataDefinedRotation )
2075  {
2076  //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
2077  double xd = xdiff * cos( angle ) - ydiff * sin( angle );
2078  double yd = xdiff * sin( angle ) + ydiff * cos( angle );
2079  xdiff = xd;
2080  ydiff = yd;
2081  }
2082 
2083  //project xPos and yPos from layer to map CRS
2084  double z = 0;
2085  if ( ct )
2086  {
2087  try
2088  {
2089  ct->transformInPlace( xPos, yPos, z );
2090  }
2091  catch ( QgsCsException &e )
2092  {
2093  Q_UNUSED( e );
2094  QgsDebugMsgLevel( QString( "Ignoring feature %1 due transformation exception on data-defined position" ).arg( f.id() ), 4 );
2095  return;
2096  }
2097  }
2098 
2099  xPos += xdiff;
2100  yPos += ydiff;
2101  }
2102  else
2103  {
2104  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2105  if ( dataDefinedRotation && placement != QgsPalLayerSettings::OverPoint )
2106  {
2107  angle = 0.0;
2108  }
2109  }
2110  }
2111  }
2112 
2113  // data defined always show?
2114  bool alwaysShow = false;
2116  {
2117  alwaysShow = exprVal.toBool();
2118  }
2119 
2120  QgsPalGeometry* lbl = new QgsPalGeometry(
2121  f.id(),
2122  labelText,
2123  geos_geom_clone,
2124  labelFont.letterSpacing(),
2125  labelFont.wordSpacing(),
2127 
2128  // record the created geometry - it will be deleted at the end.
2129  geometries.append( lbl );
2130 
2131  // store the label's calculated font for later use during painting
2132 #if QT_VERSION >= 0x040800
2133  QgsDebugMsgLevel( QString( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString() ).arg( labelFont.styleName() ), 4 );
2134 #endif
2135  lbl->setDefinedFont( labelFont );
2136 
2137  // feature to the layer
2138  try
2139  {
2140  if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(),
2141  xPos, yPos, dataDefinedPosition, angle, dataDefinedRotation,
2142  quadOffsetX, quadOffsetY, offsetX, offsetY, alwaysShow ) )
2143  return;
2144  }
2145  catch ( std::exception &e )
2146  {
2147  Q_UNUSED( e );
2148  QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( f.id() ) + QString::fromLatin1( e.what() ), 4 );
2149  return;
2150  }
2151 
2152  // TODO: only for placement which needs character info
2153  pal::Feature* feat = palLayer->getFeature( lbl->strId() );
2154  // account for any data defined font metrics adjustments
2155  feat->setLabelInfo( lbl->info( labelFontMetrics, xform, rasterCompressFactor, maxcharanglein, maxcharangleout ) );
2156  delete labelFontMetrics;
2157 
2158  // TODO: allow layer-wide feature dist in PAL...?
2159 
2160  // data defined label-feature distance?
2161  double distance = dist;
2163  {
2164  bool ok;
2165  double distD = exprVal.toDouble( &ok );
2166  if ( ok )
2167  {
2168  distance = distD;
2169  }
2170  }
2171 
2172  // data defined label-feature distance units?
2173  bool distinmapunit = distInMapUnits;
2175  {
2176  QString units = exprVal.toString().trimmed();
2177  QgsDebugMsgLevel( QString( "exprVal DistanceUnits:%1" ).arg( units ), 4 );
2178  if ( !units.isEmpty() )
2179  {
2180  distinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2181  }
2182  }
2183 
2184  if ( distance != 0 )
2185  {
2186  if ( distinmapunit ) //convert distance from mm/map units to pixels
2187  {
2188  distance /= context.mapToPixel().mapUnitsPerPixel();
2189  }
2190  else //mm
2191  {
2192  distance *= vectorScaleFactor;
2193  }
2194  feat->setDistLabel( qAbs( ptOne.x() - ptZero.x() )* distance );
2195  }
2196 
2197  //add parameters for data defined labeling to QgsPalGeometry
2198  QMap< DataDefinedProperties, QVariant >::const_iterator dIt = dataDefinedValues.constBegin();
2199  for ( ; dIt != dataDefinedValues.constEnd(); ++dIt )
2200  {
2201  lbl->addDataDefinedValue( dIt.key(), dIt.value() );
2202  }
2203 
2204  // set geometry's pinned property
2205  lbl->setIsPinned( labelIsPinned );
2206 }
2207 
2208 bool QgsPalLayerSettings::dataDefinedValEval( const QString& valType,
2210  QVariant& exprVal )
2211 {
2212  if ( dataDefinedEvaluate( p, exprVal ) )
2213  {
2214  QString dbgStr = QString( "exprVal %1:" ).arg( mDataDefinedNames.value( p ).first ) + "%1";
2215 
2216  if ( valType == QString( "bool" ) )
2217  {
2218  bool bol = exprVal.toBool();
2219  QgsDebugMsgLevel( dbgStr.arg( bol ? "true" : "false" ), 4 );
2220  dataDefinedValues.insert( p, QVariant( bol ) );
2221  return true;
2222  }
2223  if ( valType == QString( "int" ) )
2224  {
2225  bool ok;
2226  int size = exprVal.toInt( &ok );
2227  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2228 
2229  if ( ok )
2230  {
2231  dataDefinedValues.insert( p, QVariant( size ) );
2232  return true;
2233  }
2234  }
2235  if ( valType == QString( "intpos" ) )
2236  {
2237  bool ok;
2238  int size = exprVal.toInt( &ok );
2239  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2240 
2241  if ( ok && size > 0 )
2242  {
2243  dataDefinedValues.insert( p, QVariant( size ) );
2244  return true;
2245  }
2246  }
2247  if ( valType == QString( "double" ) )
2248  {
2249  bool ok;
2250  double size = exprVal.toDouble( &ok );
2251  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2252 
2253  if ( ok )
2254  {
2255  dataDefinedValues.insert( p, QVariant( size ) );
2256  return true;
2257  }
2258  }
2259  if ( valType == QString( "doublepos" ) )
2260  {
2261  bool ok;
2262  double size = exprVal.toDouble( &ok );
2263  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2264 
2265  if ( ok && size > 0.0 )
2266  {
2267  dataDefinedValues.insert( p, QVariant( size ) );
2268  return true;
2269  }
2270  }
2271  if ( valType == QString( "rotation180" ) )
2272  {
2273  bool ok;
2274  double rot = exprVal.toDouble( &ok );
2275  QgsDebugMsgLevel( dbgStr.arg( rot ), 4 );
2276  if ( ok )
2277  {
2278  if ( rot < -180.0 && rot >= -360 )
2279  {
2280  rot += 360;
2281  }
2282  if ( rot > 180.0 && rot <= 360 )
2283  {
2284  rot -= 360;
2285  }
2286  if ( rot >= -180 && rot <= 180 )
2287  {
2288  dataDefinedValues.insert( p, QVariant( rot ) );
2289  return true;
2290  }
2291  }
2292  }
2293  if ( valType == QString( "transp" ) )
2294  {
2295  bool ok;
2296  int size = exprVal.toInt( &ok );
2297  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2298  if ( ok && size >= 0 && size <= 100 )
2299  {
2300  dataDefinedValues.insert( p, QVariant( size ) );
2301  return true;
2302  }
2303  }
2304  if ( valType == QString( "string" ) )
2305  {
2306  QString str = exprVal.toString(); // don't trim whitespace
2307  QgsDebugMsgLevel( dbgStr.arg( str ), 4 );
2308 
2309  dataDefinedValues.insert( p, QVariant( str ) ); // let it stay empty if it is
2310  return true;
2311  }
2312  if ( valType == QString( "units" ) )
2313  {
2314  QString unitstr = exprVal.toString().trimmed();
2315  QgsDebugMsgLevel( dbgStr.arg( unitstr ), 4 );
2316 
2317  if ( !unitstr.isEmpty() )
2318  {
2319  dataDefinedValues.insert( p, QVariant(( int )_decodeUnits( unitstr ) ) );
2320  return true;
2321  }
2322  }
2323  if ( valType == QString( "color" ) )
2324  {
2325  QString colorstr = exprVal.toString().trimmed();
2326  QgsDebugMsgLevel( dbgStr.arg( colorstr ), 4 );
2327  QColor color = QgsSymbolLayerV2Utils::decodeColor( colorstr );
2328 
2329  if ( color.isValid() )
2330  {
2331  dataDefinedValues.insert( p, QVariant( color ) );
2332  return true;
2333  }
2334  }
2335  if ( valType == QString( "joinstyle" ) )
2336  {
2337  QString joinstr = exprVal.toString().trimmed();
2338  QgsDebugMsgLevel( dbgStr.arg( joinstr ), 4 );
2339 
2340  if ( !joinstr.isEmpty() )
2341  {
2342  dataDefinedValues.insert( p, QVariant(( int )_decodePenJoinStyle( joinstr ) ) );
2343  return true;
2344  }
2345  }
2346  if ( valType == QString( "blendmode" ) )
2347  {
2348  QString blendstr = exprVal.toString().trimmed();
2349  QgsDebugMsgLevel( dbgStr.arg( blendstr ), 4 );
2350 
2351  if ( !blendstr.isEmpty() )
2352  {
2353  dataDefinedValues.insert( p, QVariant(( int )_decodeBlendMode( blendstr ) ) );
2354  return true;
2355  }
2356  }
2357  if ( valType == QString( "pointf" ) )
2358  {
2359  QString ptstr = exprVal.toString().trimmed();
2360  QgsDebugMsgLevel( dbgStr.arg( ptstr ), 4 );
2361 
2362  if ( !ptstr.isEmpty() )
2363  {
2364  dataDefinedValues.insert( p, QVariant( QgsSymbolLayerV2Utils::decodePoint( ptstr ) ) );
2365  return true;
2366  }
2367  }
2368  }
2369  return false;
2370 }
2371 
2374  const QgsRenderContext& context )
2375 {
2376  // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
2377 
2378  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2379 
2380  // Two ways to generate new data defined font:
2381  // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
2382  // 2) Family + named style (bold or italic is ignored)
2383 
2384  // data defined font family?
2385  QString ddFontFamily( "" );
2387  {
2388  QString family = exprVal.toString().trimmed();
2389  QgsDebugMsgLevel( QString( "exprVal Font family:%1" ).arg( family ), 4 );
2390 
2391  if ( labelFont.family() != family )
2392  {
2393  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
2394  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
2395  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
2396  {
2397  ddFontFamily = family;
2398  }
2399  }
2400  }
2401 
2402  // data defined named font style?
2403  QString ddFontStyle( "" );
2405  {
2406  QString fontstyle = exprVal.toString().trimmed();
2407  QgsDebugMsgLevel( QString( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
2408  ddFontStyle = fontstyle;
2409  }
2410 
2411  // data defined bold font style?
2412  bool ddBold = false;
2414  {
2415  bool bold = exprVal.toBool();
2416  QgsDebugMsgLevel( QString( "exprVal Font bold:%1" ).arg( bold ? "true" : "false" ), 4 );
2417  ddBold = bold;
2418  }
2419 
2420  // data defined italic font style?
2421  bool ddItalic = false;
2423  {
2424  bool italic = exprVal.toBool();
2425  QgsDebugMsgLevel( QString( "exprVal Font italic:%1" ).arg( italic ? "true" : "false" ), 4 );
2426  ddItalic = italic;
2427  }
2428 
2429  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
2430  // (currently defaults to what has been read in from layer settings)
2431  QFont newFont;
2432  QFont appFont = QApplication::font();
2433  bool newFontBuilt = false;
2434  if ( ddBold || ddItalic )
2435  {
2436  // new font needs built, since existing style needs removed
2437  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
2438  newFontBuilt = true;
2439  newFont.setBold( ddBold );
2440  newFont.setItalic( ddItalic );
2441  }
2442  else if ( !ddFontStyle.isEmpty()
2443  && ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
2444  {
2445  if ( !ddFontFamily.isEmpty() )
2446  {
2447  // both family and style are different, build font from database
2448  QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
2449  if ( appFont != styledfont )
2450  {
2451  newFont = styledfont;
2452  newFontBuilt = true;
2453  }
2454  }
2455 
2456  // update the font face style
2457  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
2458  }
2459  else if ( !ddFontFamily.isEmpty() )
2460  {
2461  if ( ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
2462  {
2463  // just family is different, build font from database
2464  QFont styledfont = mFontDB.font( ddFontFamily, textNamedStyle, appFont.pointSize() );
2465  if ( appFont != styledfont )
2466  {
2467  newFont = styledfont;
2468  newFontBuilt = true;
2469  }
2470  }
2471  else
2472  {
2473  newFont = QFont( ddFontFamily );
2474  newFontBuilt = true;
2475  }
2476  }
2477 
2478  if ( newFontBuilt )
2479  {
2480  // copy over existing font settings
2481  //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
2482  newFont.setPixelSize( labelFont.pixelSize() );
2483  newFont.setCapitalization( labelFont.capitalization() );
2484  newFont.setUnderline( labelFont.underline() );
2485  newFont.setStrikeOut( labelFont.strikeOut() );
2486  newFont.setWordSpacing( labelFont.wordSpacing() );
2487  newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
2488 
2489  labelFont = newFont;
2490  }
2491 
2492  // data defined word spacing?
2493  double wordspace = labelFont.wordSpacing();
2495  {
2496  bool ok;
2497  double wspacing = exprVal.toDouble( &ok );
2498  QgsDebugMsgLevel( QString( "exprVal FontWordSpacing:%1" ).arg( wspacing ), 4 );
2499  if ( ok )
2500  {
2501  wordspace = wspacing;
2502  }
2503  }
2504  labelFont.setWordSpacing( sizeToPixel( wordspace, context, fontunits, false ) );
2505 
2506  // data defined letter spacing?
2507  double letterspace = labelFont.letterSpacing();
2509  {
2510  bool ok;
2511  double lspacing = exprVal.toDouble( &ok );
2512  QgsDebugMsgLevel( QString( "exprVal FontLetterSpacing:%1" ).arg( lspacing ), 4 );
2513  if ( ok )
2514  {
2515  letterspace = lspacing;
2516  }
2517  }
2518  labelFont.setLetterSpacing( QFont::AbsoluteSpacing, sizeToPixel( letterspace, context, fontunits, false ) );
2519 
2520  // data defined font capitalization?
2521  QFont::Capitalization fontcaps = labelFont.capitalization();
2523  {
2524  QString fcase = exprVal.toString().trimmed();
2525  QgsDebugMsgLevel( QString( "exprVal FontCase:%1" ).arg( fcase ), 4 );
2526 
2527  if ( !fcase.isEmpty() )
2528  {
2529  if ( fcase.compare( "NoChange", Qt::CaseInsensitive ) == 0 )
2530  {
2531  fontcaps = QFont::MixedCase;
2532  }
2533  else if ( fcase.compare( "Upper", Qt::CaseInsensitive ) == 0 )
2534  {
2535  fontcaps = QFont::AllUppercase;
2536  }
2537  else if ( fcase.compare( "Lower", Qt::CaseInsensitive ) == 0 )
2538  {
2539  fontcaps = QFont::AllLowercase;
2540  }
2541  else if ( fcase.compare( "Capitalize", Qt::CaseInsensitive ) == 0 )
2542  {
2543  fontcaps = QFont::Capitalize;
2544  }
2545 
2546  if ( fontcaps != labelFont.capitalization() )
2547  {
2548  labelFont.setCapitalization( fontcaps );
2549  }
2550  }
2551  }
2552 
2553  // data defined strikeout font style?
2555  {
2556  bool strikeout = exprVal.toBool();
2557  QgsDebugMsgLevel( QString( "exprVal Font strikeout:%1" ).arg( strikeout ? "true" : "false" ), 4 );
2558  labelFont.setStrikeOut( strikeout );
2559  }
2560 
2561  // data defined underline font style?
2563  {
2564  bool underline = exprVal.toBool();
2565  QgsDebugMsgLevel( QString( "exprVal Font underline:%1" ).arg( underline ? "true" : "false" ), 4 );
2566  labelFont.setUnderline( underline );
2567  }
2568 
2569  // pass the rest on to QgsPalLabeling::drawLabeling
2570 
2571  // data defined font color?
2572  dataDefinedValEval( "color", QgsPalLayerSettings::Color, exprVal );
2573 
2574  // data defined font transparency?
2576 
2577  // data defined font blend mode?
2578  dataDefinedValEval( "blendmode", QgsPalLayerSettings::FontBlendMode, exprVal );
2579 
2580 }
2581 
2583 {
2584  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2585 
2586  // data defined draw buffer?
2587  bool drawBuffer = bufferDraw;
2588  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::BufferDraw, exprVal ) )
2589  {
2590  drawBuffer = exprVal.toBool();
2591  }
2592 
2593  if ( !drawBuffer )
2594  {
2595  return;
2596  }
2597 
2598  // data defined buffer size?
2599  double bufrSize = bufferSize;
2600  if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::BufferSize, exprVal ) )
2601  {
2602  bufrSize = exprVal.toDouble();
2603  }
2604 
2605  // data defined buffer transparency?
2606  int bufTransp = bufferTransp;
2607  if ( dataDefinedValEval( "transp", QgsPalLayerSettings::BufferTransp, exprVal ) )
2608  {
2609  bufTransp = exprVal.toInt();
2610  }
2611 
2612  drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufTransp < 100 );
2613 
2614  if ( !drawBuffer )
2615  {
2616  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
2619  return; // don't bother evaluating values that won't be used
2620  }
2621 
2622  // data defined buffer units?
2624 
2625  // data defined buffer color?
2627 
2628  // data defined buffer pen join style?
2630 
2631  // data defined buffer blend mode?
2633 }
2634 
2636 {
2637  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2638 
2639  // data defined multiline wrap character?
2640  QString wrapchr = wrapChar;
2641  if ( dataDefinedValEval( "string", QgsPalLayerSettings::MultiLineWrapChar, exprVal ) )
2642  {
2643  wrapchr = exprVal.toString();
2644  }
2645 
2646  // data defined multiline height?
2648 
2649  // data defined multiline text align?
2651  {
2652  QString str = exprVal.toString().trimmed();
2653  QgsDebugMsgLevel( QString( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
2654 
2655  if ( !str.isEmpty() )
2656  {
2657  // "Left"
2659 
2660  if ( str.compare( "Center", Qt::CaseInsensitive ) == 0 )
2661  {
2663  }
2664  else if ( str.compare( "Right", Qt::CaseInsensitive ) == 0 )
2665  {
2666  aligntype = QgsPalLayerSettings::MultiRight;
2667  }
2668  dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant(( int )aligntype ) );
2669  }
2670  }
2671 
2672  // data defined direction symbol?
2673  bool drawDirSymb = addDirectionSymbol;
2674  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::DirSymbDraw, exprVal ) )
2675  {
2676  drawDirSymb = exprVal.toBool();
2677  }
2678 
2679  if ( drawDirSymb )
2680  {
2681  // data defined direction left symbol?
2683 
2684  // data defined direction right symbol?
2686 
2687  // data defined direction symbol placement?
2689  {
2690  QString str = exprVal.toString().trimmed();
2691  QgsDebugMsgLevel( QString( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
2692 
2693  if ( !str.isEmpty() )
2694  {
2695  // "LeftRight"
2697 
2698  if ( str.compare( "Above", Qt::CaseInsensitive ) == 0 )
2699  {
2701  }
2702  else if ( str.compare( "Below", Qt::CaseInsensitive ) == 0 )
2703  {
2705  }
2706  dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant(( int )placetype ) );
2707  }
2708  }
2709 
2710  // data defined direction symbol reversed?
2712  }
2713 
2714  // formatting for numbers is inline with generation of base label text and not passed to label painting
2715 }
2716 
2718 {
2719  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2720 
2721  // data defined draw shape?
2722  bool drawShape = shapeDraw;
2723  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::ShapeDraw, exprVal ) )
2724  {
2725  drawShape = exprVal.toBool();
2726  }
2727 
2728  if ( !drawShape )
2729  {
2730  return;
2731  }
2732 
2733  // data defined shape transparency?
2734  int shapeTransp = shapeTransparency;
2735  if ( dataDefinedValEval( "transp", QgsPalLayerSettings::ShapeTransparency, exprVal ) )
2736  {
2737  shapeTransp = exprVal.toInt();
2738  }
2739 
2740  drawShape = ( drawShape && shapeTransp < 100 ); // size is not taken into account (could be)
2741 
2742  if ( !drawShape )
2743  {
2744  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
2746  return; // don't bother evaluating values that won't be used
2747  }
2748 
2749  // data defined shape kind?
2752  {
2753  QString skind = exprVal.toString().trimmed();
2754  QgsDebugMsgLevel( QString( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
2755 
2756  if ( !skind.isEmpty() )
2757  {
2758  // "Rectangle"
2760 
2761  if ( skind.compare( "Square", Qt::CaseInsensitive ) == 0 )
2762  {
2764  }
2765  else if ( skind.compare( "Ellipse", Qt::CaseInsensitive ) == 0 )
2766  {
2768  }
2769  else if ( skind.compare( "Circle", Qt::CaseInsensitive ) == 0 )
2770  {
2772  }
2773  else if ( skind.compare( "SVG", Qt::CaseInsensitive ) == 0 )
2774  {
2776  }
2777  shapeKind = shpkind;
2778  dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant(( int )shpkind ) );
2779  }
2780  }
2781 
2782  // data defined shape SVG path?
2783  QString svgPath = shapeSVGFile;
2785  {
2786  QString svgfile = exprVal.toString().trimmed();
2787  QgsDebugMsgLevel( QString( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
2788 
2789  // '' empty paths are allowed
2790  svgPath = svgfile;
2791  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgfile ) );
2792  }
2793 
2794  // data defined shape size type?
2797  {
2798  QString stype = exprVal.toString().trimmed();
2799  QgsDebugMsgLevel( QString( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
2800 
2801  if ( !stype.isEmpty() )
2802  {
2803  // "Buffer"
2805 
2806  if ( stype.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
2807  {
2809  }
2810  shpSizeType = sizType;
2811  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant(( int )sizType ) );
2812  }
2813  }
2814 
2815  // data defined shape size X? (SVGs only use X for sizing)
2816  double ddShpSizeX = shapeSize.x();
2817  if ( dataDefinedValEval( "double", QgsPalLayerSettings::ShapeSizeX, exprVal ) )
2818  {
2819  ddShpSizeX = exprVal.toDouble();
2820  }
2821 
2822  // data defined shape size Y?
2823  double ddShpSizeY = shapeSize.y();
2824  if ( dataDefinedValEval( "double", QgsPalLayerSettings::ShapeSizeY, exprVal ) )
2825  {
2826  ddShpSizeY = exprVal.toDouble();
2827  }
2828 
2829  // don't continue under certain circumstances (e.g. size is fixed)
2830  bool skip = false;
2831  if ( shapeKind == QgsPalLayerSettings::ShapeSVG
2832  && ( svgPath.isEmpty()
2833  || ( !svgPath.isEmpty()
2834  && shpSizeType == QgsPalLayerSettings::SizeFixed
2835  && ddShpSizeX == 0.0 ) ) )
2836  {
2837  skip = true;
2838  }
2839  if ( shapeKind != QgsPalLayerSettings::ShapeSVG
2840  && shpSizeType == QgsPalLayerSettings::SizeFixed
2841  && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
2842  {
2843  skip = true;
2844  }
2845 
2846  if ( skip )
2847  {
2848  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
2854  return; // don't bother evaluating values that won't be used
2855  }
2856 
2857  // data defined shape size units?
2859 
2860  // data defined shape rotation type?
2862  {
2863  QString rotstr = exprVal.toString().trimmed();
2864  QgsDebugMsgLevel( QString( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
2865 
2866  if ( !rotstr.isEmpty() )
2867  {
2868  // "Sync"
2870 
2871  if ( rotstr.compare( "Offset", Qt::CaseInsensitive ) == 0 )
2872  {
2874  }
2875  else if ( rotstr.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
2876  {
2878  }
2879  dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant(( int )rottype ) );
2880  }
2881  }
2882 
2883  // data defined shape rotation?
2884  dataDefinedValEval( "rotation180", QgsPalLayerSettings::ShapeRotation, exprVal );
2885 
2886  // data defined shape offset?
2888 
2889  // data defined shape offset units?
2891 
2892  // data defined shape radii?
2894 
2895  // data defined shape radii units?
2897 
2898  // data defined shape blend mode?
2900 
2901  // data defined shape fill color?
2903 
2904  // data defined shape border color?
2906 
2907  // data defined shape border width?
2909 
2910  // data defined shape border width units?
2912 
2913  // data defined shape join style?
2915 
2916 }
2917 
2919 {
2920  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2921 
2922  // data defined draw shadow?
2923  bool drawShadow = shadowDraw;
2924  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::ShadowDraw, exprVal ) )
2925  {
2926  drawShadow = exprVal.toBool();
2927  }
2928 
2929  if ( !drawShadow )
2930  {
2931  return;
2932  }
2933 
2934  // data defined shadow transparency?
2935  int shadowTransp = shadowTransparency;
2936  if ( dataDefinedValEval( "transp", QgsPalLayerSettings::ShadowTransparency, exprVal ) )
2937  {
2938  shadowTransp = exprVal.toInt();
2939  }
2940 
2941  // data defined shadow offset distance?
2942  double shadowOffDist = shadowOffsetDist;
2943  if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShadowOffsetDist, exprVal ) )
2944  {
2945  shadowOffDist = exprVal.toDouble();
2946  }
2947 
2948  // data defined shadow offset distance?
2949  double shadowRad = shadowRadius;
2950  if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShadowRadius, exprVal ) )
2951  {
2952  shadowRad = exprVal.toDouble();
2953  }
2954 
2955  drawShadow = ( drawShadow && shadowTransp < 100 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
2956 
2957  if ( !drawShadow )
2958  {
2959  dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
2963  return; // don't bother evaluating values that won't be used
2964  }
2965 
2966  // data defined shadow under type?
2968  {
2969  QString str = exprVal.toString().trimmed();
2970  QgsDebugMsgLevel( QString( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
2971 
2972  if ( !str.isEmpty() )
2973  {
2974  // "Lowest"
2976 
2977  if ( str.compare( "Text", Qt::CaseInsensitive ) == 0 )
2978  {
2980  }
2981  else if ( str.compare( "Buffer", Qt::CaseInsensitive ) == 0 )
2982  {
2984  }
2985  else if ( str.compare( "Background", Qt::CaseInsensitive ) == 0 )
2986  {
2988  }
2989  dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant(( int )shdwtype ) );
2990  }
2991  }
2992 
2993  // data defined shadow offset angle?
2995 
2996  // data defined shadow offset units?
2998 
2999  // data defined shadow radius?
3001 
3002  // data defined shadow radius units?
3004 
3005  // data defined shadow scale? ( gui bounds to 0-2000, no upper bound here )
3007 
3008  // data defined shadow color?
3010 
3011  // data defined shadow blend mode?
3013 }
3014 
3015 int QgsPalLayerSettings::sizeToPixel( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor ) const
3016 {
3017  return ( int )( scaleToPixelContext( size, c, unit, rasterfactor ) + 0.5 );
3018 }
3019 
3020 double QgsPalLayerSettings::scaleToPixelContext( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor ) const
3021 {
3022  // if render context is that of device (i.e. not a scaled map), just return size
3023  double mapUnitsPerPixel = c.mapToPixel().mapUnitsPerPixel();
3024 
3025  if ( unit == MapUnits && mapUnitsPerPixel > 0.0 )
3026  {
3027  size = size / mapUnitsPerPixel * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3028  }
3029  else // e.g. in points or mm
3030  {
3031  double ptsTomm = ( unit == Points ? 0.352778 : 1 );
3032  size *= ptsTomm * c.scaleFactor() * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3033  }
3034  return size;
3035 }
3036 
3037 // -------------
3038 
3040  : mMapRenderer( NULL ), mPal( NULL )
3041 {
3042 
3043  // find out engine defaults
3044  Pal p;
3045  mCandPoint = p.getPointP();
3046  mCandLine = p.getLineP();
3047  mCandPolygon = p.getPolyP();
3048 
3049  switch ( p.getSearch() )
3050  {
3051  case CHAIN: mSearch = Chain; break;
3052  case POPMUSIC_TABU: mSearch = Popmusic_Tabu; break;
3053  case POPMUSIC_CHAIN: mSearch = Popmusic_Chain; break;
3054  case POPMUSIC_TABU_CHAIN: mSearch = Popmusic_Tabu_Chain; break;
3055  case FALP: mSearch = Falp; break;
3056  }
3057 
3058  mShowingCandidates = false;
3059  mShowingShadowRects = false;
3060  mShowingAllLabels = false;
3061  mShowingPartialsLabels = p.getShowPartial();
3062 
3064 }
3065 
3067 {
3068  // make sure we've freed everything
3069  exit();
3070 
3072 
3073  delete mLabelSearchTree;
3074  mLabelSearchTree = NULL;
3075 }
3076 
3078 {
3079  // don't do QgsPalLayerSettings::readFromLayer( layer ) if not needed
3080  bool enabled = false;
3081  if ( layer->customProperty( "labeling" ).toString() == QString( "pal" ) )
3082  enabled = layer->customProperty( "labeling/enabled", QVariant( false ) ).toBool();
3083 
3084  return enabled;
3085 }
3086 
3088 {
3089  QHash<QgsVectorLayer*, QgsPalLayerSettings>::iterator lit;
3090  for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
3091  {
3092  clearActiveLayer( lit.key() );
3093  }
3094  mActiveLayers.clear();
3095 }
3096 
3098 {
3100 
3101  // delete all QgsDataDefined objects (which also deletes their QgsExpression object)
3102  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::iterator it = lyr.dataDefinedProperties.begin();
3103  for ( ; it != lyr.dataDefinedProperties.constEnd(); ++it )
3104  {
3105  delete( it.value() );
3106  it.value() = 0;
3107  }
3108  lyr.dataDefinedProperties.clear();
3109 }
3110 
3111 int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QSet<int>& attrIndices, QgsRenderContext& ctx )
3112 {
3113  Q_ASSERT( mMapRenderer != NULL );
3114 
3115  if ( !willUseLayer( layer ) )
3116  {
3117  return 0;
3118  }
3119 
3120  QgsDebugMsgLevel( "PREPARE LAYER " + layer->id(), 4 );
3121 
3122  // start with a temporary settings class, find out labeling info
3123  QgsPalLayerSettings lyrTmp;
3124  lyrTmp.readFromLayer( layer );
3125 
3126  if ( lyrTmp.fieldName.isEmpty() )
3127  {
3128  return 0;
3129  }
3130 
3131  int fldIndex = -1;
3132  if ( lyrTmp.isExpression )
3133  {
3134  QgsExpression exp( lyrTmp.fieldName );
3135  if ( exp.hasEvalError() )
3136  {
3137  QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 );
3138  return 0;
3139  }
3140  }
3141  else
3142  {
3143  // If we aren't an expression, we check to see if we can find the column.
3144  fldIndex = layer->fieldNameIndex( lyrTmp.fieldName );
3145  if ( fldIndex == -1 )
3146  {
3147  return 0;
3148  }
3149  }
3150 
3151  // add layer settings to the pallabeling hashtable: <QgsVectorLayer*, QgsPalLayerSettings>
3152  mActiveLayers.insert( layer, lyrTmp );
3153  // start using the reference to the layer in hashtable instead of local instance
3155 
3156  lyr.mCurFields = &( layer->pendingFields() );
3157 
3158  // add field indices for label's text, from expression or field
3159  if ( lyr.isExpression )
3160  {
3161  // prepare expression for use in QgsPalLayerSettings::registerFeature()
3162  QgsExpression* exp = lyr.getLabelExpression();
3163  exp->prepare( layer->pendingFields() );
3164  if ( exp->hasEvalError() )
3165  {
3166  QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 );
3167  }
3168  foreach ( QString name, exp->referencedColumns() )
3169  {
3170  QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 );
3171  attrIndices.insert( layer->fieldNameIndex( name ) );
3172  }
3173  }
3174  else
3175  {
3176  if ( fldIndex != -1 )
3177  {
3178  attrIndices.insert( fldIndex );
3179  }
3180  }
3181 
3182  // add field indices of data defined expression or field
3183  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator dIt = lyr.dataDefinedProperties.constBegin();
3184  for ( ; dIt != lyr.dataDefinedProperties.constEnd(); ++dIt )
3185  {
3186  QgsDataDefined* dd = dIt.value();
3187  if ( !dd->isActive() )
3188  {
3189  continue;
3190  }
3191 
3192  // NOTE: the following also prepares any expressions for later use
3193 
3194  // store parameters for data defined expressions
3195  QMap<QString, QVariant> exprParams;
3196  exprParams.insert( "scale", ctx.rendererScale() );
3197 
3198  dd->setExpressionParams( exprParams );
3199 
3200  // this will return columns for expressions or field name, depending upon what is set to be used
3201  QStringList cols = dd->referencedColumns( layer ); // <-- prepares any expressions, too
3202 
3203  //QgsDebugMsgLevel( QString( "Data defined referenced columns:" ) + cols.join( "," ), 4 );
3204  foreach ( QString name, cols )
3205  {
3206  attrIndices.insert( layer->fieldNameIndex( name ) );
3207  }
3208  }
3209 
3210  // how to place the labels
3211  Arrangement arrangement;
3212  switch ( lyr.placement )
3213  {
3214  case QgsPalLayerSettings::AroundPoint: arrangement = P_POINT; break;
3215  case QgsPalLayerSettings::OverPoint: arrangement = P_POINT_OVER; break;
3216  case QgsPalLayerSettings::Line: arrangement = P_LINE; break;
3217  case QgsPalLayerSettings::Curved: arrangement = P_CURVED; break;
3218  case QgsPalLayerSettings::Horizontal: arrangement = P_HORIZ; break;
3219  case QgsPalLayerSettings::Free: arrangement = P_FREE; break;
3220  default: Q_ASSERT( "unsupported placement" && 0 ); return 0;
3221  }
3222 
3223  // create the pal layer
3224  double priority = 1 - lyr.priority / 10.0; // convert 0..10 --> 1..0
3225  double min_scale = -1, max_scale = -1;
3226 
3227  // handled in QgsPalLayerSettings::registerFeature now
3228  //if ( lyr.scaleVisibility && !lyr.dataDefinedIsActive( QgsPalLayerSettings::ScaleVisibility ) )
3229  //{
3230  // min_scale = lyr.scaleMin;
3231  // max_scale = lyr.scaleMax;
3232  //}
3233 
3234  Layer* l = mPal->addLayer( layer->id().toUtf8().data(),
3235  min_scale, max_scale, arrangement,
3236  METER, priority, lyr.obstacle, true, true,
3237  lyr.displayAll );
3238 
3239  if ( lyr.placementFlags )
3240  l->setArrangementFlags( lyr.placementFlags );
3241 
3242  // set label mode (label per feature is the default)
3243  l->setLabelMode( lyr.labelPerPart ? Layer::LabelPerFeaturePart : Layer::LabelPerFeature );
3244 
3245  // set whether adjacent lines should be merged
3246  l->setMergeConnectedLines( lyr.mergeLines );
3247 
3248  // set how to show upside-down labels
3249  Layer::UpsideDownLabels upsdnlabels;
3250  switch ( lyr.upsidedownLabels )
3251  {
3252  case QgsPalLayerSettings::Upright: upsdnlabels = Layer::Upright; break;
3253  case QgsPalLayerSettings::ShowDefined: upsdnlabels = Layer::ShowDefined; break;
3254  case QgsPalLayerSettings::ShowAll: upsdnlabels = Layer::ShowAll; break;
3255  default: Q_ASSERT( "unsupported upside-down label setting" && 0 ); return 0;
3256  }
3257  l->setUpsidedownLabels( upsdnlabels );
3258 
3259 // // fix for font size in map units causing font to show pointsize at small map scales
3260 // int pixelFontSize = lyr.sizeToPixel( lyr.textFont.pointSizeF(), ctx,
3261 // lyr.fontSizeInMapUnits ? QgsPalLayerSettings::MapUnits : QgsPalLayerSettings::Points,
3262 // true );
3263 
3264 // if ( pixelFontSize < 1 )
3265 // {
3266 // lyr.textFont.setPointSize( 1 );
3267 // lyr.textFont.setPixelSize( 1 );
3268 // }
3269 // else
3270 // {
3271 // lyr.textFont.setPixelSize( pixelFontSize );
3272 // }
3273 
3274 // // scale spacing sizes if using map units
3275 // if ( lyr.fontSizeInMapUnits )
3276 // {
3277 // double spacingPixelSize;
3278 // if ( lyr.textFont.wordSpacing() != 0 )
3279 // {
3280 // spacingPixelSize = lyr.textFont.wordSpacing() / ctx.mapToPixel().mapUnitsPerPixel() * ctx.rasterScaleFactor();
3281 // lyr.textFont.setWordSpacing( spacingPixelSize );
3282 // }
3283 
3284 // if ( lyr.textFont.letterSpacing() != 0 )
3285 // {
3286 // spacingPixelSize = lyr.textFont.letterSpacing() / ctx.mapToPixel().mapUnitsPerPixel() * ctx.rasterScaleFactor();
3287 // lyr.textFont.setLetterSpacing( QFont::AbsoluteSpacing, spacingPixelSize );
3288 // }
3289 // }
3290 
3291  //raster and vector scale factors
3292  lyr.vectorScaleFactor = ctx.scaleFactor();
3294 
3295  // save the pal layer to our layer context (with some additional info)
3296  lyr.palLayer = l;
3297  lyr.fieldIndex = fldIndex;
3298 
3300  lyr.ct = 0;
3302  {
3304  if ( tr )
3305  {
3306  lyr.ct = tr->clone();
3307  }
3308  }
3309  lyr.ptZero = lyr.xform->toMapCoordinates( 0, 0 );
3310  lyr.ptOne = lyr.xform->toMapCoordinates( 1, 0 );
3311 
3312  // rect for clipping
3314 
3315  lyr.mFeatsSendingToPal = 0;
3316 
3317  return 1; // init successful
3318 }
3319 
3321 {
3322  Layer* l = mPal->addLayer( layer->id().append( "d" ).toUtf8().data(), -1, -1, pal::Arrangement( s->placement ), METER, s->priority, s->obstacle, true, true );
3323  l->setArrangementFlags( s->placementFlags );
3324 
3325  s->palLayer = l;
3326  s->ct = 0;
3328  {
3330  if ( tr )
3331  {
3332  s->ct = tr->clone();
3333  }
3334  }
3336  mActiveDiagramLayers.insert( layer, *s );
3337  return 1;
3338 }
3339 
3341 {
3343  lyr.registerFeature( layer, f, context );
3344 }
3345 
3347 {
3348  //get diagram layer settings, diagram renderer
3349  QHash<QgsVectorLayer*, QgsDiagramLayerSettings>::iterator layerIt = mActiveDiagramLayers.find( layer );
3350  if ( layerIt == mActiveDiagramLayers.constEnd() )
3351  {
3352  return;
3353  }
3354 
3355  //convert geom to geos
3356  QgsGeometry* geom = feat.geometry();
3357 
3358  if ( layerIt.value().ct && !willUseLayer( layer ) ) // reproject the geometry if feature not already transformed for labeling
3359  {
3360  geom->transform( *( layerIt.value().ct ) );
3361  }
3362 
3363  const GEOSGeometry* geos_geom = geom->asGeos();
3364  if ( geos_geom == 0 )
3365  {
3366  return; // invalid geometry
3367  }
3368 
3369  //create PALGeometry with diagram = true
3370  QgsPalGeometry* lbl = new QgsPalGeometry( feat.id(), "", GEOSGeom_clone( geos_geom ) );
3371  lbl->setIsDiagram( true );
3372 
3373  // record the created geometry - it will be deleted at the end.
3374  layerIt.value().geometries.append( lbl );
3375 
3376  double diagramWidth = 0;
3377  double diagramHeight = 0;
3378  QgsDiagramRendererV2* dr = layerIt.value().renderer;
3379  if ( dr )
3380  {
3381  QSizeF diagSize = dr->sizeMapUnits( feat, context );
3382  if ( diagSize.isValid() )
3383  {
3384  diagramWidth = diagSize.width();
3385  diagramHeight = diagSize.height();
3386  }
3387 
3388  //append the diagram attributes to lbl
3389  lbl->setDiagramAttributes( feat.attributes(), feat.fields() );
3390  }
3391 
3392  // feature to the layer
3393  int ddColX = layerIt.value().xPosColumn;
3394  int ddColY = layerIt.value().yPosColumn;
3395  double ddPosX = 0.0;
3396  double ddPosY = 0.0;
3397  bool ddPos = ( ddColX >= 0 && ddColY >= 0 );
3398  if ( ddPos )
3399  {
3400  bool posXOk, posYOk;
3401  //data defined diagram position is always centered
3402  ddPosX = feat.attribute( ddColX ).toDouble( &posXOk ) - diagramWidth / 2.0;
3403  ddPosY = feat.attribute( ddColY ).toDouble( &posYOk ) - diagramHeight / 2.0;
3404  if ( !posXOk || !posYOk )
3405  {
3406  ddPos = false;
3407  }
3408  else
3409  {
3410  const QgsCoordinateTransform* ct = layerIt.value().ct;
3411  if ( ct )
3412  {
3413  double z = 0;
3414  ct->transformInPlace( ddPosX, ddPosY, z );
3415  }
3416  }
3417  }
3418 
3419  try
3420  {
3421  if ( !layerIt.value().palLayer->registerFeature( lbl->strId(), lbl, diagramWidth, diagramHeight, "", ddPosX, ddPosY, ddPos ) )
3422  {
3423  return;
3424  }
3425  }
3426  catch ( std::exception &e )
3427  {
3428  Q_UNUSED( e );
3429  QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( feat.id() ) + QString::fromLatin1( e.what() ), 4 );
3430  return;
3431  }
3432 
3433  pal::Feature* palFeat = layerIt.value().palLayer->getFeature( lbl->strId() );
3434  QgsPoint ptZero = layerIt.value().xform->toMapCoordinates( 0, 0 );
3435  QgsPoint ptOne = layerIt.value().xform->toMapCoordinates( 1, 0 );
3436  palFeat->setDistLabel( qAbs( ptOne.x() - ptZero.x() ) * layerIt.value().dist );
3437 }
3438 
3439 
3441 {
3442  mMapRenderer = mr;
3443 
3444  // delete if exists already
3445  if ( mPal )
3446  delete mPal;
3447 
3448  mPal = new Pal;
3449 
3450  SearchMethod s;
3451  switch ( mSearch )
3452  {
3453  default:
3454  case Chain: s = CHAIN; break;
3455  case Popmusic_Tabu: s = POPMUSIC_TABU; break;
3456  case Popmusic_Chain: s = POPMUSIC_CHAIN; break;
3457  case Popmusic_Tabu_Chain: s = POPMUSIC_TABU_CHAIN; break;
3458  case Falp: s = FALP; break;
3459  }
3460  mPal->setSearch( s );
3461 
3462  // set number of candidates generated per feature
3463  mPal->setPointP( mCandPoint );
3464  mPal->setLineP( mCandLine );
3465  mPal->setPolyP( mCandPolygon );
3466 
3467  mPal->setShowPartial( mShowingPartialsLabels );
3468 
3469  clearActiveLayers(); // free any previous QgsDataDefined objects
3470  mActiveDiagramLayers.clear();
3471 }
3472 
3474 {
3475  delete mPal;
3476  mPal = NULL;
3477  mMapRenderer = NULL;
3478 }
3479 
3480 QgsPalLayerSettings& QgsPalLabeling::layer( const QString& layerName )
3481 {
3482  QHash<QgsVectorLayer*, QgsPalLayerSettings>::iterator lit;
3483  for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
3484  {
3485  if ( lit.key() && lit.key()->id() == layerName )
3486  {
3487  return lit.value();
3488  }
3489  }
3490  return mInvalidLayerSettings;
3491 }
3492 
3494  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3495 {
3496  //font color
3497  if ( ddValues.contains( QgsPalLayerSettings::Color ) )
3498  {
3499  QVariant ddColor = ddValues.value( QgsPalLayerSettings::Color );
3500  tmpLyr.textColor = ddColor.value<QColor>();
3501  }
3502 
3503  //font transparency
3504  if ( ddValues.contains( QgsPalLayerSettings::FontTransp ) )
3505  {
3506  tmpLyr.textTransp = ddValues.value( QgsPalLayerSettings::FontTransp ).toInt();
3507  }
3508 
3509  tmpLyr.textColor.setAlphaF(( 100.0 - ( double )( tmpLyr.textTransp ) ) / 100.0 );
3510 
3511  //font blend mode
3512  if ( ddValues.contains( QgsPalLayerSettings::FontBlendMode ) )
3513  {
3514  tmpLyr.blendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::FontBlendMode ).toInt();
3515  }
3516 }
3517 
3519  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3520 {
3521  if ( ddValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
3522  {
3523  tmpLyr.wrapChar = ddValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
3524  }
3525 
3526  if ( !tmpLyr.wrapChar.isEmpty() )
3527  {
3528 
3529  if ( ddValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
3530  {
3531  tmpLyr.multilineHeight = ddValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
3532  }
3533 
3534  if ( ddValues.contains( QgsPalLayerSettings::MultiLineAlignment ) )
3535  {
3537  }
3538 
3539  }
3540 
3541  if ( ddValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
3542  {
3543  tmpLyr.addDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
3544  }
3545 
3546  if ( tmpLyr.addDirectionSymbol )
3547  {
3548 
3549  if ( ddValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
3550  {
3551  tmpLyr.leftDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
3552  }
3553  if ( ddValues.contains( QgsPalLayerSettings::DirSymbRight ) )
3554  {
3555  tmpLyr.rightDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
3556  }
3557 
3558  if ( ddValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
3559  {
3561  }
3562 
3563  if ( ddValues.contains( QgsPalLayerSettings::DirSymbReverse ) )
3564  {
3565  tmpLyr.reverseDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbReverse ).toBool();
3566  }
3567 
3568  }
3569 }
3570 
3572  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3573 {
3574  //buffer draw
3575  if ( ddValues.contains( QgsPalLayerSettings::BufferDraw ) )
3576  {
3577  tmpLyr.bufferDraw = ddValues.value( QgsPalLayerSettings::BufferDraw ).toBool();
3578  }
3579 
3580  if ( !tmpLyr.bufferDraw )
3581  {
3582  // tmpLyr.bufferSize > 0.0 && tmpLyr.bufferTransp < 100 figured in during evaluation
3583  return; // don't continue looking for unused values
3584  }
3585 
3586  //buffer size
3587  if ( ddValues.contains( QgsPalLayerSettings::BufferSize ) )
3588  {
3589  tmpLyr.bufferSize = ddValues.value( QgsPalLayerSettings::BufferSize ).toDouble();
3590  }
3591 
3592  //buffer transparency
3593  if ( ddValues.contains( QgsPalLayerSettings::BufferTransp ) )
3594  {
3595  tmpLyr.bufferTransp = ddValues.value( QgsPalLayerSettings::BufferTransp ).toInt();
3596  }
3597 
3598  //buffer size units
3599  if ( ddValues.contains( QgsPalLayerSettings::BufferUnit ) )
3600  {
3602  tmpLyr.bufferSizeInMapUnits = ( bufunit == QgsPalLayerSettings::MapUnits );
3603  }
3604 
3605  //buffer color
3606  if ( ddValues.contains( QgsPalLayerSettings::BufferColor ) )
3607  {
3608  QVariant ddColor = ddValues.value( QgsPalLayerSettings::BufferColor );
3609  tmpLyr.bufferColor = ddColor.value<QColor>();
3610  }
3611 
3612  // apply any transparency
3613  tmpLyr.bufferColor.setAlphaF(( 100.0 - ( double )( tmpLyr.bufferTransp ) ) / 100.0 );
3614 
3615  //buffer pen join style
3616  if ( ddValues.contains( QgsPalLayerSettings::BufferJoinStyle ) )
3617  {
3618  tmpLyr.bufferJoinStyle = ( Qt::PenJoinStyle )ddValues.value( QgsPalLayerSettings::BufferJoinStyle ).toInt();
3619  }
3620 
3621  //buffer blend mode
3622  if ( ddValues.contains( QgsPalLayerSettings::BufferBlendMode ) )
3623  {
3624  tmpLyr.bufferBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::BufferBlendMode ).toInt();
3625  }
3626 }
3627 
3629  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3630 {
3631  //shape draw
3632  if ( ddValues.contains( QgsPalLayerSettings::ShapeDraw ) )
3633  {
3634  tmpLyr.shapeDraw = ddValues.value( QgsPalLayerSettings::ShapeDraw ).toBool();
3635  }
3636 
3637  if ( !tmpLyr.shapeDraw )
3638  {
3639  return; // don't continue looking for unused values
3640  }
3641 
3642  if ( ddValues.contains( QgsPalLayerSettings::ShapeKind ) )
3643  {
3644  tmpLyr.shapeType = ( QgsPalLayerSettings::ShapeType )ddValues.value( QgsPalLayerSettings::ShapeKind ).toInt();
3645  }
3646 
3647  if ( ddValues.contains( QgsPalLayerSettings::ShapeSVGFile ) )
3648  {
3649  tmpLyr.shapeSVGFile = ddValues.value( QgsPalLayerSettings::ShapeSVGFile ).toString();
3650  }
3651 
3652  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeType ) )
3653  {
3655  }
3656 
3657  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeX ) )
3658  {
3659  tmpLyr.shapeSize.setX( ddValues.value( QgsPalLayerSettings::ShapeSizeX ).toDouble() );
3660  }
3661  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeY ) )
3662  {
3663  tmpLyr.shapeSize.setY( ddValues.value( QgsPalLayerSettings::ShapeSizeY ).toDouble() );
3664  }
3665 
3666  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeUnits ) )
3667  {
3669  }
3670 
3671  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotationType ) )
3672  {
3674  }
3675 
3676  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotation ) )
3677  {
3678  tmpLyr.shapeRotation = ddValues.value( QgsPalLayerSettings::ShapeRotation ).toDouble();
3679  }
3680 
3681  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffset ) )
3682  {
3683  tmpLyr.shapeOffset = ddValues.value( QgsPalLayerSettings::ShapeOffset ).toPointF();
3684  }
3685 
3686  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffsetUnits ) )
3687  {
3689  }
3690 
3691  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadii ) )
3692  {
3693  tmpLyr.shapeRadii = ddValues.value( QgsPalLayerSettings::ShapeRadii ).toPointF();
3694  }
3695 
3696  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadiiUnits ) )
3697  {
3699  }
3700 
3701  if ( ddValues.contains( QgsPalLayerSettings::ShapeTransparency ) )
3702  {
3703  tmpLyr.shapeTransparency = ddValues.value( QgsPalLayerSettings::ShapeTransparency ).toInt();
3704  }
3705 
3706  if ( ddValues.contains( QgsPalLayerSettings::ShapeBlendMode ) )
3707  {
3708  tmpLyr.shapeBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::ShapeBlendMode ).toInt();
3709  }
3710 
3711  if ( ddValues.contains( QgsPalLayerSettings::ShapeFillColor ) )
3712  {
3713  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeFillColor );
3714  tmpLyr.shapeFillColor = ddColor.value<QColor>();
3715  }
3716 
3717  if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderColor ) )
3718  {
3719  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeBorderColor );
3720  tmpLyr.shapeBorderColor = ddColor.value<QColor>();
3721  }
3722 
3723  if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderWidth ) )
3724  {
3725  tmpLyr.shapeBorderWidth = ddValues.value( QgsPalLayerSettings::ShapeBorderWidth ).toDouble();
3726  }
3727 
3728  if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderWidthUnits ) )
3729  {
3731  }
3732 
3733  if ( ddValues.contains( QgsPalLayerSettings::ShapeJoinStyle ) )
3734  {
3735  tmpLyr.shapeJoinStyle = ( Qt::PenJoinStyle )ddValues.value( QgsPalLayerSettings::ShapeJoinStyle ).toInt();
3736  }
3737 }
3738 
3740  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3741 {
3742  //shadow draw
3743  if ( ddValues.contains( QgsPalLayerSettings::ShadowDraw ) )
3744  {
3745  tmpLyr.shadowDraw = ddValues.value( QgsPalLayerSettings::ShadowDraw ).toBool();
3746  }
3747 
3748  if ( !tmpLyr.shadowDraw )
3749  {
3750  return; // don't continue looking for unused values
3751  }
3752 
3753  if ( ddValues.contains( QgsPalLayerSettings::ShadowUnder ) )
3754  {
3756  }
3757 
3758  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetAngle ) )
3759  {
3760  tmpLyr.shadowOffsetAngle = ddValues.value( QgsPalLayerSettings::ShadowOffsetAngle ).toInt();
3761  }
3762 
3763  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetDist ) )
3764  {
3765  tmpLyr.shadowOffsetDist = ddValues.value( QgsPalLayerSettings::ShadowOffsetDist ).toDouble();
3766  }
3767 
3768  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetUnits ) )
3769  {
3771  }
3772 
3773  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadius ) )
3774  {
3775  tmpLyr.shadowRadius = ddValues.value( QgsPalLayerSettings::ShadowRadius ).toDouble();
3776  }
3777 
3778  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadiusUnits ) )
3779  {
3781  }
3782 
3783  if ( ddValues.contains( QgsPalLayerSettings::ShadowTransparency ) )
3784  {
3785  tmpLyr.shadowTransparency = ddValues.value( QgsPalLayerSettings::ShadowTransparency ).toInt();
3786  }
3787 
3788  if ( ddValues.contains( QgsPalLayerSettings::ShadowScale ) )
3789  {
3790  tmpLyr.shadowScale = ddValues.value( QgsPalLayerSettings::ShadowScale ).toInt();
3791  }
3792 
3793  if ( ddValues.contains( QgsPalLayerSettings::ShadowColor ) )
3794  {
3795  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShadowColor );
3796  tmpLyr.shadowColor = ddColor.value<QColor>();
3797  }
3798 
3799  if ( ddValues.contains( QgsPalLayerSettings::ShadowBlendMode ) )
3800  {
3801  tmpLyr.shadowBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::ShadowBlendMode ).toInt();
3802  }
3803 }
3804 
3806 {
3807  Q_ASSERT( mMapRenderer != NULL );
3808  QPainter* painter = context.painter();
3809  QgsRectangle extent = context.extent();
3810 
3811  if ( mLabelSearchTree )
3812  {
3814  }
3815 
3816  QTime t;
3817  t.start();
3818 
3819  // do the labeling itself
3820  double scale = mMapRenderer->scale(); // scale denominator
3821  QgsRectangle r = extent;
3822  double bbox[] = { r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum() };
3823 
3824  std::list<LabelPosition*>* labels;
3825  pal::Problem* problem;
3826  try
3827  {
3828  problem = mPal->extractProblem( scale, bbox );
3829  }
3830  catch ( std::exception& e )
3831  {
3832  Q_UNUSED( e );
3833  QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
3834  //mActiveLayers.clear(); // clean up
3835  return;
3836  }
3837 
3839 
3840  // draw rectangles with all candidates
3841  // this is done before actual solution of the problem
3842  // before number of candidates gets reduced
3843  mCandidates.clear();
3844  if ( mShowingCandidates && problem )
3845  {
3846  painter->setPen( QColor( 0, 0, 0, 64 ) );
3847  painter->setBrush( Qt::NoBrush );
3848  for ( int i = 0; i < problem->getNumFeatures(); i++ )
3849  {
3850  for ( int j = 0; j < problem->getFeatureCandidateCount( i ); j++ )
3851  {
3852  pal::LabelPosition* lp = problem->getFeatureCandidate( i, j );
3853 
3854  drawLabelCandidateRect( lp, painter, xform );
3855  }
3856  }
3857  }
3858 
3859  // find the solution
3860  labels = mPal->solveProblem( problem, mShowingAllLabels );
3861 
3862  QgsDebugMsgLevel( QString( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( labels->size() ), 4 );
3863  t.restart();
3864 
3865  painter->setRenderHint( QPainter::Antialiasing );
3866 
3867  //dpi ration for QPicture
3868  QPicture localPict;
3869  QPainter localp;
3870  localp.begin( &localPict );
3871  double localdpi = ( localp.device()->logicalDpiX() + localp.device()->logicalDpiY() ) / 2;
3872  double contextdpi = ( painter->device()->logicalDpiX() + painter->device()->logicalDpiY() ) / 2;
3873  double dpiRatio = localdpi / contextdpi;
3874  localp.end();
3875 
3876  // draw the labels
3877  std::list<LabelPosition*>::iterator it = labels->begin();
3878  for ( ; it != labels->end(); ++it )
3879  {
3880  QgsPalGeometry* palGeometry = dynamic_cast< QgsPalGeometry* >(( *it )->getFeaturePart()->getUserGeometry() );
3881  if ( !palGeometry )
3882  {
3883  continue;
3884  }
3885 
3886  //layer names
3887  QString layerName = QString::fromUtf8(( *it )->getLayerName() );
3888  if ( palGeometry->isDiagram() )
3889  {
3890  QgsFeature feature;
3891  //render diagram
3892  QHash<QgsVectorLayer*, QgsDiagramLayerSettings>::iterator dit = mActiveDiagramLayers.begin();
3893  for ( dit = mActiveDiagramLayers.begin(); dit != mActiveDiagramLayers.end(); ++dit )
3894  {
3895  if ( dit.key() && dit.key()->id().append( "d" ) == layerName )
3896  {
3897  palGeometry->feature( feature );
3898  QgsPoint outPt = xform->transform(( *it )->getX(), ( *it )->getY() );
3899  dit.value().renderer->renderDiagram( feature, context, QPointF( outPt.x(), outPt.y() ) );
3900  }
3901  }
3902 
3903  //insert into label search tree to manipulate position interactively
3904  if ( mLabelSearchTree )
3905  {
3906  //for diagrams, remove the additional 'd' at the end of the layer id
3907  QString layerId = layerName;
3908  layerId.chop( 1 );
3909  mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), QString( "" ), layerId, QFont(), true, false );
3910  }
3911  continue;
3912  }
3913 
3914  const QgsPalLayerSettings& lyr = layer( layerName );
3915 
3916  // Copy to temp, editable layer settings
3917  // these settings will be changed by any data defined values, then used for rendering label components
3918  // settings may be adjusted during rendering of components
3919  QgsPalLayerSettings tmpLyr( lyr );
3920 
3921  // apply any previously applied data defined settings for the label
3922  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues = palGeometry->dataDefinedValues();
3923 
3924  //font
3925  QFont dFont = palGeometry->definedFont();
3926  // following debug is >= Qt 4.8 only ( because of QFont::styleName() )
3927 #if QT_VERSION >= 0x040800
3928  QgsDebugMsgLevel( QString( "PAL font tmpLyr: %1, Style: %2" ).arg( tmpLyr.textFont.toString() ).arg( QFontInfo( tmpLyr.textFont ).styleName() ), 4 );
3929  QgsDebugMsgLevel( QString( "PAL font definedFont: %1, Style: %2" ).arg( dFont.toString() ).arg( dFont.styleName() ), 4 );
3930 #endif
3931  tmpLyr.textFont = dFont;
3932 
3933  // update tmpLyr with any data defined text style values
3934  dataDefinedTextStyle( tmpLyr, ddValues );
3935 
3936  // update tmpLyr with any data defined text buffer values
3937  dataDefinedTextBuffer( tmpLyr, ddValues );
3938 
3939  // update tmpLyr with any data defined text formatting values
3940  dataDefinedTextFormatting( tmpLyr, ddValues );
3941 
3942  // update tmpLyr with any data defined shape background values
3943  dataDefinedShapeBackground( tmpLyr, ddValues );
3944 
3945  // update tmpLyr with any data defined drop shadow values
3946  dataDefinedDropShadow( tmpLyr, ddValues );
3947 
3948 
3950 
3951  // Render the components of a label in reverse order
3952  // (backgrounds -> text)
3953 
3954  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowLowest )
3955  {
3956  if ( tmpLyr.shapeDraw )
3957  {
3959  }
3960  else if ( tmpLyr.bufferDraw )
3961  {
3963  }
3964  else
3965  {
3967  }
3968  }
3969 
3970  if ( tmpLyr.shapeDraw )
3971  {
3972  drawLabel( *it, context, tmpLyr, LabelShape, dpiRatio );
3973  }
3974 
3975  if ( tmpLyr.bufferDraw )
3976  {
3977  drawLabel( *it, context, tmpLyr, LabelBuffer, dpiRatio );
3978  }
3979 
3980  drawLabel( *it, context, tmpLyr, LabelText, dpiRatio );
3981 
3982  if ( mLabelSearchTree )
3983  {
3984  QString labeltext = (( QgsPalGeometry* )( *it )->getFeaturePart()->getUserGeometry() )->text();
3985  mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), layerName, labeltext, dFont, false, palGeometry->isPinned() );
3986  }
3987  }
3988 
3989  // Reset composition mode for further drawing operations
3990  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
3991 
3992  QgsDebugMsgLevel( QString( "LABELING draw: %1 ms" ).arg( t.elapsed() ), 4 );
3993 
3994  delete problem;
3995  delete labels;
3996 
3997  // delete all allocated geometries for features
3998  QHash<QgsVectorLayer*, QgsPalLayerSettings>::iterator lit;
3999  for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
4000  {
4001  QgsPalLayerSettings& lyr = lit.value();
4002  for ( QList<QgsPalGeometry*>::iterator git = lyr.geometries.begin(); git != lyr.geometries.end(); ++git )
4003  delete *git;
4004  if ( lyr.limitNumLabels )
4005  {
4006  QgsDebugMsgLevel( QString( "mFeaturesToLabel: %1" ).arg( lyr.mFeaturesToLabel ), 4 );
4007  QgsDebugMsgLevel( QString( "maxNumLabels: %1" ).arg( lyr.maxNumLabels ), 4 );
4008  QgsDebugMsgLevel( QString( "mFeatsSendingToPal: %1" ).arg( lyr.mFeatsSendingToPal ), 4 );
4009  QgsDebugMsgLevel( QString( "mFeatsRegPal: %1" ).arg( lyr.geometries.count() ), 4 );
4010  }
4011  lyr.geometries.clear();
4012  }
4013 
4014  //delete all allocated geometries for diagrams
4015  QHash<QgsVectorLayer*, QgsDiagramLayerSettings>::iterator dIt = mActiveDiagramLayers.begin();
4016  for ( ; dIt != mActiveDiagramLayers.end(); ++dIt )
4017  {
4018  QgsDiagramLayerSettings& dls = dIt.value();
4019  for ( QList<QgsPalGeometry*>::iterator git = dls.geometries.begin(); git != dls.geometries.end(); ++git )
4020  {
4021  delete *git;
4022  }
4023  dls.geometries.clear();
4024  }
4025 }
4026 
4027 QList<QgsLabelPosition> QgsPalLabeling::labelsAtPosition( const QgsPoint& p )
4028 {
4029  QList<QgsLabelPosition> positions;
4030 
4031  QList<QgsLabelPosition*> positionPointers;
4032  if ( mLabelSearchTree )
4033  {
4034  mLabelSearchTree->label( p, positionPointers );
4035  QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
4036  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
4037  {
4038  positions.push_back( QgsLabelPosition( **pointerIt ) );
4039  }
4040  }
4041 
4042  return positions;
4043 }
4044 
4045 QList<QgsLabelPosition> QgsPalLabeling::labelsWithinRect( const QgsRectangle& r )
4046 {
4047  QList<QgsLabelPosition> positions;
4048 
4049  QList<QgsLabelPosition*> positionPointers;
4050  if ( mLabelSearchTree )
4051  {
4052  mLabelSearchTree->labelsInRect( r, positionPointers );
4053  QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
4054  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
4055  {
4056  positions.push_back( QgsLabelPosition( **pointerIt ) );
4057  }
4058  }
4059 
4060  return positions;
4061 }
4062 
4063 void QgsPalLabeling::numCandidatePositions( int& candPoint, int& candLine, int& candPolygon )
4064 {
4065  candPoint = mCandPoint;
4066  candLine = mCandLine;
4067  candPolygon = mCandPolygon;
4068 }
4069 
4070 void QgsPalLabeling::setNumCandidatePositions( int candPoint, int candLine, int candPolygon )
4071 {
4072  mCandPoint = candPoint;
4073  mCandLine = candLine;
4074  mCandPolygon = candPolygon;
4075 }
4076 
4078 {
4079  mSearch = s;
4080 }
4081 
4083 {
4084  return mSearch;
4085 }
4086 
4087 void QgsPalLabeling::drawLabelCandidateRect( pal::LabelPosition* lp, QPainter* painter, const QgsMapToPixel* xform )
4088 {
4089  QgsPoint outPt = xform->transform( lp->getX(), lp->getY() );
4090  QgsPoint outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
4091 
4092  painter->save();
4093  painter->translate( QPointF( outPt.x(), outPt.y() ) );
4094  painter->rotate( -lp->getAlpha() * 180 / M_PI );
4095  QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
4096  painter->drawRect( rect );
4097  painter->restore();
4098 
4099  // save the rect
4100  rect.moveTo( outPt.x(), outPt.y() );
4101  mCandidates.append( QgsLabelCandidate( rect, lp->getCost() * 1000 ) );
4102 
4103  // show all parts of the multipart label
4104  if ( lp->getNextPart() )
4105  drawLabelCandidateRect( lp->getNextPart(), painter, xform );
4106 }
4107 
4108 void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QgsRenderContext& context, QgsPalLayerSettings& tmpLyr, DrawLabelType drawType, double dpiRatio )
4109 {
4110  // NOTE: this is repeatedly called for multi-part labels
4111  QPainter* painter = context.painter();
4112  const QgsMapToPixel* xform = &context.mapToPixel();
4113 
4114  QgsLabelComponent component;
4115  component.setDpiRatio( dpiRatio );
4116 
4117  QgsPoint outPt = xform->transform( label->getX(), label->getY() );
4118 // QgsPoint outPt2 = xform->transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() );
4119 // QRectF labelRect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
4120 
4121  component.setOrigin( outPt );
4122  component.setRotation( label->getAlpha() );
4123 
4124  if ( drawType == QgsPalLabeling::LabelShape )
4125  {
4126  // get rotated label's center point
4127  QgsPoint centerPt( outPt );
4128  QgsPoint outPt2 = xform->transform( label->getX() + label->getWidth() / 2,
4129  label->getY() + label->getHeight() / 2 );
4130 
4131  double xc = outPt2.x() - outPt.x();
4132  double yc = outPt2.y() - outPt.y();
4133 
4134  double angle = -label->getAlpha();
4135  double xd = xc * cos( angle ) - yc * sin( angle );
4136  double yd = xc * sin( angle ) + yc * cos( angle );
4137 
4138  centerPt.setX( centerPt.x() + xd );
4139  centerPt.setY( centerPt.y() + yd );
4140 
4141  component.setCenter( centerPt );
4142  component.setSize( QgsPoint( label->getWidth(), label->getHeight() ) );
4143 
4144  drawLabelBackground( context, component, tmpLyr );
4145  }
4146 
4147  if ( drawType == QgsPalLabeling::LabelBuffer
4148  || drawType == QgsPalLabeling::LabelText )
4149  {
4150 
4151  // TODO: optimize access :)
4152  QString text = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->text();
4153  QString txt = ( label->getPartId() == -1 ? text : QString( text[label->getPartId()] ) );
4154  QFontMetricsF* labelfm = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->getLabelFontMetrics();
4155 
4156  QString wrapchr = !tmpLyr.wrapChar.isEmpty() ? tmpLyr.wrapChar : QString( "\n" );
4157 
4158  //add the direction symbol if needed
4159  if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line &&
4160  tmpLyr.addDirectionSymbol )
4161  {
4162  bool prependSymb = false;
4163  QString symb = tmpLyr.rightDirectionSymbol;
4164 
4165  if ( label->getReversed() )
4166  {
4167  prependSymb = true;
4168  symb = tmpLyr.leftDirectionSymbol;
4169  }
4170 
4171  if ( tmpLyr.reverseDirectionSymbol )
4172  {
4173  if ( symb == tmpLyr.rightDirectionSymbol )
4174  {
4175  prependSymb = true;
4176  symb = tmpLyr.leftDirectionSymbol;
4177  }
4178  else
4179  {
4180  prependSymb = false;
4181  symb = tmpLyr.rightDirectionSymbol;
4182  }
4183  }
4184 
4186  {
4187  prependSymb = true;
4188  symb = symb + wrapchr;
4189  }
4191  {
4192  prependSymb = false;
4193  symb = wrapchr + symb;
4194  }
4195 
4196  if ( prependSymb )
4197  {
4198  txt.prepend( symb );
4199  }
4200  else
4201  {
4202  txt.append( symb );
4203  }
4204  }
4205 
4206  //QgsDebugMsgLevel( "drawLabel " + txt, 4 );
4207 
4208  QStringList multiLineList = txt.split( wrapchr );
4209  int lines = multiLineList.size();
4210 
4211  double labelWidest = 0.0;
4212  for ( int i = 0; i < lines; ++i )
4213  {
4214  double labelWidth = labelfm->width( multiLineList.at( i ) );
4215  if ( labelWidth > labelWidest )
4216  {
4217  labelWidest = labelWidth;
4218  }
4219  }
4220 
4221  double labelHeight = labelfm->ascent() + labelfm->descent(); // ignore +1 for baseline
4222  // double labelHighest = labelfm->height() + ( double )(( lines - 1 ) * labelHeight * tmpLyr.multilineHeight );
4223 
4224  // needed to move bottom of text's descender to within bottom edge of label
4225  double ascentOffset = 0.25 * labelfm->ascent(); // labelfm->descent() is not enough
4226 
4227  for ( int i = 0; i < lines; ++i )
4228  {
4229  painter->save();
4230  painter->translate( QPointF( outPt.x(), outPt.y() ) );
4231  painter->rotate( -label->getAlpha() * 180 / M_PI );
4232 
4233  // scale down painter: the font size has been multiplied by raster scale factor
4234  // to workaround a Qt font scaling bug with small font sizes
4235  painter->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
4236 
4237  // figure x offset for horizontal alignment of multiple lines
4238  double xMultiLineOffset = 0.0;
4239  double labelWidth = labelfm->width( multiLineList.at( i ) );
4240  if ( lines > 1 && tmpLyr.multilineAlign != QgsPalLayerSettings::MultiLeft )
4241  {
4242  double labelWidthDiff = labelWidest - labelWidth;
4244  {
4245  labelWidthDiff /= 2;
4246  }
4247  xMultiLineOffset = labelWidthDiff;
4248  //QgsDebugMsgLevel( QString( "xMultiLineOffset: %1" ).arg( xMultiLineOffset ), 4 );
4249  }
4250 
4251  double yMultiLineOffset = ( lines - 1 - i ) * labelHeight * tmpLyr.multilineHeight;
4252  painter->translate( QPointF( xMultiLineOffset, - ascentOffset - yMultiLineOffset ) );
4253 
4254  component.setText( multiLineList.at( i ) );
4255  component.setSize( QgsPoint( labelWidth, labelHeight ) );
4256  component.setOffset( QgsPoint( 0.0, -ascentOffset ) );
4257  component.setRotation( -component.rotation() * 180 / M_PI );
4258  component.setRotationOffset( 0.0 );
4259 
4260  if ( drawType == QgsPalLabeling::LabelBuffer )
4261  {
4262  // draw label's buffer
4263  drawLabelBuffer( context, component, tmpLyr );
4264  }
4265  else
4266  {
4267  // draw label's text, QPainterPath method
4268  QPainterPath path;
4269  path.addText( 0, 0, tmpLyr.textFont, component.text() );
4270 
4271  // store text's drawing in QPicture for drop shadow call
4272  QPicture textPict;
4273  QPainter textp;
4274  textp.begin( &textPict );
4275  textp.setPen( Qt::NoPen );
4276  textp.setBrush( tmpLyr.textColor );
4277  textp.drawPath( path );
4278  textp.end();
4279 
4280  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowText )
4281  {
4282  component.setPicture( &textPict );
4283  component.setPictureBuffer( 0.0 ); // no pen width to deal with
4284  component.setOrigin( QgsPoint( 0.0, 0.0 ) );
4285 
4286  drawLabelShadow( context, component, tmpLyr );
4287  }
4288 
4289  // paint the text
4290  if ( context.useAdvancedEffects() )
4291  {
4292  painter->setCompositionMode( tmpLyr.blendMode );
4293  }
4294 // painter->setPen( Qt::NoPen );
4295 // painter->setBrush( tmpLyr.textColor );
4296 // painter->drawPath( path );
4297 
4298  // scale for any print output or image saving @ specific dpi
4299  painter->scale( component.dpiRatio(), component.dpiRatio() );
4300  painter->drawPicture( 0, 0, textPict );
4301 
4302  // regular text draw, for testing optimization
4303 // painter->setFont( tmpLyr.textFont );
4304 // painter->setPen( tmpLyr.textColor );
4305 // painter->drawText( 0, 0, multiLineList.at( i ) );
4306 
4307  }
4308  painter->restore();
4309  }
4310  }
4311 
4312  // NOTE: this used to be within above multi-line loop block, at end. (a mistake since 2010? [LS])
4313  if ( label->getNextPart() )
4314  drawLabel( label->getNextPart(), context, tmpLyr, drawType, dpiRatio );
4315 }
4316 
4318  QgsLabelComponent component,
4319  const QgsPalLayerSettings& tmpLyr )
4320 {
4321  QPainter* p = context.painter();
4322 
4323  double penSize = tmpLyr.scaleToPixelContext( tmpLyr.bufferSize, context,
4325 
4326  QPainterPath path;
4327  path.addText( 0, 0, tmpLyr.textFont, component.text() );
4328  QPen pen( tmpLyr.bufferColor );
4329  pen.setWidthF( penSize );
4330  pen.setJoinStyle( tmpLyr.bufferJoinStyle );
4331  QColor tmpColor( tmpLyr.bufferColor );
4332  // honor pref for whether to fill buffer interior
4333  if ( tmpLyr.bufferNoFill )
4334  {
4335  tmpColor.setAlpha( 0 );
4336  }
4337 
4338  // store buffer's drawing in QPicture for drop shadow call
4339  QPicture buffPict;
4340  QPainter buffp;
4341  buffp.begin( &buffPict );
4342  buffp.setPen( pen );
4343  buffp.setBrush( tmpColor );
4344  buffp.drawPath( path );
4345  buffp.end();
4346 
4347  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowBuffer )
4348  {
4349  component.setOrigin( QgsPoint( 0.0, 0.0 ) );
4350  component.setPicture( &buffPict );
4351  component.setPictureBuffer( penSize / 2.0 );
4352 
4353  drawLabelShadow( context, component, tmpLyr );
4354  }
4355 
4356  p->save();
4357  if ( context.useAdvancedEffects() )
4358  {
4359  p->setCompositionMode( tmpLyr.bufferBlendMode );
4360  }
4361 // p->setPen( pen );
4362 // p->setBrush( tmpColor );
4363 // p->drawPath( path );
4364 
4365  // scale for any print output or image saving @ specific dpi
4366  p->scale( component.dpiRatio(), component.dpiRatio() );
4367  p->drawPicture( 0, 0, buffPict );
4368  p->restore();
4369 }
4370 
4372  QgsLabelComponent component,
4373  const QgsPalLayerSettings& tmpLyr )
4374 {
4375  QPainter* p = context.painter();
4376  double labelWidth = component.size().x(), labelHeight = component.size().y();
4377  //QgsDebugMsgLevel( QString( "Background label rotation: %1" ).arg( component.rotation() ), 4 );
4378 
4379  // shared calculations between shapes and SVG
4380 
4381  // configure angles, set component rotation and rotationOffset
4383  {
4384  component.setRotation( -( component.rotation() * 180 / M_PI ) ); // RotationSync
4385  component.setRotationOffset(
4387  }
4388  else // RotationFixed
4389  {
4390  component.setRotation( 0.0 ); // don't use label's rotation
4391  component.setRotationOffset( tmpLyr.shapeRotation );
4392  }
4393 
4394  // mm to map units conversion factor
4395  double mmToMapUnits = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
4396 
4397  // TODO: the following label-buffered generated shapes and SVG symbols should be moved into marker symbology classes
4398 
4399  if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeSVG )
4400  {
4401  // all calculations done in shapeSizeUnits, which are then passed to symbology class for painting
4402 
4403  if ( tmpLyr.shapeSVGFile.isEmpty() )
4404  return;
4405 
4406  double sizeOut = 0.0;
4407  // only one size used for SVG sizing/scaling (no use of shapeSize.y() or Y field in gui)
4409  {
4410  sizeOut = tmpLyr.shapeSize.x();
4411  }
4412  else if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeBuffer )
4413  {
4414  // add buffer to greatest dimension of label
4415  if ( labelWidth >= labelHeight )
4416  sizeOut = labelWidth;
4417  else if ( labelHeight > labelWidth )
4418  sizeOut = labelHeight;
4419 
4420  // label size in map units, convert to shapeSizeUnits, if different
4421  if ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM )
4422  {
4423  sizeOut /= mmToMapUnits;
4424  }
4425 
4426  // add buffer
4427  sizeOut += tmpLyr.shapeSize.x() * 2;
4428  }
4429 
4430  // don't bother rendering symbols smaller than 1x1 pixels in size
4431  // TODO: add option to not show any svgs under/over a certian size
4432  if ( tmpLyr.scaleToPixelContext( sizeOut, context, tmpLyr.shapeSizeUnits ) < 1.0 )
4433  return;
4434 
4435  QgsStringMap map; // for SVG symbology marker
4436  map["name"] = QgsSymbolLayerV2Utils::symbolNameToPath( tmpLyr.shapeSVGFile.trimmed() );
4437  map["size"] = QString::number( sizeOut );
4438  map["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
4440  map["angle"] = QString::number( 0.0 ); // angle is handled by this local painter
4441 
4442  // offset is handled by this local painter
4443  // TODO: see why the marker renderer doesn't seem to translate offset *after* applying rotation
4444  //map["offset"] = QgsSymbolLayerV2Utils::encodePoint( tmpLyr.shapeOffset );
4445  //map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
4446  // tmpLyr.shapeOffsetUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
4447 
4448  map["fill"] = tmpLyr.shapeFillColor.name();
4449  map["outline"] = tmpLyr.shapeBorderColor.name();
4450  map["outline-width"] = QString::number( tmpLyr.shapeBorderWidth );
4451 
4452  // TODO: fix overriding SVG symbol's border width/units in QgsSvgCache
4453  // currently broken, fall back to symbol's
4454  //map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
4455  // tmpLyr.shapeBorderWidthUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
4456 
4457  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowShape )
4458  {
4459  // configure SVG shadow specs
4460  QgsStringMap shdwmap( map );
4461  shdwmap["fill"] = tmpLyr.shadowColor.name();
4462  shdwmap["outline"] = tmpLyr.shadowColor.name();
4463  shdwmap["size"] = QString::number( sizeOut * tmpLyr.rasterCompressFactor );
4464 
4465  // store SVG's drawing in QPicture for drop shadow call
4466  QPicture svgPict;
4467  QPainter svgp;
4468  svgp.begin( &svgPict );
4469 
4470  // draw shadow symbol
4471 
4472  // clone current render context map unit/mm conversion factors, but not
4473  // other map canvas parameters, then substitute this painter for use in symbology painting
4474  // NOTE: this is because the shadow needs to be scaled correctly for output to map canvas,
4475  // but will be created relative to the SVG's computed size, not the current map canvas
4476  QgsRenderContext shdwContext;
4477  shdwContext.setMapToPixel( context.mapToPixel() );
4478  shdwContext.setScaleFactor( context.scaleFactor() );
4479  shdwContext.setPainter( &svgp );
4480 
4481  QgsSymbolLayerV2* symShdwL = QgsSvgMarkerSymbolLayerV2::create( shdwmap );
4482  QgsSvgMarkerSymbolLayerV2* svgShdwM = static_cast<QgsSvgMarkerSymbolLayerV2*>( symShdwL );
4483  QgsSymbolV2RenderContext svgShdwContext( shdwContext, QgsSymbolV2::Mixed,
4484  ( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
4485 
4486  double svgSize = tmpLyr.scaleToPixelContext( sizeOut, context, tmpLyr.shapeSizeUnits, true );
4487  svgShdwM->renderPoint( QPointF( svgSize / 2, -svgSize / 2 ), svgShdwContext );
4488  svgp.end();
4489 
4490  component.setPicture( &svgPict );
4491  // TODO: when SVG symbol's border width/units is fixed in QgsSvgCache, adjust for it here
4492  component.setPictureBuffer( 0.0 );
4493 
4494  component.setSize( QgsPoint( svgSize, svgSize ) );
4495  component.setOffset( QgsPoint( 0.0, 0.0 ) );
4496 
4497  // rotate about origin center of SVG
4498  p->save();
4499  p->translate( component.center().x(), component.center().y() );
4500  p->rotate( component.rotation() );
4501  p->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
4502  double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits, true );
4503  double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits, true );
4504  p->translate( QPointF( xoff, yoff ) );
4505  p->rotate( component.rotationOffset() );
4506  p->translate( -svgSize / 2, svgSize / 2 );
4507 
4508  drawLabelShadow( context, component, tmpLyr );
4509  p->restore();
4510 
4511  delete svgShdwM;
4512  svgShdwM = 0;
4513  }
4514 
4515  // draw the actual symbol
4517  QgsSvgMarkerSymbolLayerV2* svgM = static_cast<QgsSvgMarkerSymbolLayerV2*>( symL );
4518  QgsSymbolV2RenderContext svgContext( context, QgsSymbolV2::Mixed,
4519  ( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
4520 
4521  p->save();
4522  if ( context.useAdvancedEffects() )
4523  {
4524  p->setCompositionMode( tmpLyr.shapeBlendMode );
4525  }
4526  p->translate( component.center().x(), component.center().y() );
4527  p->rotate( component.rotation() );
4528  double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits );
4529  double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits );
4530  p->translate( QPointF( xoff, yoff ) );
4531  p->rotate( component.rotationOffset() );
4532  svgM->renderPoint( QPointF( 0, 0 ), svgContext );
4533  p->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
4534  p->restore();
4535 
4536  delete svgM;
4537  svgM = 0;
4538 
4539  }
4540  else // Generated Shapes
4541  {
4542  // all calculations done in shapeSizeUnits
4543 
4544  double w = labelWidth / ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM ? mmToMapUnits : 1 );
4545  double h = labelHeight / ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM ? mmToMapUnits : 1 );
4546 
4547  double xsize = tmpLyr.shapeSize.x();
4548  double ysize = tmpLyr.shapeSize.y();
4549 
4551  {
4552  w = xsize;
4553  h = ysize;
4554  }
4555  else if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeBuffer )
4556  {
4558  {
4559  if ( w > h )
4560  h = w;
4561  else if ( h > w )
4562  w = h;
4563  }
4564  else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeCircle )
4565  {
4566  // start with label bound by circle
4567  h = sqrt( pow( w, 2 ) + pow( h, 2 ) );
4568  w = h;
4569  }
4570  else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeEllipse )
4571  {
4572  // start with label bound by ellipse
4573  h = h / sqrt( 2.0 ) * 2;
4574  w = w / sqrt( 2.0 ) * 2;
4575  }
4576 
4577  w += xsize * 2;
4578  h += ysize * 2;
4579  }
4580 
4581  // convert everything over to map pixels from here on
4582  w = tmpLyr.scaleToPixelContext( w, context, tmpLyr.shapeSizeUnits, true );
4583  h = tmpLyr.scaleToPixelContext( h, context, tmpLyr.shapeSizeUnits, true );
4584 
4585  // offsets match those of symbology: -x = left, -y = up
4586  QRectF rect( -w / 2.0, - h / 2.0, w, h );
4587 
4588  if ( rect.isNull() )
4589  return;
4590 
4591  p->save();
4592  p->translate( QPointF( component.center().x(), component.center().y() ) );
4593  p->rotate( component.rotation() );
4594  double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits, false );
4595  double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits, false );
4596  p->translate( QPointF( xoff, yoff ) );
4597  p->rotate( component.rotationOffset() );
4598 
4599  double penSize = tmpLyr.scaleToPixelContext( tmpLyr.shapeBorderWidth, context, tmpLyr.shapeBorderWidthUnits, true );
4600 
4601  QPen pen;
4602  if ( tmpLyr.shapeBorderWidth > 0 )
4603  {
4604  pen.setColor( tmpLyr.shapeBorderColor );
4605  pen.setWidthF( penSize );
4607  pen.setJoinStyle( tmpLyr.shapeJoinStyle );
4608  }
4609  else
4610  {
4611  pen = Qt::NoPen;
4612  }
4613 
4614  // store painting in QPicture for shadow drawing
4615  QPicture shapePict;
4616  QPainter shapep;
4617  shapep.begin( &shapePict );
4618  shapep.setPen( pen );
4619  shapep.setBrush( tmpLyr.shapeFillColor );
4620 
4623  {
4625  {
4626  shapep.drawRoundedRect( rect, tmpLyr.shapeRadii.x(), tmpLyr.shapeRadii.y(), Qt::RelativeSize );
4627  }
4628  else
4629  {
4630  double xRadius = tmpLyr.scaleToPixelContext( tmpLyr.shapeRadii.x(), context, tmpLyr.shapeRadiiUnits, true );
4631  double yRadius = tmpLyr.scaleToPixelContext( tmpLyr.shapeRadii.y(), context, tmpLyr.shapeRadiiUnits, true );
4632  shapep.drawRoundedRect( rect, xRadius, yRadius );
4633  }
4634  }
4635  else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeEllipse
4637  {
4638  shapep.drawEllipse( rect );
4639  }
4640  shapep.end();
4641 
4642  p->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
4643 
4644  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowShape )
4645  {
4646  component.setPicture( &shapePict );
4647  component.setPictureBuffer( penSize / 2.0 );
4648 
4649  component.setSize( QgsPoint( rect.width(), rect.height() ) );
4650  component.setOffset( QgsPoint( rect.width() / 2, -rect.height() / 2 ) );
4651  drawLabelShadow( context, component, tmpLyr );
4652  }
4653 
4654  p->setOpacity(( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
4655  if ( context.useAdvancedEffects() )
4656  {
4657  p->setCompositionMode( tmpLyr.shapeBlendMode );
4658  }
4659 
4660  // scale for any print output or image saving @ specific dpi
4661  p->scale( component.dpiRatio(), component.dpiRatio() );
4662  p->drawPicture( 0, 0, shapePict );
4663  p->restore();
4664  }
4665 }
4666 
4668  QgsLabelComponent component,
4669  const QgsPalLayerSettings& tmpLyr )
4670 {
4671  // incoming component sizes should be multiplied by rasterCompressFactor, as
4672  // this allows shadows to be created at paint device dpi (e.g. high resolution),
4673  // then scale device painter by 1.0 / rasterCompressFactor for output
4674 
4675  QPainter* p = context.painter();
4676  double componentWidth = component.size().x(), componentHeight = component.size().y();
4677  double xOffset = component.offset().x(), yOffset = component.offset().y();
4678  double pictbuffer = component.pictureBuffer();
4679 
4680  // generate pixmap representation of label component drawing
4681  bool mapUnits = ( tmpLyr.shadowRadiusUnits == QgsPalLayerSettings::MapUnits );
4682  double radius = tmpLyr.scaleToPixelContext( tmpLyr.shadowRadius , context, tmpLyr.shadowRadiusUnits, !mapUnits );
4683  radius /= ( mapUnits ? tmpLyr.vectorScaleFactor / component.dpiRatio() : 1 );
4684  radius = ( int )( radius + 0.5 );
4685 
4686  // TODO: add labeling gui option to adjust blurBufferClippingScale to minimize pixels, or
4687  // to ensure shadow isn't clipped too tight. (Or, find a better method of buffering)
4688  double blurBufferClippingScale = 3.75;
4689  int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
4690 
4691  QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
4692  componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
4693  QImage::Format_ARGB32_Premultiplied );
4694 
4695  // TODO: add labeling gui option to not show any shadows under/over a certian size
4696  // keep very small QImages from causing paint device issues, i.e. must be at least > 1
4697  int minBlurImgSize = 1;
4698  // max limitation on QgsSvgCache is 10,000 for screen, which will probably be reasonable for future caching here, too
4699  // 4 x QgsSvgCache limit for output to print/image at higher dpi
4700  // TODO: should it be higher, scale with dpi, or have no limit? Needs testing with very large labels rendered at high dpi output
4701  int maxBlurImgSize = 40000;
4702  if ( blurImg.isNull()
4703  || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
4704  || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
4705  return;
4706 
4707  blurImg.fill( QColor( Qt::transparent ).rgba() );
4708  QPainter pictp;
4709  if ( !pictp.begin( &blurImg ) )
4710  return;
4711  pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
4712  QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
4713  blurbuffer + pictbuffer + componentHeight + yOffset );
4714 
4715  pictp.drawPicture( imgOffset,
4716  *component.picture() );
4717 
4718  // overlay shadow color
4719  pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
4720  pictp.fillRect( blurImg.rect(), tmpLyr.shadowColor );
4721  pictp.end();
4722 
4723  // blur the QImage in-place
4724  if ( tmpLyr.shadowRadius > 0.0 && radius > 0 )
4725  {
4726  QgsSymbolLayerV2Utils::blurImageInPlace( blurImg, blurImg.rect(), radius, tmpLyr.shadowRadiusAlphaOnly );
4727  }
4728 
4729  if ( tmpLyr.showingShadowRects ) // engine setting, not per layer
4730  {
4731  // debug rect for QImage shadow registration and clipping visualization
4732  QPainter picti;
4733  picti.begin( &blurImg );
4734  picti.setBrush( Qt::Dense7Pattern );
4735  QPen imgPen( QColor( 0, 0, 255, 255 ) );
4736  imgPen.setWidth( 1 );
4737  picti.setPen( imgPen );
4738  picti.setOpacity( 0.1 );
4739  picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
4740  picti.end();
4741  }
4742 
4743  double offsetDist = tmpLyr.scaleToPixelContext( tmpLyr.shadowOffsetDist, context, tmpLyr.shadowOffsetUnits, true );
4744  double angleRad = tmpLyr.shadowOffsetAngle * M_PI / 180; // to radians
4745  if ( tmpLyr.shadowOffsetGlobal )
4746  {
4747  // TODO: check for differences in rotation origin and cw/ccw direction,
4748  // when this shadow function is used for something other than labels
4749 
4750  // it's 0-->cw-->360 for labels
4751  //QgsDebugMsgLevel( QString( "Shadow aggregated label rotation (degrees): %1" ).arg( component.rotation() + component.rotationOffset() ), 4 );
4752  angleRad -= ( component.rotation() * M_PI / 180 + component.rotationOffset() * M_PI / 180 );
4753  }
4754 
4755  QPointF transPt( -offsetDist * cos( angleRad + M_PI / 2 ),
4756  -offsetDist * sin( angleRad + M_PI / 2 ) );
4757 
4758  p->save();
4759  p->setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
4760  if ( context.useAdvancedEffects() )
4761  {
4762  p->setCompositionMode( tmpLyr.shadowBlendMode );
4763  }
4764  p->setOpacity(( 100.0 - ( double )( tmpLyr.shadowTransparency ) ) / 100.0 );
4765 
4766  double scale = ( double )tmpLyr.shadowScale / 100.0;
4767  // TODO: scale from center/center, left/center or left/top, instead of default left/bottom?
4768  p->scale( scale, scale );
4769  if ( component.useOrigin() )
4770  {
4771  p->translate( component.origin().x(), component.origin().y() );
4772  }
4773  p->translate( transPt );
4774  p->translate( -imgOffset.x(),
4775  -imgOffset.y() );
4776  p->drawImage( 0, 0, blurImg );
4777  p->restore();
4778 
4779  // debug rects
4780  if ( tmpLyr.showingShadowRects ) // engine setting, not per layer
4781  {
4782  // draw debug rect for QImage painting registration
4783  p->save();
4784  p->setBrush( Qt::NoBrush );
4785  QPen imgPen( QColor( 255, 0, 0, 10 ) );
4786  imgPen.setWidth( 2 );
4787  imgPen.setStyle( Qt::DashLine );
4788  p->setPen( imgPen );
4789  p->scale( scale, scale );
4790  if ( component.useOrigin() )
4791  {
4792  p->translate( component.origin().x(), component.origin().y() );
4793  }
4794  p->translate( transPt );
4795  p->translate( -imgOffset.x(),
4796  -imgOffset.y() );
4797  p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
4798  p->restore();
4799 
4800  // draw debug rect for passed in component dimensions
4801  p->save();
4802  p->setBrush( Qt::NoBrush );
4803  QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
4804  componentRectPen.setWidth( 1 );
4805  if ( component.useOrigin() )
4806  {
4807  p->translate( component.origin().x(), component.origin().y() );
4808  }
4809  p->setPen( componentRectPen );
4810  p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
4811  p->restore();
4812  }
4813 }
4814 
4816 {
4817  // start with engine defaults for new project, or project that has no saved settings
4818  Pal p;
4819  bool saved = false;
4821  "PAL", "/SearchMethod", ( int )p.getSearch(), &saved ) );
4823  "PAL", "/CandidatesPoint", p.getPointP(), &saved );
4825  "PAL", "/CandidatesLine", p.getLineP(), &saved );
4827  "PAL", "/CandidatesPolygon", p.getPolyP(), &saved );
4829  "PAL", "/ShowingCandidates", false, &saved );
4831  "PAL", "/ShowingShadowRects", false, &saved );
4833  "PAL", "/ShowingAllLabels", false, &saved );
4835  "PAL", "/ShowingPartialsLabels", p.getShowPartial(), &saved );
4836  mSavedWithProject = saved;
4837 }
4838 
4840 {
4841  QgsProject::instance()->writeEntry( "PAL", "/SearchMethod", ( int )mSearch );
4842  QgsProject::instance()->writeEntry( "PAL", "/CandidatesPoint", mCandPoint );
4843  QgsProject::instance()->writeEntry( "PAL", "/CandidatesLine", mCandLine );
4844  QgsProject::instance()->writeEntry( "PAL", "/CandidatesPolygon", mCandPolygon );
4845  QgsProject::instance()->writeEntry( "PAL", "/ShowingCandidates", mShowingCandidates );
4846  QgsProject::instance()->writeEntry( "PAL", "/ShowingShadowRects", mShowingShadowRects );
4847  QgsProject::instance()->writeEntry( "PAL", "/ShowingAllLabels", mShowingAllLabels );
4848  QgsProject::instance()->writeEntry( "PAL", "/ShowingPartialsLabels", mShowingPartialsLabels );
4849  mSavedWithProject = true;
4850 }
4851 
4853 {
4854  QgsProject::instance()->removeEntry( "PAL", "/SearchMethod" );
4855  QgsProject::instance()->removeEntry( "PAL", "/CandidatesPoint" );
4856  QgsProject::instance()->removeEntry( "PAL", "/CandidatesLine" );
4857  QgsProject::instance()->removeEntry( "PAL", "/CandidatesPolygon" );
4858  QgsProject::instance()->removeEntry( "PAL", "/ShowingCandidates" );
4859  QgsProject::instance()->removeEntry( "PAL", "/ShowingShadowRects" );
4860  QgsProject::instance()->removeEntry( "PAL", "/ShowingAllLabels" );
4861  QgsProject::instance()->removeEntry( "PAL", "/ShowingPartialsLabels" );
4862  mSavedWithProject = false;
4863 }
4864 
4866 {
4867  QgsPalLabeling* lbl = new QgsPalLabeling();
4872  return lbl;
4873 }