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