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