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