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