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