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