QGIS API Documentation  2.11.0-Master
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 <cmath>
32 
33 #include <QApplication>
34 #include <QByteArray>
35 #include <QString>
36 #include <QFontMetrics>
37 #include <QTime>
38 #include <QPainter>
39 
40 #include "diagram/qgsdiagram.h"
41 #include "qgsdiagramrendererv2.h"
42 #include "qgsfontutils.h"
43 #include "qgslabelsearchtree.h"
44 #include "qgsexpression.h"
45 #include "qgsdatadefined.h"
46 
47 #include <qgslogger.h>
48 #include <qgsvectorlayer.h>
49 #include <qgsmaplayerregistry.h>
50 #include <qgsvectordataprovider.h>
51 #include <qgsgeometry.h>
52 #include <qgsmaprenderer.h>
53 #include <qgsmarkersymbollayerv2.h>
54 #include <qgsproject.h>
55 #include "qgssymbolv2.h"
56 #include "qgssymbollayerv2utils.h"
57 #include <QMessageBox>
58 
59 
60 Q_GUI_EXPORT extern int qt_defaultDpiX();
61 Q_GUI_EXPORT extern int qt_defaultDpiY();
62 
63 static void _fixQPictureDPI( QPainter* p )
64 {
65  // QPicture makes an assumption that we drawing to it with system DPI.
66  // Then when being drawn, it scales the painter. The following call
67  // negates the effect. There is no way of setting QPicture's DPI.
68  // See QTBUG-20361
69  p->scale(( double )qt_defaultDpiX() / p->device()->logicalDpiX(),
70  ( double )qt_defaultDpiY() / p->device()->logicalDpiY() );
71 }
72 
73 
74 using namespace pal;
75 
76 // -------------
77 
79  : upsidedownLabels( Upright )
80  , palLayer( NULL )
81  , mCurFeat( 0 )
82  , xform( NULL )
83  , ct( NULL )
84  , extentGeom( NULL )
85  , mFeaturesToLabel( 0 )
86  , mFeatsSendingToPal( 0 )
87  , mFeatsRegPal( 0 )
88  , expression( 0 )
89 {
90  enabled = false;
91  drawLabels = true;
92  isExpression = false;
93  fieldIndex = 0;
94 
95  // text style
97  textNamedStyle = QString( "" );
98  fontSizeInMapUnits = false;
99  textColor = Qt::black;
100  textTransp = 0;
101  blendMode = QPainter::CompositionMode_SourceOver;
102  previewBkgrdColor = Qt::white;
103  // font processing info
104  mTextFontFound = true;
106 
107  // text formatting
108  wrapChar = "";
109  multilineHeight = 1.0;
111  addDirectionSymbol = false;
112  leftDirectionSymbol = QString( "<" );
113  rightDirectionSymbol = QString( ">" );
114  reverseDirectionSymbol = false;
116  formatNumbers = false;
117  decimals = 3;
118  plusSign = false;
119 
120  // text buffer
121  bufferDraw = false;
122  bufferSize = 1.0;
123  bufferSizeInMapUnits = false;
124  bufferColor = Qt::white;
125  bufferTransp = 0;
126  bufferNoFill = false;
127  bufferJoinStyle = Qt::BevelJoin;
128  bufferBlendMode = QPainter::CompositionMode_SourceOver;
129 
130  // shape background
131  shapeDraw = false;
133  shapeSVGFile = QString();
135  shapeSize = QPointF( 0.0, 0.0 );
136  shapeSizeUnits = MM;
138  shapeRotation = 0.0;
139  shapeOffset = QPointF( 0.0, 0.0 );
141  shapeRadii = QPointF( 0.0, 0.0 );
143  shapeFillColor = Qt::white;
144  shapeBorderColor = Qt::darkGray;
145  shapeBorderWidth = 0.0;
147  shapeJoinStyle = Qt::BevelJoin;
148  shapeTransparency = 0;
149  shapeBlendMode = QPainter::CompositionMode_SourceOver;
150 
151  // drop shadow
152  shadowDraw = false;
154  shadowOffsetAngle = 135;
155  shadowOffsetDist = 1.0;
157  shadowOffsetGlobal = true;
158  shadowRadius = 1.5;
160  shadowRadiusAlphaOnly = false;
161  shadowTransparency = 30;
162  shadowScale = 100;
163  shadowColor = Qt::black;
164  shadowBlendMode = QPainter::CompositionMode_Multiply;
165 
166  // placement
169  centroidWhole = false;
170  centroidInside = false;
171  fitInPolygonOnly = false;
173  xOffset = 0;
174  yOffset = 0;
175  labelOffsetInMapUnits = true;
176  dist = 0;
177  distInMapUnits = false;
178  angleOffset = 0;
179  preserveRotation = true;
180  maxCurvedCharAngleIn = 20.0;
181  maxCurvedCharAngleOut = -20.0;
182  priority = 5;
183  repeatDistance = 0;
185 
186  // rendering
187  scaleVisibility = false;
188  scaleMin = 1;
189  scaleMax = 10000000;
190  fontLimitPixelSize = false;
191  fontMinPixelSize = 0; //trigger to turn it on by default for map unit labels
192  fontMaxPixelSize = 10000;
193  displayAll = false;
195 
196  labelPerPart = false;
197  mergeLines = false;
198  minFeatureSize = 0.0;
199  limitNumLabels = false;
200  maxNumLabels = 2000;
201  obstacle = true;
202  obstacleFactor = 1.0;
204 
205  // scale factors
206  vectorScaleFactor = 1.0;
207  rasterCompressFactor = 1.0;
208 
209  // data defined string and old-style index values
210  // NOTE: in QPair use -1 for second value (other values are for old-style layer properties migration)
211 
212  // text style
213  mDataDefinedNames.insert( Size, QPair<QString, int>( "Size", 0 ) );
214  mDataDefinedNames.insert( Bold, QPair<QString, int>( "Bold", 1 ) );
215  mDataDefinedNames.insert( Italic, QPair<QString, int>( "Italic", 2 ) );
216  mDataDefinedNames.insert( Underline, QPair<QString, int>( "Underline", 3 ) );
217  mDataDefinedNames.insert( Color, QPair<QString, int>( "Color", 4 ) );
218  mDataDefinedNames.insert( Strikeout, QPair<QString, int>( "Strikeout", 5 ) );
219  mDataDefinedNames.insert( Family, QPair<QString, int>( "Family", 6 ) );
220  mDataDefinedNames.insert( FontStyle, QPair<QString, int>( "FontStyle", -1 ) );
221  mDataDefinedNames.insert( FontSizeUnit, QPair<QString, int>( "FontSizeUnit", -1 ) );
222  mDataDefinedNames.insert( FontTransp, QPair<QString, int>( "FontTransp", 18 ) );
223  mDataDefinedNames.insert( FontCase, QPair<QString, int>( "FontCase", -1 ) );
224  mDataDefinedNames.insert( FontLetterSpacing, QPair<QString, int>( "FontLetterSpacing", -1 ) );
225  mDataDefinedNames.insert( FontWordSpacing, QPair<QString, int>( "FontWordSpacing", -1 ) );
226  mDataDefinedNames.insert( FontBlendMode, QPair<QString, int>( "FontBlendMode", -1 ) );
227 
228  // text formatting
229  mDataDefinedNames.insert( MultiLineWrapChar, QPair<QString, int>( "MultiLineWrapChar", -1 ) );
230  mDataDefinedNames.insert( MultiLineHeight, QPair<QString, int>( "MultiLineHeight", -1 ) );
231  mDataDefinedNames.insert( MultiLineAlignment, QPair<QString, int>( "MultiLineAlignment", -1 ) );
232  mDataDefinedNames.insert( DirSymbDraw, QPair<QString, int>( "DirSymbDraw", -1 ) );
233  mDataDefinedNames.insert( DirSymbLeft, QPair<QString, int>( "DirSymbLeft", -1 ) );
234  mDataDefinedNames.insert( DirSymbRight, QPair<QString, int>( "DirSymbRight", -1 ) );
235  mDataDefinedNames.insert( DirSymbPlacement, QPair<QString, int>( "DirSymbPlacement", -1 ) );
236  mDataDefinedNames.insert( DirSymbReverse, QPair<QString, int>( "DirSymbReverse", -1 ) );
237  mDataDefinedNames.insert( NumFormat, QPair<QString, int>( "NumFormat", -1 ) );
238  mDataDefinedNames.insert( NumDecimals, QPair<QString, int>( "NumDecimals", -1 ) );
239  mDataDefinedNames.insert( NumPlusSign, QPair<QString, int>( "NumPlusSign", -1 ) );
240 
241  // text buffer
242  mDataDefinedNames.insert( BufferDraw, QPair<QString, int>( "BufferDraw", -1 ) );
243  mDataDefinedNames.insert( BufferSize, QPair<QString, int>( "BufferSize", 7 ) );
244  mDataDefinedNames.insert( BufferUnit, QPair<QString, int>( "BufferUnit", -1 ) );
245  mDataDefinedNames.insert( BufferColor, QPair<QString, int>( "BufferColor", 8 ) );
246  mDataDefinedNames.insert( BufferTransp, QPair<QString, int>( "BufferTransp", 19 ) );
247  mDataDefinedNames.insert( BufferJoinStyle, QPair<QString, int>( "BufferJoinStyle", -1 ) );
248  mDataDefinedNames.insert( BufferBlendMode, QPair<QString, int>( "BufferBlendMode", -1 ) );
249 
250  // background
251  mDataDefinedNames.insert( ShapeDraw, QPair<QString, int>( "ShapeDraw", -1 ) );
252  mDataDefinedNames.insert( ShapeKind, QPair<QString, int>( "ShapeKind", -1 ) );
253  mDataDefinedNames.insert( ShapeSVGFile, QPair<QString, int>( "ShapeSVGFile", -1 ) );
254  mDataDefinedNames.insert( ShapeSizeType, QPair<QString, int>( "ShapeSizeType", -1 ) );
255  mDataDefinedNames.insert( ShapeSizeX, QPair<QString, int>( "ShapeSizeX", -1 ) );
256  mDataDefinedNames.insert( ShapeSizeY, QPair<QString, int>( "ShapeSizeY", -1 ) );
257  mDataDefinedNames.insert( ShapeSizeUnits, QPair<QString, int>( "ShapeSizeUnits", -1 ) );
258  mDataDefinedNames.insert( ShapeRotationType, QPair<QString, int>( "ShapeRotationType", -1 ) );
259  mDataDefinedNames.insert( ShapeRotation, QPair<QString, int>( "ShapeRotation", -1 ) );
260  mDataDefinedNames.insert( ShapeOffset, QPair<QString, int>( "ShapeOffset", -1 ) );
261  mDataDefinedNames.insert( ShapeOffsetUnits, QPair<QString, int>( "ShapeOffsetUnits", -1 ) );
262  mDataDefinedNames.insert( ShapeRadii, QPair<QString, int>( "ShapeRadii", -1 ) );
263  mDataDefinedNames.insert( ShapeRadiiUnits, QPair<QString, int>( "ShapeRadiiUnits", -1 ) );
264  mDataDefinedNames.insert( ShapeTransparency, QPair<QString, int>( "ShapeTransparency", -1 ) );
265  mDataDefinedNames.insert( ShapeBlendMode, QPair<QString, int>( "ShapeBlendMode", -1 ) );
266  mDataDefinedNames.insert( ShapeFillColor, QPair<QString, int>( "ShapeFillColor", -1 ) );
267  mDataDefinedNames.insert( ShapeBorderColor, QPair<QString, int>( "ShapeBorderColor", -1 ) );
268  mDataDefinedNames.insert( ShapeBorderWidth, QPair<QString, int>( "ShapeBorderWidth", -1 ) );
269  mDataDefinedNames.insert( ShapeBorderWidthUnits, QPair<QString, int>( "ShapeBorderWidthUnits", -1 ) );
270  mDataDefinedNames.insert( ShapeJoinStyle, QPair<QString, int>( "ShapeJoinStyle", -1 ) );
271 
272  // drop shadow
273  mDataDefinedNames.insert( ShadowDraw, QPair<QString, int>( "ShadowDraw", -1 ) );
274  mDataDefinedNames.insert( ShadowUnder, QPair<QString, int>( "ShadowUnder", -1 ) );
275  mDataDefinedNames.insert( ShadowOffsetAngle, QPair<QString, int>( "ShadowOffsetAngle", -1 ) );
276  mDataDefinedNames.insert( ShadowOffsetDist, QPair<QString, int>( "ShadowOffsetDist", -1 ) );
277  mDataDefinedNames.insert( ShadowOffsetUnits, QPair<QString, int>( "ShadowOffsetUnits", -1 ) );
278  mDataDefinedNames.insert( ShadowRadius, QPair<QString, int>( "ShadowRadius", -1 ) );
279  mDataDefinedNames.insert( ShadowRadiusUnits, QPair<QString, int>( "ShadowRadiusUnits", -1 ) );
280  mDataDefinedNames.insert( ShadowTransparency, QPair<QString, int>( "ShadowTransparency", -1 ) );
281  mDataDefinedNames.insert( ShadowScale, QPair<QString, int>( "ShadowScale", -1 ) );
282  mDataDefinedNames.insert( ShadowColor, QPair<QString, int>( "ShadowColor", -1 ) );
283  mDataDefinedNames.insert( ShadowBlendMode, QPair<QString, int>( "ShadowBlendMode", -1 ) );
284 
285  // placement
286  mDataDefinedNames.insert( CentroidWhole, QPair<QString, int>( "CentroidWhole", -1 ) );
287  mDataDefinedNames.insert( OffsetQuad, QPair<QString, int>( "OffsetQuad", -1 ) );
288  mDataDefinedNames.insert( OffsetXY, QPair<QString, int>( "OffsetXY", -1 ) );
289  mDataDefinedNames.insert( OffsetUnits, QPair<QString, int>( "OffsetUnits", -1 ) );
290  mDataDefinedNames.insert( LabelDistance, QPair<QString, int>( "LabelDistance", 13 ) );
291  mDataDefinedNames.insert( DistanceUnits, QPair<QString, int>( "DistanceUnits", -1 ) );
292  mDataDefinedNames.insert( OffsetRotation, QPair<QString, int>( "OffsetRotation", -1 ) );
293  mDataDefinedNames.insert( CurvedCharAngleInOut, QPair<QString, int>( "CurvedCharAngleInOut", -1 ) );
294  mDataDefinedNames.insert( RepeatDistance, QPair<QString, int>( "RepeatDistance", -1 ) );
295  mDataDefinedNames.insert( RepeatDistanceUnit, QPair<QString, int>( "RepeatDistanceUnit", -1 ) );
296  mDataDefinedNames.insert( Priority, QPair<QString, int>( "Priority", -1 ) );
297  mDataDefinedNames.insert( IsObstacle, QPair<QString, int>( "IsObstacle", -1 ) );
298  mDataDefinedNames.insert( ObstacleFactor, QPair<QString, int>( "ObstacleFactor", -1 ) );
299 
300  // (data defined only)
301  mDataDefinedNames.insert( PositionX, QPair<QString, int>( "PositionX", 9 ) );
302  mDataDefinedNames.insert( PositionY, QPair<QString, int>( "PositionY", 10 ) );
303  mDataDefinedNames.insert( Hali, QPair<QString, int>( "Hali", 11 ) );
304  mDataDefinedNames.insert( Vali, QPair<QString, int>( "Vali", 12 ) );
305  mDataDefinedNames.insert( Rotation, QPair<QString, int>( "Rotation", 14 ) );
306 
307  //rendering
308  mDataDefinedNames.insert( ScaleVisibility, QPair<QString, int>( "ScaleVisibility", -1 ) );
309  mDataDefinedNames.insert( MinScale, QPair<QString, int>( "MinScale", 16 ) );
310  mDataDefinedNames.insert( MaxScale, QPair<QString, int>( "MaxScale", 17 ) );
311  mDataDefinedNames.insert( FontLimitPixel, QPair<QString, int>( "FontLimitPixel", -1 ) );
312  mDataDefinedNames.insert( FontMinPixel, QPair<QString, int>( "FontMinPixel", -1 ) );
313  mDataDefinedNames.insert( FontMaxPixel, QPair<QString, int>( "FontMaxPixel", -1 ) );
314  // (data defined only)
315  mDataDefinedNames.insert( Show, QPair<QString, int>( "Show", 15 ) );
316  mDataDefinedNames.insert( AlwaysShow, QPair<QString, int>( "AlwaysShow", 20 ) );
317 
318  // temp stuff for when drawing label components (don't copy)
319  showingShadowRects = false;
320 }
321 
323  : palLayer( NULL )
324  , mCurFeat( NULL )
325  , fieldIndex( 0 )
326  , xform( NULL )
327  , ct( NULL )
328  , extentGeom( NULL )
329  , mFeaturesToLabel( 0 )
330  , mFeatsSendingToPal( 0 )
331  , mFeatsRegPal( 0 )
332  , showingShadowRects( false )
333  , expression( NULL )
334 {
335  // copy only permanent stuff
336 
337  enabled = s.enabled;
339 
340  // text style
341  fieldName = s.fieldName;
343  textFont = s.textFont;
347  textColor = s.textColor;
349  blendMode = s.blendMode;
351  // font processing info
354 
355  // text formatting
356  wrapChar = s.wrapChar;
365  decimals = s.decimals;
366  plusSign = s.plusSign;
367 
368  // text buffer
378 
379  // placement
380  placement = s.placement;
386  xOffset = s.xOffset;
387  yOffset = s.yOffset;
390  dist = s.dist;
397  priority = s.priority;
401 
402  // rendering
404  scaleMin = s.scaleMin;
405  scaleMax = s.scaleMax;
411 
417  obstacle = s.obstacle;
420 
421  // shape background
422  shapeDraw = s.shapeDraw;
423  shapeType = s.shapeType;
426  shapeSize = s.shapeSize;
445 
446  // drop shadow
462 
463  // data defined
465  mDataDefinedNames = s.mDataDefinedNames;
466 
467  // scale factors
470 }
471 
472 
474 {
475  // pal layer is deleted internally in PAL
476 
477  delete ct;
478  delete expression;
479  delete extentGeom;
480 
481  // clear pointers to QgsDataDefined objects
483 }
484 
485 
487 {
488  QgsPalLayerSettings settings;
489  settings.readFromLayer( layer );
490  return settings;
491 }
492 
493 
495 {
496  if ( expression == NULL )
497  {
498  expression = new QgsExpression( fieldName );
499  }
500  return expression;
501 }
502 
503 static QColor _readColor( QgsVectorLayer* layer, QString property, QColor defaultColor = Qt::black, bool withAlpha = true )
504 {
505  int r = layer->customProperty( property + "R", QVariant( defaultColor.red() ) ).toInt();
506  int g = layer->customProperty( property + "G", QVariant( defaultColor.green() ) ).toInt();
507  int b = layer->customProperty( property + "B", QVariant( defaultColor.blue() ) ).toInt();
508  int a = withAlpha ? layer->customProperty( property + "A", QVariant( defaultColor.alpha() ) ).toInt() : 255;
509  return QColor( r, g, b, a );
510 }
511 
512 static void _writeColor( QgsVectorLayer* layer, QString property, QColor color, bool withAlpha = true )
513 {
514  layer->setCustomProperty( property + "R", color.red() );
515  layer->setCustomProperty( property + "G", color.green() );
516  layer->setCustomProperty( property + "B", color.blue() );
517  if ( withAlpha )
518  layer->setCustomProperty( property + "A", color.alpha() );
519 }
520 
522 {
523  if ( str.compare( "Point", Qt::CaseInsensitive ) == 0
524  || str.compare( "Points", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Points;
525  if ( str.compare( "MapUnit", Qt::CaseInsensitive ) == 0
526  || str.compare( "MapUnits", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::MapUnits;
527  if ( str.compare( "Percent", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Percent;
528  return QgsPalLayerSettings::MM; // "MM"
529 }
530 
531 static Qt::PenJoinStyle _decodePenJoinStyle( const QString& str )
532 {
533  if ( str.compare( "Miter", Qt::CaseInsensitive ) == 0 ) return Qt::MiterJoin;
534  if ( str.compare( "Round", Qt::CaseInsensitive ) == 0 ) return Qt::RoundJoin;
535  return Qt::BevelJoin; // "Bevel"
536 }
537 
538 void QgsPalLayerSettings::readDataDefinedPropertyMap( QgsVectorLayer* layer,
540 {
541  if ( !layer )
542  {
543  return;
544  }
545 
547  while ( i.hasNext() )
548  {
549  i.next();
550  readDataDefinedProperty( layer, i.key(), propertyMap );
551  }
552 }
553 
554 void QgsPalLayerSettings::writeDataDefinedPropertyMap( QgsVectorLayer* layer,
556 {
557  if ( !layer )
558  {
559  return;
560  }
561 
563  while ( i.hasNext() )
564  {
565  i.next();
566  QString newPropertyName = "labeling/dataDefined/" + i.value().first;
567  QVariant propertyValue = QVariant();
568 
570  if ( it != propertyMap.constEnd() )
571  {
572  QgsDataDefined* dd = it.value();
573  if ( dd )
574  {
575  bool active = dd->isActive();
576  bool useExpr = dd->useExpression();
577  QString expr = dd->expressionString();
578  QString field = dd->field();
579 
580  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
581 
582  if ( !defaultVals )
583  {
584  // TODO: update this when project settings for labeling are migrated to better XML layout
585  QStringList values;
586  values << ( active ? "1" : "0" );
587  values << ( useExpr ? "1" : "0" );
588  values << expr;
589  values << field;
590  if ( !values.isEmpty() )
591  {
592  propertyValue = QVariant( values.join( "~~" ) );
593  }
594  }
595  }
596  }
597 
598  if ( propertyValue.isValid() )
599  {
600  layer->setCustomProperty( newPropertyName, propertyValue );
601  }
602  else
603  {
604  // remove unused properties
605  layer->removeCustomProperty( newPropertyName );
606  }
607 
608  if ( layer->customProperty( newPropertyName, QVariant() ).isValid() && i.value().second > -1 )
609  {
610  // remove old-style field index-based property, if still present
611  layer->removeCustomProperty( QString( "labeling/dataDefinedProperty" ) + QString::number( i.value().second ) );
612  }
613  }
614 }
615 
616 void QgsPalLayerSettings::readDataDefinedProperty( QgsVectorLayer* layer,
619 {
620  QString newPropertyName = "labeling/dataDefined/" + mDataDefinedNames.value( p ).first;
621  QVariant newPropertyField = layer->customProperty( newPropertyName, QVariant() );
622 
623  QString ddString = QString();
624  if ( newPropertyField.isValid() )
625  {
626  ddString = newPropertyField.toString();
627  }
628  else // maybe working with old-style field index-based property (< QGIS 2.0)
629  {
630  int oldIndx = mDataDefinedNames.value( p ).second;
631 
632  if ( oldIndx < 0 ) // something went wrong and we are working with new-style
633  {
634  return;
635  }
636 
637  QString oldPropertyName = "labeling/dataDefinedProperty" + QString::number( oldIndx );
638  QVariant oldPropertyField = layer->customProperty( oldPropertyName, QVariant() );
639 
640  if ( !oldPropertyField.isValid() )
641  {
642  return;
643  }
644 
645  // switch from old-style field index- to name-based properties
646  bool conversionOk;
647  int indx = oldPropertyField.toInt( &conversionOk );
648 
649  if ( conversionOk )
650  {
651  // Fix to migrate from old-style vector api, where returned QMap keys possibly
652  // had 'holes' in sequence of field indices, e.g. 0,2,3
653  // QgsAttrPalIndexNameHash provides a means of access field name in sequences from
654  // providers that procuded holes (e.g. PostGIS skipped geom column), otherwise it is empty
655  QgsAttrPalIndexNameHash oldIndicesToNames = layer->dataProvider()->palAttributeIndexNames();
656 
657  if ( !oldIndicesToNames.isEmpty() )
658  {
659  ddString = oldIndicesToNames.value( indx );
660  }
661  else
662  {
663  QgsFields fields = layer->dataProvider()->fields();
664  if ( indx < fields.size() ) // in case field count has changed
665  {
666  ddString = fields.at( indx ).name();
667  }
668  }
669  }
670 
671  if ( !ddString.isEmpty() )
672  {
673  //upgrade any existing property to field name-based
674  layer->setCustomProperty( newPropertyName, QVariant( updateDataDefinedString( ddString ) ) );
675 
676  // fix for buffer drawing triggered off of just its data defined size in the past (<2.0)
677  if ( oldIndx == 7 ) // old bufferSize enum
678  {
679  bufferDraw = true;
680  layer->setCustomProperty( "labeling/bufferDraw", true );
681  }
682 
683  // fix for scale visibility limits triggered off of just its data defined values in the past (<2.0)
684  if ( oldIndx == 16 || oldIndx == 17 ) // old minScale and maxScale enums
685  {
686  scaleVisibility = true;
687  layer->setCustomProperty( "labeling/scaleVisibility", true );
688  }
689  }
690 
691  // remove old-style field index-based property
692  layer->removeCustomProperty( oldPropertyName );
693  }
694 
695  if ( !ddString.isEmpty() && ddString != QString( "0~~0~~~~" ) )
696  {
697  // TODO: update this when project settings for labeling are migrated to better XML layout
698  QString newStyleString = updateDataDefinedString( ddString );
699  QStringList ddv = newStyleString.split( "~~" );
700 
701  QgsDataDefined* dd = new QgsDataDefined( ddv.at( 0 ).toInt(), ddv.at( 1 ).toInt(), ddv.at( 2 ), ddv.at( 3 ) );
702  propertyMap.insert( p, dd );
703  }
704  else
705  {
706  // remove unused properties
707  layer->removeCustomProperty( newPropertyName );
708  }
709 }
710 
712 {
713  if ( layer->customProperty( "labeling" ).toString() != QString( "pal" ) )
714  {
715  // for polygons the "over point" (over centroid) placement is better than the default
716  // "around point" (around centroid) which is more suitable for points
717  if ( layer->geometryType() == QGis::Polygon )
719 
720  return; // there's no information available
721  }
722 
723  // NOTE: set defaults for newly added properties, for backwards compatibility
724 
725  enabled = layer->labelsEnabled();
726  drawLabels = layer->customProperty( "labeling/drawLabels", true ).toBool();
727 
728  // text style
729  fieldName = layer->customProperty( "labeling/fieldName" ).toString();
730  isExpression = layer->customProperty( "labeling/isExpression" ).toBool();
731  QFont appFont = QApplication::font();
732  mTextFontFamily = layer->customProperty( "labeling/fontFamily", QVariant( appFont.family() ) ).toString();
733  QString fontFamily = mTextFontFamily;
735  {
736  // trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
737  mTextFontFound = false;
738 
739  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
740  // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
741 
742  // for now, do not use matching algorithm for substitution if family not found, substitute default instead
743  fontFamily = appFont.family();
744  }
745 
746  double fontSize = layer->customProperty( "labeling/fontSize" ).toDouble();
747  fontSizeInMapUnits = layer->customProperty( "labeling/fontSizeInMapUnits" ).toBool();
748  fontSizeMapUnitScale.minScale = layer->customProperty( "labeling/fontSizeMapUnitMinScale", 0.0 ).toDouble();
749  fontSizeMapUnitScale.maxScale = layer->customProperty( "labeling/fontSizeMapUnitMaxScale", 0.0 ).toDouble();
750  int fontWeight = layer->customProperty( "labeling/fontWeight" ).toInt();
751  bool fontItalic = layer->customProperty( "labeling/fontItalic" ).toBool();
752  textFont = QFont( fontFamily, fontSize, fontWeight, fontItalic );
753  textFont.setPointSizeF( fontSize ); //double precision needed because of map units
754  textNamedStyle = QgsFontUtils::translateNamedStyle( layer->customProperty( "labeling/namedStyle", QVariant( "" ) ).toString() );
755  QgsFontUtils::updateFontViaStyle( textFont, textNamedStyle ); // must come after textFont.setPointSizeF()
756  textFont.setCapitalization(( QFont::Capitalization )layer->customProperty( "labeling/fontCapitals", QVariant( 0 ) ).toUInt() );
757  textFont.setUnderline( layer->customProperty( "labeling/fontUnderline" ).toBool() );
758  textFont.setStrikeOut( layer->customProperty( "labeling/fontStrikeout" ).toBool() );
759  textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( "labeling/fontLetterSpacing", QVariant( 0.0 ) ).toDouble() );
760  textFont.setWordSpacing( layer->customProperty( "labeling/fontWordSpacing", QVariant( 0.0 ) ).toDouble() );
761  textColor = _readColor( layer, "labeling/textColor", Qt::black, false );
762  textTransp = layer->customProperty( "labeling/textTransp" ).toInt();
764  ( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/blendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() );
765  previewBkgrdColor = QColor( layer->customProperty( "labeling/previewBkgrdColor", QVariant( "#ffffff" ) ).toString() );
766 
767 
768  // text formatting
769  wrapChar = layer->customProperty( "labeling/wrapChar" ).toString();
770  multilineHeight = layer->customProperty( "labeling/multilineHeight", QVariant( 1.0 ) ).toDouble();
771  multilineAlign = ( MultiLineAlign )layer->customProperty( "labeling/multilineAlign", QVariant( MultiLeft ) ).toUInt();
772  addDirectionSymbol = layer->customProperty( "labeling/addDirectionSymbol" ).toBool();
773  leftDirectionSymbol = layer->customProperty( "labeling/leftDirectionSymbol", QVariant( "<" ) ).toString();
774  rightDirectionSymbol = layer->customProperty( "labeling/rightDirectionSymbol", QVariant( ">" ) ).toString();
775  reverseDirectionSymbol = layer->customProperty( "labeling/reverseDirectionSymbol" ).toBool();
776  placeDirectionSymbol = ( DirectionSymbols )layer->customProperty( "labeling/placeDirectionSymbol", QVariant( SymbolLeftRight ) ).toUInt();
777  formatNumbers = layer->customProperty( "labeling/formatNumbers" ).toBool();
778  decimals = layer->customProperty( "labeling/decimals" ).toInt();
779  plusSign = layer->customProperty( "labeling/plussign" ).toInt();
780 
781  // text buffer
782  double bufSize = layer->customProperty( "labeling/bufferSize", QVariant( 0.0 ) ).toDouble();
783 
784  // fix for buffer being keyed off of just its size in the past (<2.0)
785  QVariant drawBuffer = layer->customProperty( "labeling/bufferDraw", QVariant() );
786  if ( drawBuffer.isValid() )
787  {
788  bufferDraw = drawBuffer.toBool();
789  bufferSize = bufSize;
790  }
791  else if ( bufSize != 0.0 )
792  {
793  bufferDraw = true;
794  bufferSize = bufSize;
795  }
796  else
797  {
798  // keep bufferSize at new 1.0 default
799  bufferDraw = false;
800  }
801 
802  bufferSizeInMapUnits = layer->customProperty( "labeling/bufferSizeInMapUnits" ).toBool();
803  bufferSizeMapUnitScale.minScale = layer->customProperty( "labeling/bufferSizeMapUnitMinScale", 0.0 ).toDouble();
804  bufferSizeMapUnitScale.maxScale = layer->customProperty( "labeling/bufferSizeMapUnitMaxScale", 0.0 ).toDouble();
805  bufferColor = _readColor( layer, "labeling/bufferColor", Qt::white, false );
806  bufferTransp = layer->customProperty( "labeling/bufferTransp" ).toInt();
808  ( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/bufferBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() );
809  bufferJoinStyle = ( Qt::PenJoinStyle )layer->customProperty( "labeling/bufferJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt();
810  bufferNoFill = layer->customProperty( "labeling/bufferNoFill", QVariant( false ) ).toBool();
811 
812  // background
813  shapeDraw = layer->customProperty( "labeling/shapeDraw", QVariant( false ) ).toBool();
814  shapeType = ( ShapeType )layer->customProperty( "labeling/shapeType", QVariant( ShapeRectangle ) ).toUInt();
815  shapeSVGFile = layer->customProperty( "labeling/shapeSVGFile", QVariant( "" ) ).toString();
816  shapeSizeType = ( SizeType )layer->customProperty( "labeling/shapeSizeType", QVariant( SizeBuffer ) ).toUInt();
817  shapeSize = QPointF( layer->customProperty( "labeling/shapeSizeX", QVariant( 0.0 ) ).toDouble(),
818  layer->customProperty( "labeling/shapeSizeY", QVariant( 0.0 ) ).toDouble() );
819  shapeSizeUnits = ( SizeUnit )layer->customProperty( "labeling/shapeSizeUnits", QVariant( MM ) ).toUInt();
820  shapeSizeMapUnitScale.minScale = layer->customProperty( "labeling/shapeSizeMapUnitMinScale", 0.0 ).toDouble();
821  shapeSizeMapUnitScale.maxScale = layer->customProperty( "labeling/shapeSizeMapUnitMaxScale", 0.0 ).toDouble();
822  shapeRotationType = ( RotationType )layer->customProperty( "labeling/shapeRotationType", QVariant( RotationSync ) ).toUInt();
823  shapeRotation = layer->customProperty( "labeling/shapeRotation", QVariant( 0.0 ) ).toDouble();
824  shapeOffset = QPointF( layer->customProperty( "labeling/shapeOffsetX", QVariant( 0.0 ) ).toDouble(),
825  layer->customProperty( "labeling/shapeOffsetY", QVariant( 0.0 ) ).toDouble() );
826  shapeOffsetUnits = ( SizeUnit )layer->customProperty( "labeling/shapeOffsetUnits", QVariant( MM ) ).toUInt();
827  shapeOffsetMapUnitScale.minScale = layer->customProperty( "labeling/shapeOffsetMapUnitMinScale", 0.0 ).toDouble();
828  shapeOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/shapeOffsetMapUnitMaxScale", 0.0 ).toDouble();
829  shapeRadii = QPointF( layer->customProperty( "labeling/shapeRadiiX", QVariant( 0.0 ) ).toDouble(),
830  layer->customProperty( "labeling/shapeRadiiY", QVariant( 0.0 ) ).toDouble() );
831  shapeRadiiUnits = ( SizeUnit )layer->customProperty( "labeling/shapeRadiiUnits", QVariant( MM ) ).toUInt();
832  shapeRadiiMapUnitScale.minScale = layer->customProperty( "labeling/shapeRaddiMapUnitMinScale", 0.0 ).toDouble();
833  shapeRadiiMapUnitScale.maxScale = layer->customProperty( "labeling/shapeRaddiMapUnitMaxScale", 0.0 ).toDouble();
834  shapeFillColor = _readColor( layer, "labeling/shapeFillColor", Qt::white, true );
835  shapeBorderColor = _readColor( layer, "labeling/shapeBorderColor", Qt::darkGray, true );
836  shapeBorderWidth = layer->customProperty( "labeling/shapeBorderWidth", QVariant( .0 ) ).toDouble();
837  shapeBorderWidthUnits = ( SizeUnit )layer->customProperty( "labeling/shapeBorderWidthUnits", QVariant( MM ) ).toUInt();
838  shapeBorderWidthMapUnitScale.minScale = layer->customProperty( "labeling/shapeBorderWidthMapUnitMinScale", 0.0 ).toDouble();
839  shapeBorderWidthMapUnitScale.maxScale = layer->customProperty( "labeling/shapeBorderWidthMapUnitMaxScale", 0.0 ).toDouble();
840  shapeJoinStyle = ( Qt::PenJoinStyle )layer->customProperty( "labeling/shapeJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt();
841  shapeTransparency = layer->customProperty( "labeling/shapeTransparency", QVariant( 0 ) ).toInt();
843  ( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/shapeBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() );
844 
845  // drop shadow
846  shadowDraw = layer->customProperty( "labeling/shadowDraw", QVariant( false ) ).toBool();
847  shadowUnder = ( ShadowType )layer->customProperty( "labeling/shadowUnder", QVariant( ShadowLowest ) ).toUInt();//ShadowLowest;
848  shadowOffsetAngle = layer->customProperty( "labeling/shadowOffsetAngle", QVariant( 135 ) ).toInt();
849  shadowOffsetDist = layer->customProperty( "labeling/shadowOffsetDist", QVariant( 1.0 ) ).toDouble();
850  shadowOffsetUnits = ( SizeUnit )layer->customProperty( "labeling/shadowOffsetUnits", QVariant( MM ) ).toUInt();
851  shadowOffsetMapUnitScale.minScale = layer->customProperty( "labeling/shadowOffsetMapUnitMinScale", 0.0 ).toDouble();
852  shadowOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/shadowOffsetMapUnitMaxScale", 0.0 ).toDouble();
853  shadowOffsetGlobal = layer->customProperty( "labeling/shadowOffsetGlobal", QVariant( true ) ).toBool();
854  shadowRadius = layer->customProperty( "labeling/shadowRadius", QVariant( 1.5 ) ).toDouble();
855  shadowRadiusUnits = ( SizeUnit )layer->customProperty( "labeling/shadowRadiusUnits", QVariant( MM ) ).toUInt();
856  shadowRadiusMapUnitScale.minScale = layer->customProperty( "labeling/shadowRadiusMapUnitMinScale", 0.0 ).toDouble();
857  shadowRadiusMapUnitScale.maxScale = layer->customProperty( "labeling/shadowRadiusMapUnitMaxScale", 0.0 ).toDouble();
858  shadowRadiusAlphaOnly = layer->customProperty( "labeling/shadowRadiusAlphaOnly", QVariant( false ) ).toBool();
859  shadowTransparency = layer->customProperty( "labeling/shadowTransparency", QVariant( 30 ) ).toInt();
860  shadowScale = layer->customProperty( "labeling/shadowScale", QVariant( 100 ) ).toInt();
861  shadowColor = _readColor( layer, "labeling/shadowColor", Qt::black, false );
863  ( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/shadowBlendMode", QVariant( QgsMapRenderer::BlendMultiply ) ).toUInt() );
864 
865  // placement
866  placement = ( Placement )layer->customProperty( "labeling/placement" ).toInt();
867  placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt();
868  centroidWhole = layer->customProperty( "labeling/centroidWhole", QVariant( false ) ).toBool();
869  centroidInside = layer->customProperty( "labeling/centroidInside", QVariant( false ) ).toBool();
870  fitInPolygonOnly = layer->customProperty( "labeling/fitInPolygonOnly", QVariant( false ) ).toBool();
871  dist = layer->customProperty( "labeling/dist" ).toDouble();
872  distInMapUnits = layer->customProperty( "labeling/distInMapUnits" ).toBool();
873  distMapUnitScale.minScale = layer->customProperty( "labeling/distMapUnitMinScale", 0.0 ).toDouble();
874  distMapUnitScale.maxScale = layer->customProperty( "labeling/distMapUnitMaxScale", 0.0 ).toDouble();
875  quadOffset = ( QuadrantPosition )layer->customProperty( "labeling/quadOffset", QVariant( QuadrantOver ) ).toUInt();
876  xOffset = layer->customProperty( "labeling/xOffset", QVariant( 0.0 ) ).toDouble();
877  yOffset = layer->customProperty( "labeling/yOffset", QVariant( 0.0 ) ).toDouble();
878  labelOffsetInMapUnits = layer->customProperty( "labeling/labelOffsetInMapUnits", QVariant( true ) ).toBool();
879  labelOffsetMapUnitScale.minScale = layer->customProperty( "labeling/labelOffsetMapUnitMinScale", 0.0 ).toDouble();
880  labelOffsetMapUnitScale.maxScale = layer->customProperty( "labeling/labelOffsetMapUnitMaxScale", 0.0 ).toDouble();
881  angleOffset = layer->customProperty( "labeling/angleOffset", QVariant( 0.0 ) ).toDouble();
882  preserveRotation = layer->customProperty( "labeling/preserveRotation", QVariant( true ) ).toBool();
883  maxCurvedCharAngleIn = layer->customProperty( "labeling/maxCurvedCharAngleIn", QVariant( 20.0 ) ).toDouble();
884  maxCurvedCharAngleOut = layer->customProperty( "labeling/maxCurvedCharAngleOut", QVariant( -20.0 ) ).toDouble();
885  priority = layer->customProperty( "labeling/priority" ).toInt();
886  repeatDistance = layer->customProperty( "labeling/repeatDistance", 0.0 ).toDouble();
887  repeatDistanceUnit = ( SizeUnit ) layer->customProperty( "labeling/repeatDistanceUnit", QVariant( MM ) ).toUInt();
888  repeatDistanceMapUnitScale.minScale = layer->customProperty( "labeling/repeatDistanceMapUnitMinScale", 0.0 ).toDouble();
889  repeatDistanceMapUnitScale.maxScale = layer->customProperty( "labeling/repeatDistanceMapUnitMaxScale", 0.0 ).toDouble();
890 
891  // rendering
892  int scalemn = layer->customProperty( "labeling/scaleMin", QVariant( 0 ) ).toInt();
893  int scalemx = layer->customProperty( "labeling/scaleMax", QVariant( 0 ) ).toInt();
894 
895  // fix for scale visibility limits being keyed off of just its values in the past (<2.0)
896  QVariant scalevis = layer->customProperty( "labeling/scaleVisibility", QVariant() );
897  if ( scalevis.isValid() )
898  {
899  scaleVisibility = scalevis.toBool();
900  scaleMin = scalemn;
901  scaleMax = scalemx;
902  }
903  else if ( scalemn > 0 || scalemx > 0 )
904  {
905  scaleVisibility = true;
906  scaleMin = scalemn;
907  scaleMax = scalemx;
908  }
909  else
910  {
911  // keep scaleMin and scaleMax at new 1.0 defaults (1 and 10000000, were 0 and 0)
912  scaleVisibility = false;
913  }
914 
915 
916  fontLimitPixelSize = layer->customProperty( "labeling/fontLimitPixelSize", QVariant( false ) ).toBool();
917  fontMinPixelSize = layer->customProperty( "labeling/fontMinPixelSize", QVariant( 0 ) ).toInt();
918  fontMaxPixelSize = layer->customProperty( "labeling/fontMaxPixelSize", QVariant( 10000 ) ).toInt();
919  displayAll = layer->customProperty( "labeling/displayAll", QVariant( false ) ).toBool();
920  upsidedownLabels = ( UpsideDownLabels )layer->customProperty( "labeling/upsidedownLabels", QVariant( Upright ) ).toUInt();
921 
922  labelPerPart = layer->customProperty( "labeling/labelPerPart" ).toBool();
923  mergeLines = layer->customProperty( "labeling/mergeLines" ).toBool();
924  minFeatureSize = layer->customProperty( "labeling/minFeatureSize" ).toDouble();
925  limitNumLabels = layer->customProperty( "labeling/limitNumLabels", QVariant( false ) ).toBool();
926  maxNumLabels = layer->customProperty( "labeling/maxNumLabels", QVariant( 2000 ) ).toInt();
927  obstacle = layer->customProperty( "labeling/obstacle", QVariant( true ) ).toBool();
928  obstacleFactor = layer->customProperty( "labeling/obstacleFactor", QVariant( 1.0 ) ).toDouble();
929  obstacleType = ( ObstacleType )layer->customProperty( "labeling/obstacleType", QVariant( PolygonInterior ) ).toUInt();
930 
931  readDataDefinedPropertyMap( layer, dataDefinedProperties );
932 }
933 
935 {
936  // this is a mark that labeling information is present
937  layer->setCustomProperty( "labeling", "pal" );
938 
939  layer->setCustomProperty( "labeling/enabled", enabled );
940  layer->setCustomProperty( "labeling/drawLabels", drawLabels );
941 
942  // text style
943  layer->setCustomProperty( "labeling/fieldName", fieldName );
944  layer->setCustomProperty( "labeling/isExpression", isExpression );
945  layer->setCustomProperty( "labeling/fontFamily", textFont.family() );
946  layer->setCustomProperty( "labeling/namedStyle", QgsFontUtils::untranslateNamedStyle( textNamedStyle ) );
947  layer->setCustomProperty( "labeling/fontSize", textFont.pointSizeF() );
948  layer->setCustomProperty( "labeling/fontSizeInMapUnits", fontSizeInMapUnits );
949  layer->setCustomProperty( "labeling/fontSizeMapUnitMinScale", fontSizeMapUnitScale.minScale );
950  layer->setCustomProperty( "labeling/fontSizeMapUnitMaxScale", fontSizeMapUnitScale.maxScale );
951  layer->setCustomProperty( "labeling/fontWeight", textFont.weight() );
952  layer->setCustomProperty( "labeling/fontItalic", textFont.italic() );
953  layer->setCustomProperty( "labeling/fontBold", textFont.bold() );
954  layer->setCustomProperty( "labeling/fontStrikeout", textFont.strikeOut() );
955  layer->setCustomProperty( "labeling/fontUnderline", textFont.underline() );
956  _writeColor( layer, "labeling/textColor", textColor );
957  layer->setCustomProperty( "labeling/fontCapitals", ( unsigned int )textFont.capitalization() );
958  layer->setCustomProperty( "labeling/fontLetterSpacing", textFont.letterSpacing() );
959  layer->setCustomProperty( "labeling/fontWordSpacing", textFont.wordSpacing() );
960  layer->setCustomProperty( "labeling/textTransp", textTransp );
961  layer->setCustomProperty( "labeling/blendMode", QgsMapRenderer::getBlendModeEnum( blendMode ) );
962  layer->setCustomProperty( "labeling/previewBkgrdColor", previewBkgrdColor.name() );
963 
964  // text formatting
965  layer->setCustomProperty( "labeling/wrapChar", wrapChar );
966  layer->setCustomProperty( "labeling/multilineHeight", multilineHeight );
967  layer->setCustomProperty( "labeling/multilineAlign", ( unsigned int )multilineAlign );
968  layer->setCustomProperty( "labeling/addDirectionSymbol", addDirectionSymbol );
969  layer->setCustomProperty( "labeling/leftDirectionSymbol", leftDirectionSymbol );
970  layer->setCustomProperty( "labeling/rightDirectionSymbol", rightDirectionSymbol );
971  layer->setCustomProperty( "labeling/reverseDirectionSymbol", reverseDirectionSymbol );
972  layer->setCustomProperty( "labeling/placeDirectionSymbol", ( unsigned int )placeDirectionSymbol );
973  layer->setCustomProperty( "labeling/formatNumbers", formatNumbers );
974  layer->setCustomProperty( "labeling/decimals", decimals );
975  layer->setCustomProperty( "labeling/plussign", plusSign );
976 
977  // text buffer
978  layer->setCustomProperty( "labeling/bufferDraw", bufferDraw );
979  layer->setCustomProperty( "labeling/bufferSize", bufferSize );
980  layer->setCustomProperty( "labeling/bufferSizeInMapUnits", bufferSizeInMapUnits );
981  layer->setCustomProperty( "labeling/bufferSizeMapUnitMinScale", bufferSizeMapUnitScale.minScale );
982  layer->setCustomProperty( "labeling/bufferSizeMapUnitMaxScale", bufferSizeMapUnitScale.maxScale );
983  _writeColor( layer, "labeling/bufferColor", bufferColor );
984  layer->setCustomProperty( "labeling/bufferNoFill", bufferNoFill );
985  layer->setCustomProperty( "labeling/bufferTransp", bufferTransp );
986  layer->setCustomProperty( "labeling/bufferJoinStyle", ( unsigned int )bufferJoinStyle );
987  layer->setCustomProperty( "labeling/bufferBlendMode", QgsMapRenderer::getBlendModeEnum( bufferBlendMode ) );
988 
989  // background
990  layer->setCustomProperty( "labeling/shapeDraw", shapeDraw );
991  layer->setCustomProperty( "labeling/shapeType", ( unsigned int )shapeType );
992  layer->setCustomProperty( "labeling/shapeSVGFile", shapeSVGFile );
993  layer->setCustomProperty( "labeling/shapeSizeType", ( unsigned int )shapeSizeType );
994  layer->setCustomProperty( "labeling/shapeSizeX", shapeSize.x() );
995  layer->setCustomProperty( "labeling/shapeSizeY", shapeSize.y() );
996  layer->setCustomProperty( "labeling/shapeSizeUnits", ( unsigned int )shapeSizeUnits );
997  layer->setCustomProperty( "labeling/shapeSizeMapUnitMinScale", shapeSizeMapUnitScale.minScale );
998  layer->setCustomProperty( "labeling/shapeSizeMapUnitMaxScale", shapeSizeMapUnitScale.maxScale );
999  layer->setCustomProperty( "labeling/shapeRotationType", ( unsigned int )shapeRotationType );
1000  layer->setCustomProperty( "labeling/shapeRotation", shapeRotation );
1001  layer->setCustomProperty( "labeling/shapeOffsetX", shapeOffset.x() );
1002  layer->setCustomProperty( "labeling/shapeOffsetY", shapeOffset.y() );
1003  layer->setCustomProperty( "labeling/shapeOffsetUnits", ( unsigned int )shapeOffsetUnits );
1004  layer->setCustomProperty( "labeling/shapeOffsetMapUnitMinScale", shapeOffsetMapUnitScale.minScale );
1005  layer->setCustomProperty( "labeling/shapeOffsetMapUnitMaxScale", shapeOffsetMapUnitScale.maxScale );
1006  layer->setCustomProperty( "labeling/shapeRadiiX", shapeRadii.x() );
1007  layer->setCustomProperty( "labeling/shapeRadiiY", shapeRadii.y() );
1008  layer->setCustomProperty( "labeling/shapeRadiiUnits", ( unsigned int )shapeRadiiUnits );
1009  layer->setCustomProperty( "labeling/shapeRadiiMapUnitMinScale", shapeRadiiMapUnitScale.minScale );
1010  layer->setCustomProperty( "labeling/shapeRadiiMapUnitMaxScale", shapeRadiiMapUnitScale.maxScale );
1011  _writeColor( layer, "labeling/shapeFillColor", shapeFillColor, true );
1012  _writeColor( layer, "labeling/shapeBorderColor", shapeBorderColor, true );
1013  layer->setCustomProperty( "labeling/shapeBorderWidth", shapeBorderWidth );
1014  layer->setCustomProperty( "labeling/shapeBorderWidthUnits", ( unsigned int )shapeBorderWidthUnits );
1015  layer->setCustomProperty( "labeling/shapeBorderWidthMapUnitMinScale", shapeBorderWidthMapUnitScale.minScale );
1016  layer->setCustomProperty( "labeling/shapeBorderWidthMapUnitMaxScale", shapeBorderWidthMapUnitScale.maxScale );
1017  layer->setCustomProperty( "labeling/shapeJoinStyle", ( unsigned int )shapeJoinStyle );
1018  layer->setCustomProperty( "labeling/shapeTransparency", shapeTransparency );
1019  layer->setCustomProperty( "labeling/shapeBlendMode", QgsMapRenderer::getBlendModeEnum( shapeBlendMode ) );
1020 
1021  // drop shadow
1022  layer->setCustomProperty( "labeling/shadowDraw", shadowDraw );
1023  layer->setCustomProperty( "labeling/shadowUnder", ( unsigned int )shadowUnder );
1024  layer->setCustomProperty( "labeling/shadowOffsetAngle", shadowOffsetAngle );
1025  layer->setCustomProperty( "labeling/shadowOffsetDist", shadowOffsetDist );
1026  layer->setCustomProperty( "labeling/shadowOffsetUnits", ( unsigned int )shadowOffsetUnits );
1027  layer->setCustomProperty( "labeling/shadowOffsetMapUnitMinScale", shadowOffsetMapUnitScale.minScale );
1028  layer->setCustomProperty( "labeling/shadowOffsetMapUnitMaxScale", shadowOffsetMapUnitScale.maxScale );
1029  layer->setCustomProperty( "labeling/shadowOffsetGlobal", shadowOffsetGlobal );
1030  layer->setCustomProperty( "labeling/shadowRadius", shadowRadius );
1031  layer->setCustomProperty( "labeling/shadowRadiusUnits", ( unsigned int )shadowRadiusUnits );
1032  layer->setCustomProperty( "labeling/shadowRadiusMapUnitMinScale", shadowRadiusMapUnitScale.minScale );
1033  layer->setCustomProperty( "labeling/shadowRadiusMapUnitMaxScale", shadowRadiusMapUnitScale.maxScale );
1034  layer->setCustomProperty( "labeling/shadowRadiusAlphaOnly", shadowRadiusAlphaOnly );
1035  layer->setCustomProperty( "labeling/shadowTransparency", shadowTransparency );
1036  layer->setCustomProperty( "labeling/shadowScale", shadowScale );
1037  _writeColor( layer, "labeling/shadowColor", shadowColor, false );
1038  layer->setCustomProperty( "labeling/shadowBlendMode", QgsMapRenderer::getBlendModeEnum( shadowBlendMode ) );
1039 
1040  // placement
1041  layer->setCustomProperty( "labeling/placement", placement );
1042  layer->setCustomProperty( "labeling/placementFlags", ( unsigned int )placementFlags );
1043  layer->setCustomProperty( "labeling/centroidWhole", centroidWhole );
1044  layer->setCustomProperty( "labeling/centroidInside", centroidInside );
1045  layer->setCustomProperty( "labeling/fitInPolygonOnly", fitInPolygonOnly );
1046  layer->setCustomProperty( "labeling/dist", dist );
1047  layer->setCustomProperty( "labeling/distInMapUnits", distInMapUnits );
1048  layer->setCustomProperty( "labeling/distMapUnitMinScale", distMapUnitScale.minScale );
1049  layer->setCustomProperty( "labeling/distMapUnitMaxScale", distMapUnitScale.maxScale );
1050  layer->setCustomProperty( "labeling/quadOffset", ( unsigned int )quadOffset );
1051  layer->setCustomProperty( "labeling/xOffset", xOffset );
1052  layer->setCustomProperty( "labeling/yOffset", yOffset );
1053  layer->setCustomProperty( "labeling/labelOffsetInMapUnits", labelOffsetInMapUnits );
1054  layer->setCustomProperty( "labeling/labelOffsetMapUnitMinScale", labelOffsetMapUnitScale.minScale );
1055  layer->setCustomProperty( "labeling/labelOffsetMapUnitMaxScale", labelOffsetMapUnitScale.maxScale );
1056  layer->setCustomProperty( "labeling/angleOffset", angleOffset );
1057  layer->setCustomProperty( "labeling/preserveRotation", preserveRotation );
1058  layer->setCustomProperty( "labeling/maxCurvedCharAngleIn", maxCurvedCharAngleIn );
1059  layer->setCustomProperty( "labeling/maxCurvedCharAngleOut", maxCurvedCharAngleOut );
1060  layer->setCustomProperty( "labeling/priority", priority );
1061  layer->setCustomProperty( "labeling/repeatDistance", repeatDistance );
1062  layer->setCustomProperty( "labeling/repeatDistanceUnit", repeatDistanceUnit );
1063  layer->setCustomProperty( "labeling/repeatDistanceMapUnitMinScale", repeatDistanceMapUnitScale.minScale );
1064  layer->setCustomProperty( "labeling/repeatDistanceMapUnitMaxScale", repeatDistanceMapUnitScale.maxScale );
1065 
1066  // rendering
1067  layer->setCustomProperty( "labeling/scaleVisibility", scaleVisibility );
1068  layer->setCustomProperty( "labeling/scaleMin", scaleMin );
1069  layer->setCustomProperty( "labeling/scaleMax", scaleMax );
1070  layer->setCustomProperty( "labeling/fontLimitPixelSize", fontLimitPixelSize );
1071  layer->setCustomProperty( "labeling/fontMinPixelSize", fontMinPixelSize );
1072  layer->setCustomProperty( "labeling/fontMaxPixelSize", fontMaxPixelSize );
1073  layer->setCustomProperty( "labeling/displayAll", displayAll );
1074  layer->setCustomProperty( "labeling/upsidedownLabels", ( unsigned int )upsidedownLabels );
1075 
1076  layer->setCustomProperty( "labeling/labelPerPart", labelPerPart );
1077  layer->setCustomProperty( "labeling/mergeLines", mergeLines );
1078  layer->setCustomProperty( "labeling/minFeatureSize", minFeatureSize );
1079  layer->setCustomProperty( "labeling/limitNumLabels", limitNumLabels );
1080  layer->setCustomProperty( "labeling/maxNumLabels", maxNumLabels );
1081  layer->setCustomProperty( "labeling/obstacle", obstacle );
1082  layer->setCustomProperty( "labeling/obstacleFactor", obstacleFactor );
1083  layer->setCustomProperty( "labeling/obstacleType", ( unsigned int )obstacleType );
1084 
1085  writeDataDefinedPropertyMap( layer, dataDefinedProperties );
1086 }
1087 
1089  bool active, bool useExpr, const QString& expr, const QString& field )
1090 {
1091  bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
1092 
1093  if ( dataDefinedProperties.contains( p ) )
1094  {
1096  if ( it != dataDefinedProperties.constEnd() )
1097  {
1098  QgsDataDefined* dd = it.value();
1099  dd->setActive( active );
1100  dd->setExpressionString( expr );
1101  dd->setField( field );
1102  dd->setUseExpression( useExpr );
1103  }
1104  }
1105  else if ( !defaultVals )
1106  {
1107  QgsDataDefined* dd = new QgsDataDefined( active, useExpr, expr, field );
1108  dataDefinedProperties.insert( p, dd );
1109  }
1110 }
1111 
1113 {
1115  if ( it != dataDefinedProperties.end() )
1116  {
1117  delete( it.value() );
1119  }
1120 }
1121 
1123 {
1124  // TODO: update or remove this when project settings for labeling are migrated to better XML layout
1125  QString newValue = value;
1126  if ( !value.isEmpty() && !value.contains( "~~" ) )
1127  {
1128  QStringList values;
1129  values << "1"; // all old-style values are active if not empty
1130  values << "0";
1131  values << "";
1132  values << value; // all old-style values are only field names
1133  newValue = values.join( "~~" );
1134  }
1135 
1136  return newValue;
1137 }
1138 
1140 {
1142  if ( it != dataDefinedProperties.constEnd() )
1143  {
1144  return it.value();
1145  }
1146  return 0;
1147 }
1148 
1150 {
1153  if ( it != dataDefinedProperties.constEnd() )
1154  {
1155  return it.value()->toMap();
1156  }
1157  return map;
1158 }
1159 
1161 {
1162  if ( !dataDefinedProperties.contains( p ) )
1163  {
1164  return QVariant();
1165  }
1166 
1167  //try to keep < 2.12 API - handle no passed expression context
1169  if ( !context )
1170  {
1171  scopedEc.reset( new QgsExpressionContext() );
1172  scopedEc->setFeature( f );
1173  scopedEc->setFields( fields );
1174  }
1175  const QgsExpressionContext* ec = context ? context : scopedEc.data();
1176 
1177  QgsDataDefined* dd = 0;
1179  if ( it != dataDefinedProperties.constEnd() )
1180  {
1181  dd = it.value();
1182  }
1183 
1184  if ( !dd )
1185  {
1186  return QVariant();
1187  }
1188 
1189  if ( !dd->isActive() )
1190  {
1191  return QVariant();
1192  }
1193 
1194  QVariant result = QVariant();
1195  bool useExpression = dd->useExpression();
1196  QString field = dd->field();
1197 
1198  //QgsDebugMsgLevel( QString( "isActive:" ) + isActive ? "1" : "0", 4 );
1199  //QgsDebugMsgLevel( QString( "useExpression:" ) + useExpression ? "1" : "0", 4 );
1200  //QgsDebugMsgLevel( QString( "expression:" ) + dd->expressionString(), 4 );
1201  //QgsDebugMsgLevel( QString( "field:" ) + field, 4 );
1202 
1203  if ( useExpression && dd->expressionIsPrepared() )
1204  {
1205  QgsExpression* expr = dd->expression();
1206  //QgsDebugMsgLevel( QString( "expr columns:" ) + expr->referencedColumns().join( "," ), 4 );
1207 
1208  result = expr->evaluate( ec );
1209  if ( expr->hasEvalError() )
1210  {
1211  QgsDebugMsgLevel( QString( "Evaluate error:" ) + expr->evalErrorString(), 4 );
1212  return QVariant();
1213  }
1214  }
1215  else if ( !useExpression && !field.isEmpty() )
1216  {
1217  // use direct attribute access instead of evaluating "field" expression (much faster)
1218  int indx = fields.indexFromName( field );
1219  if ( indx != -1 )
1220  {
1221  result = f.attribute( indx );
1222  }
1223  }
1224  return result;
1225 }
1226 
1228 {
1229  // null passed-around QVariant
1230  exprVal.clear();
1231 
1232  //try to keep < 2.12 API - handle no passed expression context
1234  if ( !context )
1235  {
1236  scopedEc.reset( new QgsExpressionContext() );
1237  if ( mCurFeat )
1238  scopedEc->setFeature( *mCurFeat );
1239  scopedEc->setFields( mCurFields );
1240  }
1241  const QgsExpressionContext* ec = context ? context : scopedEc.data();
1242 
1243  QVariant result = dataDefinedValue( p, *mCurFeat, mCurFields, ec );
1244 
1245  if ( result.isValid() && !result.isNull() )
1246  {
1247  //QgsDebugMsgLevel( QString( "result type:" ) + QString( result.typeName() ), 4 );
1248  //QgsDebugMsgLevel( QString( "result string:" ) + result.toString(), 4 );
1249  exprVal = result;
1250  return true;
1251  }
1252 
1253  return false;
1254 }
1255 
1257 {
1258  bool isActive = false;
1260  if ( it != dataDefinedProperties.constEnd() )
1261  {
1262  isActive = it.value()->isActive();
1263  }
1264 
1265  return isActive;
1266 }
1267 
1269 {
1270  bool useExpression = false;
1272  if ( it != dataDefinedProperties.constEnd() )
1273  {
1274  useExpression = it.value()->useExpression();
1275  }
1276 
1277  return useExpression;
1278 }
1279 
1280 bool QgsPalLayerSettings::checkMinimumSizeMM( const QgsRenderContext& ct, const QgsGeometry* geom, double minSize ) const
1281 {
1282  return QgsPalLabeling::checkMinimumSizeMM( ct, geom, minSize );
1283 }
1284 
1285 void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString text, double& labelX, double& labelY, QgsFeature* f, const QgsRenderContext *context )
1286 {
1287  if ( !fm || !f )
1288  {
1289  return;
1290  }
1291 
1292  //try to keep < 2.12 API - handle no passed render context
1294  if ( !context )
1295  {
1296  scopedRc.reset( new QgsRenderContext() );
1297  if ( f )
1298  scopedRc->expressionContext().setFeature( *f );
1299  }
1300  const QgsRenderContext* rc = context ? context : scopedRc.data();
1301 
1302  QString wrapchr = wrapChar;
1303  double multilineH = multilineHeight;
1304 
1305  bool addDirSymb = addDirectionSymbol;
1306  QString leftDirSymb = leftDirectionSymbol;
1307  QString rightDirSymb = rightDirectionSymbol;
1309 
1310  if ( f == mCurFeat ) // called internally, use any stored data defined values
1311  {
1312  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
1313  {
1314  wrapchr = dataDefinedValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
1315  }
1316 
1317  if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
1318  {
1319  multilineH = dataDefinedValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
1320  }
1321 
1322  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
1323  {
1324  addDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
1325  }
1326 
1327  if ( addDirSymb )
1328  {
1329 
1330  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
1331  {
1332  leftDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
1333  }
1334  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbRight ) )
1335  {
1336  rightDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
1337  }
1338 
1339  if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
1340  {
1341  placeDirSymb = ( QgsPalLayerSettings::DirectionSymbols )dataDefinedValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt();
1342  }
1343 
1344  }
1345 
1346  }
1347  else // called externally with passed-in feature, evaluate data defined
1348  {
1350  if ( exprVal.isValid() )
1351  {
1352  wrapchr = exprVal.toString();
1353  }
1354  exprVal.clear();
1356  if ( exprVal.isValid() )
1357  {
1358  bool ok;
1359  double size = exprVal.toDouble( &ok );
1360  if ( ok )
1361  {
1362  multilineH = size;
1363  }
1364  }
1365 
1366  exprVal.clear();
1368  if ( exprVal.isValid() )
1369  {
1370  addDirSymb = exprVal.toBool();
1371  }
1372 
1373  if ( addDirSymb ) // don't do extra evaluations if not adding a direction symbol
1374  {
1375  exprVal.clear();
1377  if ( exprVal.isValid() )
1378  {
1379  leftDirSymb = exprVal.toString();
1380  }
1381  exprVal.clear();
1383  if ( exprVal.isValid() )
1384  {
1385  rightDirSymb = exprVal.toString();
1386  }
1387  exprVal.clear();
1389  if ( exprVal.isValid() )
1390  {
1391  bool ok;
1392  int enmint = exprVal.toInt( &ok );
1393  if ( ok )
1394  {
1395  placeDirSymb = ( QgsPalLayerSettings::DirectionSymbols )enmint;
1396  }
1397  }
1398  }
1399 
1400  }
1401 
1402  if ( wrapchr.isEmpty() )
1403  {
1404  wrapchr = QString( "\n" ); // default to new line delimiter
1405  }
1406 
1407  //consider the space needed for the direction symbol
1408  if ( addDirSymb && placement == QgsPalLayerSettings::Line
1409  && ( !leftDirSymb.isEmpty() || !rightDirSymb.isEmpty() ) )
1410  {
1411  QString dirSym = leftDirSymb;
1412 
1413  if ( fm->width( rightDirSymb ) > fm->width( dirSym ) )
1414  dirSym = rightDirSymb;
1415 
1416  if ( placeDirSymb == QgsPalLayerSettings::SymbolLeftRight )
1417  {
1418  text.append( dirSym );
1419  }
1420  else
1421  {
1422  text.prepend( dirSym + QString( "\n" ) ); // SymbolAbove or SymbolBelow
1423  }
1424  }
1425 
1426  double w = 0.0, h = 0.0;
1427  QStringList multiLineSplit = QgsPalLabeling::splitToLines( text, wrapchr );
1428  int lines = multiLineSplit.size();
1429 
1430  double labelHeight = fm->ascent() + fm->descent(); // ignore +1 for baseline
1431 
1432  h += fm->height() + ( double )(( lines - 1 ) * labelHeight * multilineH );
1433  h /= rasterCompressFactor;
1434 
1435  for ( int i = 0; i < lines; ++i )
1436  {
1437  double width = fm->width( multiLineSplit.at( i ) );
1438  if ( width > w )
1439  {
1440  w = width;
1441  }
1442  }
1443  w /= rasterCompressFactor;
1444 
1445 #if 0 // XXX strk
1446  QgsPoint ptSize = xform->toMapCoordinatesF( w, h );
1447  labelX = qAbs( ptSize.x() - ptZero.x() );
1448  labelY = qAbs( ptSize.y() - ptZero.y() );
1449 #else
1450  double uPP = xform->mapUnitsPerPixel();
1451  labelX = w * uPP;
1452  labelY = h * uPP;
1453 #endif
1454 }
1455 
1457 {
1458  if ( !drawLabels )
1459  {
1460  if ( obstacle )
1461  {
1462  registerObstacleFeature( f, context, dxfLayer );
1463  }
1464  return;
1465  }
1466 
1467  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
1468  mCurFeat = &f;
1469 // mCurFields = &layer->pendingFields();
1470 
1471  // store data defined-derived values for later adding to QgsPalGeometry for use during rendering
1472  dataDefinedValues.clear();
1473 
1474  // data defined show label? defaults to show label if not 0
1476  {
1477  bool showLabel = dataDefinedEvaluate( QgsPalLayerSettings::Show, exprVal, &context.expressionContext() );
1478  showLabel = exprVal.toBool();
1479  QgsDebugMsgLevel( QString( "exprVal Show:%1" ).arg( showLabel ? "true" : "false" ), 4 );
1480  if ( !showLabel )
1481  {
1482  return;
1483  }
1484  }
1485 
1486  // data defined scale visibility?
1487  bool useScaleVisibility = scaleVisibility;
1489  {
1490  QgsDebugMsgLevel( QString( "exprVal ScaleVisibility:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
1491  useScaleVisibility = exprVal.toBool();
1492  }
1493 
1494  if ( useScaleVisibility )
1495  {
1496  // data defined min scale?
1497  double minScale = scaleMin;
1499  {
1500  QgsDebugMsgLevel( QString( "exprVal MinScale:%1" ).arg( exprVal.toDouble() ), 4 );
1501  bool conversionOk;
1502  double mins = exprVal.toDouble( &conversionOk );
1503  if ( conversionOk )
1504  {
1505  minScale = mins;
1506  }
1507  }
1508 
1509  // scales closer than 1:1
1510  if ( minScale < 0 )
1511  {
1512  minScale = 1 / qAbs( minScale );
1513  }
1514 
1515  if ( minScale != 0 && context.rendererScale() < minScale )
1516  {
1517  return;
1518  }
1519 
1520  // data defined max scale?
1521  double maxScale = scaleMax;
1523  {
1524  QgsDebugMsgLevel( QString( "exprVal MaxScale:%1" ).arg( exprVal.toDouble() ), 4 );
1525  bool conversionOk;
1526  double maxs = exprVal.toDouble( &conversionOk );
1527  if ( conversionOk )
1528  {
1529  maxScale = maxs;
1530  }
1531  }
1532 
1533  // scales closer than 1:1
1534  if ( maxScale < 0 )
1535  {
1536  maxScale = 1 / qAbs( maxScale );
1537  }
1538 
1539  if ( maxScale != 0 && context.rendererScale() > maxScale )
1540  {
1541  return;
1542  }
1543  }
1544 
1545  QFont labelFont = textFont;
1546  // labelFont will be added to label's QgsPalGeometry for use during label painting
1547 
1548  // data defined font units?
1551  {
1552  QString units = exprVal.toString().trimmed();
1553  QgsDebugMsgLevel( QString( "exprVal Font units:%1" ).arg( units ), 4 );
1554  if ( !units.isEmpty() )
1555  {
1556  fontunits = _decodeUnits( units );
1557  }
1558  }
1559 
1560  //data defined label size?
1561  double fontSize = labelFont.pointSizeF(); // font size doesn't have its own class data member
1562  if ( dataDefinedEvaluate( QgsPalLayerSettings::Size, exprVal, &context.expressionContext() ) )
1563  {
1564  QgsDebugMsgLevel( QString( "exprVal Size:%1" ).arg( exprVal.toDouble() ), 4 );
1565  bool ok;
1566  double size = exprVal.toDouble( &ok );
1567  if ( ok )
1568  {
1569  fontSize = size;
1570  }
1571  }
1572  if ( fontSize <= 0.0 )
1573  {
1574  return;
1575  }
1576 
1577  int fontPixelSize = sizeToPixel( fontSize, context, fontunits, true, fontSizeMapUnitScale );
1578  // don't try to show font sizes less than 1 pixel (Qt complains)
1579  if ( fontPixelSize < 1 )
1580  {
1581  return;
1582  }
1583  labelFont.setPixelSize( fontPixelSize );
1584 
1585  // NOTE: labelFont now always has pixelSize set, so pointSize or pointSizeF might return -1
1586 
1587  // defined 'minimum/maximum pixel font size'?
1588  if ( fontunits == QgsPalLayerSettings::MapUnits )
1589  {
1590  bool useFontLimitPixelSize = fontLimitPixelSize;
1592  {
1593  QgsDebugMsgLevel( QString( "exprVal FontLimitPixel:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
1594  useFontLimitPixelSize = exprVal.toBool();
1595  }
1596 
1597  if ( useFontLimitPixelSize )
1598  {
1599  int fontMinPixel = fontMinPixelSize;
1601  {
1602  bool ok;
1603  int sizeInt = exprVal.toInt( &ok );
1604  QgsDebugMsgLevel( QString( "exprVal FontMinPixel:%1" ).arg( sizeInt ), 4 );
1605  if ( ok )
1606  {
1607  fontMinPixel = sizeInt;
1608  }
1609  }
1610 
1611  int fontMaxPixel = fontMaxPixelSize;
1613  {
1614  bool ok;
1615  int sizeInt = exprVal.toInt( &ok );
1616  QgsDebugMsgLevel( QString( "exprVal FontMaxPixel:%1" ).arg( sizeInt ), 4 );
1617  if ( ok )
1618  {
1619  fontMaxPixel = sizeInt;
1620  }
1621  }
1622 
1623  if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
1624  {
1625  return;
1626  }
1627  }
1628  }
1629 
1630  // NOTE: the following parsing functions calculate and store any data defined values for later use in QgsPalLabeling::drawLabeling
1631  // this is done to provide clarity, and because such parsing is not directly related to PAL feature registration calculations
1632 
1633  // calculate rest of font attributes and store any data defined values
1634  // this is done here for later use in making label backgrounds part of collision management (when implemented)
1635  parseTextStyle( labelFont, fontunits, context );
1636  parseTextFormatting( context );
1637  parseTextBuffer( context );
1638  parseShapeBackground( context );
1639  parseDropShadow( context );
1640 
1641  QString labelText;
1642 
1643  // Check to see if we are a expression string.
1644  if ( isExpression )
1645  {
1647  if ( exp->hasParserError() )
1648  {
1649  QgsDebugMsgLevel( QString( "Expression parser error:%1" ).arg( exp->parserErrorString() ), 4 );
1650  return;
1651  }
1652  exp->setScale( context.rendererScale() );
1653 // QVariant result = exp->evaluate( &f, layer->pendingFields() );
1654  QVariant result = exp->evaluate( &context.expressionContext() ); // expression prepared in QgsPalLabeling::prepareLayer()
1655  if ( exp->hasEvalError() )
1656  {
1657  QgsDebugMsgLevel( QString( "Expression parser eval error:%1" ).arg( exp->evalErrorString() ), 4 );
1658  return;
1659  }
1660  labelText = result.isNull() ? "" : result.toString();
1661  }
1662  else
1663  {
1664  const QVariant &v = f.attribute( fieldIndex );
1665  labelText = v.isNull() ? "" : v.toString();
1666  }
1667 
1668  // data defined format numbers?
1669  bool formatnum = formatNumbers;
1671  {
1672  formatnum = exprVal.toBool();
1673  QgsDebugMsgLevel( QString( "exprVal NumFormat:%1" ).arg( formatnum ? "true" : "false" ), 4 );
1674  }
1675 
1676  // format number if label text is coercible to a number
1677  if ( formatnum )
1678  {
1679  // data defined decimal places?
1680  int decimalPlaces = decimals;
1682  {
1683  bool ok;
1684  int dInt = exprVal.toInt( &ok );
1685  QgsDebugMsgLevel( QString( "exprVal NumDecimals:%1" ).arg( dInt ), 4 );
1686  if ( ok && dInt > 0 ) // needs to be positive
1687  {
1688  decimalPlaces = dInt;
1689  }
1690  }
1691 
1692  // data defined plus sign?
1693  bool signPlus = plusSign;
1695  {
1696  signPlus = exprVal.toBool();
1697  QgsDebugMsgLevel( QString( "exprVal NumPlusSign:%1" ).arg( signPlus ? "true" : "false" ), 4 );
1698  }
1699 
1700  QVariant textV( labelText );
1701  bool ok;
1702  double d = textV.toDouble( &ok );
1703  if ( ok )
1704  {
1705  QString numberFormat;
1706  if ( d > 0 && signPlus )
1707  {
1708  numberFormat.append( "+" );
1709  }
1710  numberFormat.append( "%1" );
1711  labelText = numberFormat.arg( d, 0, 'f', decimalPlaces );
1712  }
1713  }
1714 
1715 
1716  // NOTE: this should come AFTER any option that affects font metrics
1717  QFontMetricsF* labelFontMetrics = new QFontMetricsF( labelFont );
1718  double labelX, labelY; // will receive label size
1719  calculateLabelSize( labelFontMetrics, labelText, labelX, labelY, mCurFeat, &context );
1720 
1721 
1722  // maximum angle between curved label characters (hardcoded defaults used in QGIS <2.0)
1723  //
1724  double maxcharanglein = 20.0; // range 20.0-60.0
1725  double maxcharangleout = -20.0; // range 20.0-95.0
1726 
1728  {
1729  maxcharanglein = maxCurvedCharAngleIn;
1730  maxcharangleout = maxCurvedCharAngleOut;
1731 
1732  //data defined maximum angle between curved label characters?
1734  {
1735  QString ptstr = exprVal.toString().trimmed();
1736  QgsDebugMsgLevel( QString( "exprVal CurvedCharAngleInOut:%1" ).arg( ptstr ), 4 );
1737 
1738  if ( !ptstr.isEmpty() )
1739  {
1740  QPointF maxcharanglePt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
1741  maxcharanglein = qBound( 20.0, ( double )maxcharanglePt.x(), 60.0 );
1742  maxcharangleout = qBound( 20.0, ( double )maxcharanglePt.y(), 95.0 );
1743  }
1744  }
1745  // make sure maxcharangleout is always negative
1746  maxcharangleout = -( qAbs( maxcharangleout ) );
1747  }
1748 
1749  // data defined centroid whole or clipped?
1750  bool wholeCentroid = centroidWhole;
1752  {
1753  QString str = exprVal.toString().trimmed();
1754  QgsDebugMsgLevel( QString( "exprVal CentroidWhole:%1" ).arg( str ), 4 );
1755 
1756  if ( !str.isEmpty() )
1757  {
1758  if ( str.compare( "Visible", Qt::CaseInsensitive ) == 0 )
1759  {
1760  wholeCentroid = false;
1761  }
1762  else if ( str.compare( "Whole", Qt::CaseInsensitive ) == 0 )
1763  {
1764  wholeCentroid = true;
1765  }
1766  }
1767  }
1768 
1769  const QgsGeometry* geom = f.constGeometry();
1770  if ( !geom )
1771  {
1772  return;
1773  }
1774 
1775  // whether we're going to create a centroid for polygon
1776  bool centroidPoly = (( placement == QgsPalLayerSettings::AroundPoint
1778  && geom->type() == QGis::Polygon );
1779 
1780  // CLIP the geometry if it is bigger than the extent
1781  // don't clip if centroid is requested for whole feature
1782  bool doClip = false;
1783  if ( !centroidPoly || ( centroidPoly && !wholeCentroid ) )
1784  {
1785  doClip = true;
1786  }
1787 
1788  const GEOSGeometry* geos_geom = 0;
1789  const QgsGeometry* preparedGeom = geom;
1790  QScopedPointer<QgsGeometry> scopedPreparedGeom;
1791 
1792  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, doClip ? extentGeom : 0 ) )
1793  {
1794  scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, doClip ? extentGeom : 0 ) );
1795  if ( !scopedPreparedGeom.data() )
1796  return;
1797  preparedGeom = scopedPreparedGeom.data();
1798  geos_geom = scopedPreparedGeom.data()->asGeos();
1799  }
1800  else
1801  {
1802  geos_geom = geom->asGeos();
1803  }
1804 
1805  if ( minFeatureSize > 0 && !checkMinimumSizeMM( context, preparedGeom, minFeatureSize ) )
1806  return;
1807 
1808  if ( geos_geom == NULL )
1809  return; // invalid geometry
1810 
1811  // likelihood exists label will be registered with PAL and may be drawn
1812  // check if max number of features to label (already registered with PAL) has been reached
1813  // Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
1814  if ( limitNumLabels )
1815  {
1816  if ( !maxNumLabels )
1817  {
1818  return;
1819  }
1821  if ( mFeatsRegPal >= maxNumLabels )
1822  {
1823  return;
1824  }
1825 
1826  int divNum = ( int )((( double )mFeaturesToLabel / maxNumLabels ) + 0.5 );
1827  if ( divNum && ( mFeatsRegPal == ( int )( mFeatsSendingToPal / divNum ) ) )
1828  {
1829  mFeatsSendingToPal += 1;
1830  if ( divNum && mFeatsSendingToPal % divNum )
1831  {
1832  return;
1833  }
1834  }
1835  }
1836 
1837  GEOSGeometry* geos_geom_clone;
1838  if ( GEOSGeomTypeId_r( QgsGeometry::getGEOSHandler(), geos_geom ) == GEOS_POLYGON && repeatDistance > 0 && placement == Line )
1839  {
1840  geos_geom_clone = GEOSBoundary_r( QgsGeometry::getGEOSHandler(), geos_geom );
1841  }
1842  else
1843  {
1844  geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
1845  }
1846 
1847  //data defined position / alignment / rotation?
1848  bool dataDefinedPosition = false;
1849  bool labelIsPinned = false;
1850  bool layerDefinedRotation = false;
1851  bool dataDefinedRotation = false;
1852  double xPos = 0.0, yPos = 0.0, angle = 0.0;
1853  bool ddXPos = false, ddYPos = false;
1854  double quadOffsetX = 0.0, quadOffsetY = 0.0;
1855  double offsetX = 0.0, offsetY = 0.0;
1856 
1857  //data defined quadrant offset?
1858  bool ddFixedQuad = false;
1859  QuadrantPosition quadOff = quadOffset;
1861  {
1862  bool ok;
1863  int quadInt = exprVal.toInt( &ok );
1864  QgsDebugMsgLevel( QString( "exprVal OffsetQuad:%1" ).arg( quadInt ), 4 );
1865  if ( ok && 0 <= quadInt && quadInt <= 8 )
1866  {
1867  quadOff = ( QuadrantPosition )quadInt;
1868  ddFixedQuad = true;
1869  }
1870  }
1871 
1872  // adjust quadrant offset of labels
1873  switch ( quadOff )
1874  {
1875  case QuadrantAboveLeft:
1876  quadOffsetX = -1.0;
1877  quadOffsetY = 1.0;
1878  break;
1879  case QuadrantAbove:
1880  quadOffsetX = 0.0;
1881  quadOffsetY = 1.0;
1882  break;
1883  case QuadrantAboveRight:
1884  quadOffsetX = 1.0;
1885  quadOffsetY = 1.0;
1886  break;
1887  case QuadrantLeft:
1888  quadOffsetX = -1.0;
1889  quadOffsetY = 0.0;
1890  break;
1891  case QuadrantRight:
1892  quadOffsetX = 1.0;
1893  quadOffsetY = 0.0;
1894  break;
1895  case QuadrantBelowLeft:
1896  quadOffsetX = -1.0;
1897  quadOffsetY = -1.0;
1898  break;
1899  case QuadrantBelow:
1900  quadOffsetX = 0.0;
1901  quadOffsetY = -1.0;
1902  break;
1903  case QuadrantBelowRight:
1904  quadOffsetX = 1.0;
1905  quadOffsetY = -1.0;
1906  break;
1907  case QuadrantOver:
1908  default:
1909  break;
1910  }
1911 
1912  //data defined label offset?
1913  double xOff = xOffset;
1914  double yOff = yOffset;
1916  {
1917  QString ptstr = exprVal.toString().trimmed();
1918  QgsDebugMsgLevel( QString( "exprVal OffsetXY:%1" ).arg( ptstr ), 4 );
1919 
1920  if ( !ptstr.isEmpty() )
1921  {
1922  QPointF ddOffPt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
1923  xOff = ddOffPt.x();
1924  yOff = ddOffPt.y();
1925  }
1926  }
1927 
1928  // data defined label offset units?
1929  bool offinmapunits = labelOffsetInMapUnits;
1931  {
1932  QString units = exprVal.toString().trimmed();
1933  QgsDebugMsgLevel( QString( "exprVal OffsetUnits:%1" ).arg( units ), 4 );
1934  if ( !units.isEmpty() )
1935  {
1936  offinmapunits = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
1937  }
1938  }
1939 
1940  // adjust offset of labels to match chosen unit and map scale
1941  // offsets match those of symbology: -x = left, -y = up
1942  double mapUntsPerMM = labelOffsetMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor();
1943  if ( xOff != 0 )
1944  {
1945  offsetX = xOff; // must be positive to match symbology offset direction
1946  if ( !offinmapunits )
1947  {
1948  offsetX *= mapUntsPerMM; //convert offset from mm to map units
1949  }
1950  }
1951  if ( yOff != 0 )
1952  {
1953  offsetY = -yOff; // must be negative to match symbology offset direction
1954  if ( !offinmapunits )
1955  {
1956  offsetY *= mapUntsPerMM; //convert offset from mm to map units
1957  }
1958  }
1959 
1960  // layer defined rotation?
1961  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
1962  if ( placement == QgsPalLayerSettings::OverPoint && angleOffset != 0 )
1963  {
1964  layerDefinedRotation = true;
1965  angle = angleOffset * M_PI / 180; // convert to radians
1966  }
1967 
1968  const QgsMapToPixel& m2p = context.mapToPixel();
1969  //data defined rotation?
1971  {
1972  bool ok;
1973  double rotD = exprVal.toDouble( &ok );
1974  QgsDebugMsgLevel( QString( "exprVal Rotation:%1" ).arg( rotD ), 4 );
1975  if ( ok )
1976  {
1977  dataDefinedRotation = true;
1978  // TODO: add setting to disable having data defined rotation follow
1979  // map rotation ?
1980  rotD -= m2p.mapRotation();
1981  angle = rotD * M_PI / 180.0;
1982  }
1983  }
1984 
1986  {
1987  if ( !exprVal.isNull() )
1988  xPos = exprVal.toDouble( &ddXPos );
1989  QgsDebugMsgLevel( QString( "exprVal PositionX:%1" ).arg( xPos ), 4 );
1990 
1992  {
1993  //data defined position. But field values could be NULL -> positions will be generated by PAL
1994  if ( !exprVal.isNull() )
1995  yPos = exprVal.toDouble( &ddYPos );
1996  QgsDebugMsgLevel( QString( "exprVal PositionY:%1" ).arg( yPos ), 4 );
1997 
1998  if ( ddXPos && ddYPos )
1999  {
2000  dataDefinedPosition = true;
2001  labelIsPinned = true;
2002  // layer rotation set, but don't rotate pinned labels unless data defined
2003  if ( layerDefinedRotation && !dataDefinedRotation )
2004  {
2005  angle = 0.0;
2006  }
2007 
2008  //x/y shift in case of alignment
2009  double xdiff = 0.0;
2010  double ydiff = 0.0;
2011 
2012  //horizontal alignment
2013  if ( dataDefinedEvaluate( QgsPalLayerSettings::Hali, exprVal, &context.expressionContext() ) )
2014  {
2015  QString haliString = exprVal.toString();
2016  QgsDebugMsgLevel( QString( "exprVal Hali:%1" ).arg( haliString ), 4 );
2017  if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
2018  {
2019  xdiff -= labelX / 2.0;
2020  }
2021  else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
2022  {
2023  xdiff -= labelX;
2024  }
2025  }
2026 
2027  //vertical alignment
2028  if ( dataDefinedEvaluate( QgsPalLayerSettings::Vali, exprVal, &context.expressionContext() ) )
2029  {
2030  QString valiString = exprVal.toString();
2031  QgsDebugMsgLevel( QString( "exprVal Vali:%1" ).arg( valiString ), 4 );
2032 
2033  if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
2034  {
2035  if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 )
2036  {
2037  ydiff -= labelY;
2038  }
2039  else
2040  {
2041  double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
2042  if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
2043  {
2044  ydiff -= labelY * descentRatio;
2045  }
2046  else //'Cap' or 'Half'
2047  {
2048  double capHeightRatio = ( labelFontMetrics->boundingRect( 'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
2049  ydiff -= labelY * capHeightRatio;
2050  if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
2051  {
2052  ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
2053  }
2054  }
2055  }
2056  }
2057  }
2058 
2059  if ( dataDefinedRotation )
2060  {
2061  //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
2062  double xd = xdiff * cos( angle ) - ydiff * sin( angle );
2063  double yd = xdiff * sin( angle ) + ydiff * cos( angle );
2064  xdiff = xd;
2065  ydiff = yd;
2066  }
2067 
2068  //project xPos and yPos from layer to map CRS
2069  double z = 0;
2070  if ( ct )
2071  {
2072  try
2073  {
2074  ct->transformInPlace( xPos, yPos, z );
2075  }
2076  catch ( QgsCsException &e )
2077  {
2078  Q_UNUSED( e );
2079  QgsDebugMsgLevel( QString( "Ignoring feature %1 due transformation exception on data-defined position" ).arg( f.id() ), 4 );
2080  return;
2081  }
2082  }
2083 
2084  //rotate position with map if data-defined
2085  if ( dataDefinedPosition && m2p.mapRotation() )
2086  {
2087  const QgsPoint& center = context.extent().center();
2088  QTransform t = QTransform::fromTranslate( center.x(), center.y() );
2089  t.rotate( -m2p.mapRotation() );
2090  t.translate( -center.x(), -center.y() );
2091  qreal xPosR, yPosR;
2092  qreal xPos_qreal = xPos, yPos_qreal = yPos;
2093  t.map( xPos_qreal, yPos_qreal, &xPosR, &yPosR );
2094  xPos = xPosR; yPos = yPosR;
2095 
2096  }
2097 
2098  xPos += xdiff;
2099  yPos += ydiff;
2100  }
2101  else
2102  {
2103  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2104  if ( dataDefinedRotation && placement != QgsPalLayerSettings::OverPoint )
2105  {
2106  angle = 0.0;
2107  }
2108  }
2109  }
2110  }
2111 
2112  // data defined always show?
2113  bool alwaysShow = false;
2115  {
2116  alwaysShow = exprVal.toBool();
2117  }
2118 
2119  QgsPalGeometry* lbl = new QgsPalGeometry(
2120  f.id(),
2121  labelText,
2122  geos_geom_clone,
2123  labelFont.letterSpacing(),
2124  labelFont.wordSpacing(),
2125  placement == QgsPalLayerSettings::Curved );
2126 
2127  lbl->setDxfLayer( dxfLayer );
2128 
2129  // record the created geometry - it will be deleted at the end.
2130  geometries.append( lbl );
2131 
2132  // store the label's calculated font for later use during painting
2133  QgsDebugMsgLevel( QString( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString() ).arg( labelFont.styleName() ), 4 );
2134  lbl->setDefinedFont( labelFont );
2135 
2136  // set repeat distance
2137  // data defined repeat distance?
2138  double repeatDist = repeatDistance;
2140  {
2141  bool ok;
2142  double distD = exprVal.toDouble( &ok );
2143  if ( ok )
2144  {
2145  repeatDist = distD;
2146  }
2147  }
2148 
2149  // data defined label-repeat distance units?
2150  bool repeatdistinmapunit = repeatDistanceUnit == QgsPalLayerSettings::MapUnits;
2152  {
2153  QString units = exprVal.toString().trimmed();
2154  QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 );
2155  if ( !units.isEmpty() )
2156  {
2157  repeatdistinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2158  }
2159  }
2160 
2161  if ( !qgsDoubleNear( repeatDist, 0.0 ) )
2162  {
2163  if ( !repeatdistinmapunit )
2164  {
2165  repeatDist *= mapUntsPerMM; //convert repeat distance from mm to map units
2166  }
2167  }
2168 
2169  // feature to the layer
2170  try
2171  {
2172  if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText,
2173  xPos, yPos, dataDefinedPosition, angle, dataDefinedRotation,
2174  quadOffsetX, quadOffsetY, offsetX, offsetY, alwaysShow, repeatDist ) )
2175  return;
2176  }
2177  catch ( std::exception &e )
2178  {
2179  Q_UNUSED( e );
2180  QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( f.id() ) + QString::fromLatin1( e.what() ), 4 );
2181  return;
2182  }
2183 
2184  // TODO: only for placement which needs character info
2185  pal::Feature* feat = palLayer->getFeature( lbl->strId() );
2186  // account for any data defined font metrics adjustments
2187  feat->setLabelInfo( lbl->info( labelFontMetrics, xform, rasterCompressFactor, maxcharanglein, maxcharangleout ) );
2188  delete labelFontMetrics;
2189 
2190  // TODO: allow layer-wide feature dist in PAL...?
2191 
2192  // data defined label-feature distance?
2193  double distance = dist;
2195  {
2196  bool ok;
2197  double distD = exprVal.toDouble( &ok );
2198  if ( ok )
2199  {
2200  distance = distD;
2201  }
2202  }
2203 
2204  // data defined label-feature distance units?
2205  bool distinmapunit = distInMapUnits;
2207  {
2208  QString units = exprVal.toString().trimmed();
2209  QgsDebugMsgLevel( QString( "exprVal DistanceUnits:%1" ).arg( units ), 4 );
2210  if ( !units.isEmpty() )
2211  {
2212  distinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2213  }
2214  }
2215 
2216  if ( distance != 0 )
2217  {
2218  if ( distinmapunit ) //convert distance from mm/map units to pixels
2219  {
2220  distance /= distMapUnitScale.computeMapUnitsPerPixel( context );
2221  }
2222  else //mm
2223  {
2224  distance *= vectorScaleFactor;
2225  }
2226  feat->setDistLabel( qAbs( ptOne.x() - ptZero.x() )* distance );
2227  }
2228 
2229  if ( ddFixedQuad )
2230  {
2231  feat->setFixedQuadrant( true );
2232  }
2233 
2234  // data defined priority?
2236  {
2237  bool ok;
2238  double priorityD = exprVal.toDouble( &ok );
2239  if ( ok )
2240  {
2241  priorityD = qBound( 0.0, priorityD, 10.0 );
2242  priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0
2243  feat->setPriority( priorityD );
2244  }
2245  }
2246 
2247  // data defined is obstacle?
2249  {
2250  feat->setIsObstacle( exprVal.toBool() );
2251  }
2252 
2253  double featObstacleFactor = obstacleFactor;
2255  {
2256  bool ok;
2257  double factorD = exprVal.toDouble( &ok );
2258  if ( ok )
2259  {
2260  factorD = qBound( 0.0, factorD, 10.0 );
2261  factorD = factorD / 5.0 + 0.0001; // convert 0 -> 10 to 0.0001 -> 2.0
2262  featObstacleFactor = factorD;
2263  }
2264  }
2265  feat->setObstacleFactor( featObstacleFactor );
2266 
2267  //add parameters for data defined labeling to QgsPalGeometry
2269  for ( ; dIt != dataDefinedValues.constEnd(); ++dIt )
2270  {
2271  lbl->addDataDefinedValue( dIt.key(), dIt.value() );
2272  }
2273 
2274  // set geometry's pinned property
2275  lbl->setIsPinned( labelIsPinned );
2276 }
2277 
2278 void QgsPalLayerSettings::registerObstacleFeature( QgsFeature& f, const QgsRenderContext& context, QString dxfLayer )
2279 {
2280  mCurFeat = &f;
2281 
2282  const QgsGeometry* geom = f.constGeometry();
2283  if ( !geom )
2284  {
2285  return;
2286  }
2287 
2288  const GEOSGeometry* geos_geom = 0;
2289  QScopedPointer<QgsGeometry> scopedPreparedGeom;
2290 
2292  {
2293  scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, extentGeom ) );
2294  if ( !scopedPreparedGeom.data() )
2295  return;
2296  geos_geom = scopedPreparedGeom.data()->asGeos();
2297  }
2298  else
2299  {
2300  geos_geom = geom->asGeos();
2301  }
2302 
2303  if ( geos_geom == NULL )
2304  return; // invalid geometry
2305 
2306  GEOSGeometry* geos_geom_clone;
2307  geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
2308 
2309  QgsPalGeometry* lbl = new QgsPalGeometry( f.id(), QString(), geos_geom_clone );
2310 
2311  lbl->setDxfLayer( dxfLayer );
2312 
2313  // record the created geometry - it will be deleted at the end.
2314  geometries.append( lbl );
2315 
2316  // feature to the layer
2317  try
2318  {
2319  if ( !palLayer->registerFeature( lbl->strId(), lbl, 0, 0 ) )
2320  return;
2321  }
2322  catch ( std::exception &e )
2323  {
2324  Q_UNUSED( e );
2325  QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( f.id() ) + QString::fromLatin1( e.what() ), 4 );
2326  return;
2327  }
2328 }
2329 
2330 bool QgsPalLayerSettings::dataDefinedValEval( const QString& valType,
2332  QVariant& exprVal, const QgsExpressionContext& context )
2333 {
2334  if ( dataDefinedEvaluate( p, exprVal, &context ) )
2335  {
2336  QString dbgStr = QString( "exprVal %1:" ).arg( mDataDefinedNames.value( p ).first ) + "%1";
2337 
2338  if ( valType == QString( "bool" ) )
2339  {
2340  bool bol = exprVal.toBool();
2341  QgsDebugMsgLevel( dbgStr.arg( bol ? "true" : "false" ), 4 );
2342  dataDefinedValues.insert( p, QVariant( bol ) );
2343  return true;
2344  }
2345  if ( valType == QString( "int" ) )
2346  {
2347  bool ok;
2348  int size = exprVal.toInt( &ok );
2349  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2350 
2351  if ( ok )
2352  {
2353  dataDefinedValues.insert( p, QVariant( size ) );
2354  return true;
2355  }
2356  }
2357  if ( valType == QString( "intpos" ) )
2358  {
2359  bool ok;
2360  int size = exprVal.toInt( &ok );
2361  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2362 
2363  if ( ok && size > 0 )
2364  {
2365  dataDefinedValues.insert( p, QVariant( size ) );
2366  return true;
2367  }
2368  }
2369  if ( valType == QString( "double" ) )
2370  {
2371  bool ok;
2372  double size = exprVal.toDouble( &ok );
2373  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2374 
2375  if ( ok )
2376  {
2377  dataDefinedValues.insert( p, QVariant( size ) );
2378  return true;
2379  }
2380  }
2381  if ( valType == QString( "doublepos" ) )
2382  {
2383  bool ok;
2384  double size = exprVal.toDouble( &ok );
2385  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2386 
2387  if ( ok && size > 0.0 )
2388  {
2389  dataDefinedValues.insert( p, QVariant( size ) );
2390  return true;
2391  }
2392  }
2393  if ( valType == QString( "rotation180" ) )
2394  {
2395  bool ok;
2396  double rot = exprVal.toDouble( &ok );
2397  QgsDebugMsgLevel( dbgStr.arg( rot ), 4 );
2398  if ( ok )
2399  {
2400  if ( rot < -180.0 && rot >= -360 )
2401  {
2402  rot += 360;
2403  }
2404  if ( rot > 180.0 && rot <= 360 )
2405  {
2406  rot -= 360;
2407  }
2408  if ( rot >= -180 && rot <= 180 )
2409  {
2410  dataDefinedValues.insert( p, QVariant( rot ) );
2411  return true;
2412  }
2413  }
2414  }
2415  if ( valType == QString( "transp" ) )
2416  {
2417  bool ok;
2418  int size = exprVal.toInt( &ok );
2419  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2420  if ( ok && size >= 0 && size <= 100 )
2421  {
2422  dataDefinedValues.insert( p, QVariant( size ) );
2423  return true;
2424  }
2425  }
2426  if ( valType == QString( "string" ) )
2427  {
2428  QString str = exprVal.toString(); // don't trim whitespace
2429  QgsDebugMsgLevel( dbgStr.arg( str ), 4 );
2430 
2431  dataDefinedValues.insert( p, QVariant( str ) ); // let it stay empty if it is
2432  return true;
2433  }
2434  if ( valType == QString( "units" ) )
2435  {
2436  QString unitstr = exprVal.toString().trimmed();
2437  QgsDebugMsgLevel( dbgStr.arg( unitstr ), 4 );
2438 
2439  if ( !unitstr.isEmpty() )
2440  {
2441  dataDefinedValues.insert( p, QVariant(( int )_decodeUnits( unitstr ) ) );
2442  return true;
2443  }
2444  }
2445  if ( valType == QString( "color" ) )
2446  {
2447  QString colorstr = exprVal.toString().trimmed();
2448  QgsDebugMsgLevel( dbgStr.arg( colorstr ), 4 );
2449  QColor color = QgsSymbolLayerV2Utils::decodeColor( colorstr );
2450 
2451  if ( color.isValid() )
2452  {
2453  dataDefinedValues.insert( p, QVariant( color ) );
2454  return true;
2455  }
2456  }
2457  if ( valType == QString( "joinstyle" ) )
2458  {
2459  QString joinstr = exprVal.toString().trimmed();
2460  QgsDebugMsgLevel( dbgStr.arg( joinstr ), 4 );
2461 
2462  if ( !joinstr.isEmpty() )
2463  {
2464  dataDefinedValues.insert( p, QVariant(( int )_decodePenJoinStyle( joinstr ) ) );
2465  return true;
2466  }
2467  }
2468  if ( valType == QString( "blendmode" ) )
2469  {
2470  QString blendstr = exprVal.toString().trimmed();
2471  QgsDebugMsgLevel( dbgStr.arg( blendstr ), 4 );
2472 
2473  if ( !blendstr.isEmpty() )
2474  {
2475  dataDefinedValues.insert( p, QVariant(( int )QgsSymbolLayerV2Utils::decodeBlendMode( blendstr ) ) );
2476  return true;
2477  }
2478  }
2479  if ( valType == QString( "pointf" ) )
2480  {
2481  QString ptstr = exprVal.toString().trimmed();
2482  QgsDebugMsgLevel( dbgStr.arg( ptstr ), 4 );
2483 
2484  if ( !ptstr.isEmpty() )
2485  {
2486  dataDefinedValues.insert( p, QVariant( QgsSymbolLayerV2Utils::decodePoint( ptstr ) ) );
2487  return true;
2488  }
2489  }
2490  }
2491  return false;
2492 }
2493 
2494 void QgsPalLayerSettings::parseTextStyle( QFont& labelFont,
2496  const QgsRenderContext& context )
2497 {
2498  // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
2499 
2500  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2501 
2502  // Two ways to generate new data defined font:
2503  // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
2504  // 2) Family + named style (bold or italic is ignored)
2505 
2506  // data defined font family?
2507  QString ddFontFamily( "" );
2509  {
2510  QString family = exprVal.toString().trimmed();
2511  QgsDebugMsgLevel( QString( "exprVal Font family:%1" ).arg( family ), 4 );
2512 
2513  if ( labelFont.family() != family )
2514  {
2515  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
2516  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
2517  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
2518  {
2519  ddFontFamily = family;
2520  }
2521  }
2522  }
2523 
2524  // data defined named font style?
2525  QString ddFontStyle( "" );
2527  {
2528  QString fontstyle = exprVal.toString().trimmed();
2529  QgsDebugMsgLevel( QString( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
2530  ddFontStyle = fontstyle;
2531  }
2532 
2533  // data defined bold font style?
2534  bool ddBold = false;
2535  if ( dataDefinedEvaluate( QgsPalLayerSettings::Bold, exprVal, &context.expressionContext() ) )
2536  {
2537  bool bold = exprVal.toBool();
2538  QgsDebugMsgLevel( QString( "exprVal Font bold:%1" ).arg( bold ? "true" : "false" ), 4 );
2539  ddBold = bold;
2540  }
2541 
2542  // data defined italic font style?
2543  bool ddItalic = false;
2545  {
2546  bool italic = exprVal.toBool();
2547  QgsDebugMsgLevel( QString( "exprVal Font italic:%1" ).arg( italic ? "true" : "false" ), 4 );
2548  ddItalic = italic;
2549  }
2550 
2551  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
2552  // (currently defaults to what has been read in from layer settings)
2553  QFont newFont;
2554  QFont appFont = QApplication::font();
2555  bool newFontBuilt = false;
2556  if ( ddBold || ddItalic )
2557  {
2558  // new font needs built, since existing style needs removed
2559  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
2560  newFontBuilt = true;
2561  newFont.setBold( ddBold );
2562  newFont.setItalic( ddItalic );
2563  }
2564  else if ( !ddFontStyle.isEmpty()
2565  && ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
2566  {
2567  if ( !ddFontFamily.isEmpty() )
2568  {
2569  // both family and style are different, build font from database
2570  QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
2571  if ( appFont != styledfont )
2572  {
2573  newFont = styledfont;
2574  newFontBuilt = true;
2575  }
2576  }
2577 
2578  // update the font face style
2579  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
2580  }
2581  else if ( !ddFontFamily.isEmpty() )
2582  {
2583  if ( ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
2584  {
2585  // just family is different, build font from database
2586  QFont styledfont = mFontDB.font( ddFontFamily, textNamedStyle, appFont.pointSize() );
2587  if ( appFont != styledfont )
2588  {
2589  newFont = styledfont;
2590  newFontBuilt = true;
2591  }
2592  }
2593  else
2594  {
2595  newFont = QFont( ddFontFamily );
2596  newFontBuilt = true;
2597  }
2598  }
2599 
2600  if ( newFontBuilt )
2601  {
2602  // copy over existing font settings
2603  //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
2604  newFont.setPixelSize( labelFont.pixelSize() );
2605  newFont.setCapitalization( labelFont.capitalization() );
2606  newFont.setUnderline( labelFont.underline() );
2607  newFont.setStrikeOut( labelFont.strikeOut() );
2608  newFont.setWordSpacing( labelFont.wordSpacing() );
2609  newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
2610 
2611  labelFont = newFont;
2612  }
2613 
2614  // data defined word spacing?
2615  double wordspace = labelFont.wordSpacing();
2617  {
2618  bool ok;
2619  double wspacing = exprVal.toDouble( &ok );
2620  QgsDebugMsgLevel( QString( "exprVal FontWordSpacing:%1" ).arg( wspacing ), 4 );
2621  if ( ok )
2622  {
2623  wordspace = wspacing;
2624  }
2625  }
2626  labelFont.setWordSpacing( sizeToPixel( wordspace, context, fontunits, false, fontSizeMapUnitScale ) );
2627 
2628  // data defined letter spacing?
2629  double letterspace = labelFont.letterSpacing();
2631  {
2632  bool ok;
2633  double lspacing = exprVal.toDouble( &ok );
2634  QgsDebugMsgLevel( QString( "exprVal FontLetterSpacing:%1" ).arg( lspacing ), 4 );
2635  if ( ok )
2636  {
2637  letterspace = lspacing;
2638  }
2639  }
2640  labelFont.setLetterSpacing( QFont::AbsoluteSpacing, sizeToPixel( letterspace, context, fontunits, false, fontSizeMapUnitScale ) );
2641 
2642  // data defined font capitalization?
2643  QFont::Capitalization fontcaps = labelFont.capitalization();
2645  {
2646  QString fcase = exprVal.toString().trimmed();
2647  QgsDebugMsgLevel( QString( "exprVal FontCase:%1" ).arg( fcase ), 4 );
2648 
2649  if ( !fcase.isEmpty() )
2650  {
2651  if ( fcase.compare( "NoChange", Qt::CaseInsensitive ) == 0 )
2652  {
2653  fontcaps = QFont::MixedCase;
2654  }
2655  else if ( fcase.compare( "Upper", Qt::CaseInsensitive ) == 0 )
2656  {
2657  fontcaps = QFont::AllUppercase;
2658  }
2659  else if ( fcase.compare( "Lower", Qt::CaseInsensitive ) == 0 )
2660  {
2661  fontcaps = QFont::AllLowercase;
2662  }
2663  else if ( fcase.compare( "Capitalize", Qt::CaseInsensitive ) == 0 )
2664  {
2665  fontcaps = QFont::Capitalize;
2666  }
2667 
2668  if ( fontcaps != labelFont.capitalization() )
2669  {
2670  labelFont.setCapitalization( fontcaps );
2671  }
2672  }
2673  }
2674 
2675  // data defined strikeout font style?
2677  {
2678  bool strikeout = exprVal.toBool();
2679  QgsDebugMsgLevel( QString( "exprVal Font strikeout:%1" ).arg( strikeout ? "true" : "false" ), 4 );
2680  labelFont.setStrikeOut( strikeout );
2681  }
2682 
2683  // data defined underline font style?
2685  {
2686  bool underline = exprVal.toBool();
2687  QgsDebugMsgLevel( QString( "exprVal Font underline:%1" ).arg( underline ? "true" : "false" ), 4 );
2688  labelFont.setUnderline( underline );
2689  }
2690 
2691  // pass the rest on to QgsPalLabeling::drawLabeling
2692 
2693  // data defined font color?
2694  dataDefinedValEval( "color", QgsPalLayerSettings::Color, exprVal, context.expressionContext() );
2695 
2696  // data defined font transparency?
2697  dataDefinedValEval( "transp", QgsPalLayerSettings::FontTransp, exprVal, context.expressionContext() );
2698 
2699  // data defined font blend mode?
2700  dataDefinedValEval( "blendmode", QgsPalLayerSettings::FontBlendMode, exprVal, context.expressionContext() );
2701 
2702 }
2703 
2704 void QgsPalLayerSettings::parseTextBuffer( const QgsRenderContext &context )
2705 {
2706  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2707 
2708  // data defined draw buffer?
2709  bool drawBuffer = bufferDraw;
2710  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::BufferDraw, exprVal, context.expressionContext() ) )
2711  {
2712  drawBuffer = exprVal.toBool();
2713  }
2714 
2715  if ( !drawBuffer )
2716  {
2717  return;
2718  }
2719 
2720  // data defined buffer size?
2721  double bufrSize = bufferSize;
2722  if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::BufferSize, exprVal, context.expressionContext() ) )
2723  {
2724  bufrSize = exprVal.toDouble();
2725  }
2726 
2727  // data defined buffer transparency?
2728  int bufTransp = bufferTransp;
2729  if ( dataDefinedValEval( "transp", QgsPalLayerSettings::BufferTransp, exprVal, context.expressionContext() ) )
2730  {
2731  bufTransp = exprVal.toInt();
2732  }
2733 
2734  drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufTransp < 100 );
2735 
2736  if ( !drawBuffer )
2737  {
2738  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
2739  dataDefinedValues.remove( QgsPalLayerSettings::BufferSize );
2740  dataDefinedValues.remove( QgsPalLayerSettings::BufferTransp );
2741  return; // don't bother evaluating values that won't be used
2742  }
2743 
2744  // data defined buffer units?
2745  dataDefinedValEval( "units", QgsPalLayerSettings::BufferUnit, exprVal, context.expressionContext() );
2746 
2747  // data defined buffer color?
2748  dataDefinedValEval( "color", QgsPalLayerSettings::BufferColor, exprVal, context.expressionContext() );
2749 
2750  // data defined buffer pen join style?
2751  dataDefinedValEval( "joinstyle", QgsPalLayerSettings::BufferJoinStyle, exprVal, context.expressionContext() );
2752 
2753  // data defined buffer blend mode?
2754  dataDefinedValEval( "blendmode", QgsPalLayerSettings::BufferBlendMode, exprVal, context.expressionContext() );
2755 }
2756 
2757 void QgsPalLayerSettings::parseTextFormatting( const QgsRenderContext &context )
2758 {
2759  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2760 
2761  // data defined multiline wrap character?
2762  QString wrapchr = wrapChar;
2763  if ( dataDefinedValEval( "string", QgsPalLayerSettings::MultiLineWrapChar, exprVal, context.expressionContext() ) )
2764  {
2765  wrapchr = exprVal.toString();
2766  }
2767 
2768  // data defined multiline height?
2769  dataDefinedValEval( "double", QgsPalLayerSettings::MultiLineHeight, exprVal, context.expressionContext() );
2770 
2771  // data defined multiline text align?
2773  {
2774  QString str = exprVal.toString().trimmed();
2775  QgsDebugMsgLevel( QString( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
2776 
2777  if ( !str.isEmpty() )
2778  {
2779  // "Left"
2781 
2782  if ( str.compare( "Center", Qt::CaseInsensitive ) == 0 )
2783  {
2785  }
2786  else if ( str.compare( "Right", Qt::CaseInsensitive ) == 0 )
2787  {
2788  aligntype = QgsPalLayerSettings::MultiRight;
2789  }
2790  else if ( str.compare( "Follow", Qt::CaseInsensitive ) == 0 )
2791  {
2793  }
2794  dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant(( int )aligntype ) );
2795  }
2796  }
2797 
2798  // data defined direction symbol?
2799  bool drawDirSymb = addDirectionSymbol;
2800  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::DirSymbDraw, exprVal, context.expressionContext() ) )
2801  {
2802  drawDirSymb = exprVal.toBool();
2803  }
2804 
2805  if ( drawDirSymb )
2806  {
2807  // data defined direction left symbol?
2808  dataDefinedValEval( "string", QgsPalLayerSettings::DirSymbLeft, exprVal, context.expressionContext() );
2809 
2810  // data defined direction right symbol?
2811  dataDefinedValEval( "string", QgsPalLayerSettings::DirSymbRight, exprVal, context.expressionContext() );
2812 
2813  // data defined direction symbol placement?
2815  {
2816  QString str = exprVal.toString().trimmed();
2817  QgsDebugMsgLevel( QString( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
2818 
2819  if ( !str.isEmpty() )
2820  {
2821  // "LeftRight"
2823 
2824  if ( str.compare( "Above", Qt::CaseInsensitive ) == 0 )
2825  {
2827  }
2828  else if ( str.compare( "Below", Qt::CaseInsensitive ) == 0 )
2829  {
2831  }
2832  dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant(( int )placetype ) );
2833  }
2834  }
2835 
2836  // data defined direction symbol reversed?
2837  dataDefinedValEval( "bool", QgsPalLayerSettings::DirSymbReverse, exprVal, context.expressionContext() );
2838  }
2839 
2840  // formatting for numbers is inline with generation of base label text and not passed to label painting
2841 }
2842 
2843 void QgsPalLayerSettings::parseShapeBackground( const QgsRenderContext &context )
2844 {
2845  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2846 
2847  // data defined draw shape?
2848  bool drawShape = shapeDraw;
2849  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::ShapeDraw, exprVal, context.expressionContext() ) )
2850  {
2851  drawShape = exprVal.toBool();
2852  }
2853 
2854  if ( !drawShape )
2855  {
2856  return;
2857  }
2858 
2859  // data defined shape transparency?
2860  int shapeTransp = shapeTransparency;
2861  if ( dataDefinedValEval( "transp", QgsPalLayerSettings::ShapeTransparency, exprVal, context.expressionContext() ) )
2862  {
2863  shapeTransp = exprVal.toInt();
2864  }
2865 
2866  drawShape = ( drawShape && shapeTransp < 100 ); // size is not taken into account (could be)
2867 
2868  if ( !drawShape )
2869  {
2870  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
2871  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
2872  return; // don't bother evaluating values that won't be used
2873  }
2874 
2875  // data defined shape kind?
2878  {
2879  QString skind = exprVal.toString().trimmed();
2880  QgsDebugMsgLevel( QString( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
2881 
2882  if ( !skind.isEmpty() )
2883  {
2884  // "Rectangle"
2886 
2887  if ( skind.compare( "Square", Qt::CaseInsensitive ) == 0 )
2888  {
2890  }
2891  else if ( skind.compare( "Ellipse", Qt::CaseInsensitive ) == 0 )
2892  {
2894  }
2895  else if ( skind.compare( "Circle", Qt::CaseInsensitive ) == 0 )
2896  {
2898  }
2899  else if ( skind.compare( "SVG", Qt::CaseInsensitive ) == 0 )
2900  {
2902  }
2903  shapeKind = shpkind;
2904  dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant(( int )shpkind ) );
2905  }
2906  }
2907 
2908  // data defined shape SVG path?
2909  QString svgPath = shapeSVGFile;
2911  {
2912  QString svgfile = exprVal.toString().trimmed();
2913  QgsDebugMsgLevel( QString( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
2914 
2915  // '' empty paths are allowed
2916  svgPath = svgfile;
2917  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgfile ) );
2918  }
2919 
2920  // data defined shape size type?
2923  {
2924  QString stype = exprVal.toString().trimmed();
2925  QgsDebugMsgLevel( QString( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
2926 
2927  if ( !stype.isEmpty() )
2928  {
2929  // "Buffer"
2931 
2932  if ( stype.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
2933  {
2935  }
2936  shpSizeType = sizType;
2937  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant(( int )sizType ) );
2938  }
2939  }
2940 
2941  // data defined shape size X? (SVGs only use X for sizing)
2942  double ddShpSizeX = shapeSize.x();
2943  if ( dataDefinedValEval( "double", QgsPalLayerSettings::ShapeSizeX, exprVal, context.expressionContext() ) )
2944  {
2945  ddShpSizeX = exprVal.toDouble();
2946  }
2947 
2948  // data defined shape size Y?
2949  double ddShpSizeY = shapeSize.y();
2950  if ( dataDefinedValEval( "double", QgsPalLayerSettings::ShapeSizeY, exprVal, context.expressionContext() ) )
2951  {
2952  ddShpSizeY = exprVal.toDouble();
2953  }
2954 
2955  // don't continue under certain circumstances (e.g. size is fixed)
2956  bool skip = false;
2957  if ( shapeKind == QgsPalLayerSettings::ShapeSVG
2958  && ( svgPath.isEmpty()
2959  || ( !svgPath.isEmpty()
2960  && shpSizeType == QgsPalLayerSettings::SizeFixed
2961  && ddShpSizeX == 0.0 ) ) )
2962  {
2963  skip = true;
2964  }
2965  if ( shapeKind != QgsPalLayerSettings::ShapeSVG
2966  && shpSizeType == QgsPalLayerSettings::SizeFixed
2967  && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
2968  {
2969  skip = true;
2970  }
2971 
2972  if ( skip )
2973  {
2974  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
2975  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
2976  dataDefinedValues.remove( QgsPalLayerSettings::ShapeKind );
2977  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSVGFile );
2978  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeX );
2979  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeY );
2980  return; // don't bother evaluating values that won't be used
2981  }
2982 
2983  // data defined shape size units?
2984  dataDefinedValEval( "units", QgsPalLayerSettings::ShapeSizeUnits, exprVal, context.expressionContext() );
2985 
2986  // data defined shape rotation type?
2988  {
2989  QString rotstr = exprVal.toString().trimmed();
2990  QgsDebugMsgLevel( QString( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
2991 
2992  if ( !rotstr.isEmpty() )
2993  {
2994  // "Sync"
2996 
2997  if ( rotstr.compare( "Offset", Qt::CaseInsensitive ) == 0 )
2998  {
3000  }
3001  else if ( rotstr.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
3002  {
3004  }
3005  dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant(( int )rottype ) );
3006  }
3007  }
3008 
3009  // data defined shape rotation?
3010  dataDefinedValEval( "rotation180", QgsPalLayerSettings::ShapeRotation, exprVal, context.expressionContext() );
3011 
3012  // data defined shape offset?
3013  dataDefinedValEval( "pointf", QgsPalLayerSettings::ShapeOffset, exprVal, context.expressionContext() );
3014 
3015  // data defined shape offset units?
3016  dataDefinedValEval( "units", QgsPalLayerSettings::ShapeOffsetUnits, exprVal, context.expressionContext() );
3017 
3018  // data defined shape radii?
3019  dataDefinedValEval( "pointf", QgsPalLayerSettings::ShapeRadii, exprVal, context.expressionContext() );
3020 
3021  // data defined shape radii units?
3022  dataDefinedValEval( "units", QgsPalLayerSettings::ShapeRadiiUnits, exprVal, context.expressionContext() );
3023 
3024  // data defined shape blend mode?
3025  dataDefinedValEval( "blendmode", QgsPalLayerSettings::ShapeBlendMode, exprVal, context.expressionContext() );
3026 
3027  // data defined shape fill color?
3028  dataDefinedValEval( "color", QgsPalLayerSettings::ShapeFillColor, exprVal, context.expressionContext() );
3029 
3030  // data defined shape border color?
3031  dataDefinedValEval( "color", QgsPalLayerSettings::ShapeBorderColor, exprVal, context.expressionContext() );
3032 
3033  // data defined shape border width?
3034  dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShapeBorderWidth, exprVal, context.expressionContext() );
3035 
3036  // data defined shape border width units?
3037  dataDefinedValEval( "units", QgsPalLayerSettings::ShapeBorderWidthUnits, exprVal, context.expressionContext() );
3038 
3039  // data defined shape join style?
3040  dataDefinedValEval( "joinstyle", QgsPalLayerSettings::ShapeJoinStyle, exprVal, context.expressionContext() );
3041 
3042 }
3043 
3044 void QgsPalLayerSettings::parseDropShadow( const QgsRenderContext &context )
3045 {
3046  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
3047 
3048  // data defined draw shadow?
3049  bool drawShadow = shadowDraw;
3050  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::ShadowDraw, exprVal, context.expressionContext() ) )
3051  {
3052  drawShadow = exprVal.toBool();
3053  }
3054 
3055  if ( !drawShadow )
3056  {
3057  return;
3058  }
3059 
3060  // data defined shadow transparency?
3061  int shadowTransp = shadowTransparency;
3062  if ( dataDefinedValEval( "transp", QgsPalLayerSettings::ShadowTransparency, exprVal, context.expressionContext() ) )
3063  {
3064  shadowTransp = exprVal.toInt();
3065  }
3066 
3067  // data defined shadow offset distance?
3068  double shadowOffDist = shadowOffsetDist;
3069  if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShadowOffsetDist, exprVal, context.expressionContext() ) )
3070  {
3071  shadowOffDist = exprVal.toDouble();
3072  }
3073 
3074  // data defined shadow offset distance?
3075  double shadowRad = shadowRadius;
3076  if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext() ) )
3077  {
3078  shadowRad = exprVal.toDouble();
3079  }
3080 
3081  drawShadow = ( drawShadow && shadowTransp < 100 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
3082 
3083  if ( !drawShadow )
3084  {
3085  dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
3086  dataDefinedValues.remove( QgsPalLayerSettings::ShadowTransparency );
3087  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOffsetDist );
3088  dataDefinedValues.remove( QgsPalLayerSettings::ShadowRadius );
3089  return; // don't bother evaluating values that won't be used
3090  }
3091 
3092  // data defined shadow under type?
3094  {
3095  QString str = exprVal.toString().trimmed();
3096  QgsDebugMsgLevel( QString( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
3097 
3098  if ( !str.isEmpty() )
3099  {
3100  // "Lowest"
3102 
3103  if ( str.compare( "Text", Qt::CaseInsensitive ) == 0 )
3104  {
3106  }
3107  else if ( str.compare( "Buffer", Qt::CaseInsensitive ) == 0 )
3108  {
3110  }
3111  else if ( str.compare( "Background", Qt::CaseInsensitive ) == 0 )
3112  {
3114  }
3115  dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant(( int )shdwtype ) );
3116  }
3117  }
3118 
3119  // data defined shadow offset angle?
3120  dataDefinedValEval( "rotation180", QgsPalLayerSettings::ShadowOffsetAngle, exprVal, context.expressionContext() );
3121 
3122  // data defined shadow offset units?
3123  dataDefinedValEval( "units", QgsPalLayerSettings::ShadowOffsetUnits, exprVal, context.expressionContext() );
3124 
3125  // data defined shadow radius?
3126  dataDefinedValEval( "double", QgsPalLayerSettings::ShadowRadius, exprVal, context.expressionContext() );
3127 
3128  // data defined shadow radius units?
3129  dataDefinedValEval( "units", QgsPalLayerSettings::ShadowRadiusUnits, exprVal, context.expressionContext() );
3130 
3131  // data defined shadow scale? ( gui bounds to 0-2000, no upper bound here )
3132  dataDefinedValEval( "intpos", QgsPalLayerSettings::ShadowScale, exprVal, context.expressionContext() );
3133 
3134  // data defined shadow color?
3135  dataDefinedValEval( "color", QgsPalLayerSettings::ShadowColor, exprVal, context.expressionContext() );
3136 
3137  // data defined shadow blend mode?
3138  dataDefinedValEval( "blendmode", QgsPalLayerSettings::ShadowBlendMode, exprVal, context.expressionContext() );
3139 }
3140 
3141 int QgsPalLayerSettings::sizeToPixel( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor, const QgsMapUnitScale& mapUnitScale ) const
3142 {
3143  return ( int )( scaleToPixelContext( size, c, unit, rasterfactor, mapUnitScale ) + 0.5 );
3144 }
3145 
3146 double QgsPalLayerSettings::scaleToPixelContext( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor, const QgsMapUnitScale& mapUnitScale ) const
3147 {
3148  // if render context is that of device (i.e. not a scaled map), just return size
3149  double mapUnitsPerPixel = mapUnitScale.computeMapUnitsPerPixel( c );
3150 
3151  if ( unit == MapUnits && mapUnitsPerPixel > 0.0 )
3152  {
3153  size = size / mapUnitsPerPixel * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3154  }
3155  else // e.g. in points or mm
3156  {
3157  double ptsTomm = ( unit == Points ? 0.352778 : 1 );
3158  size *= ptsTomm * c.scaleFactor() * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3159  }
3160  return size;
3161 }
3162 
3163 // -------------
3164 
3166  : mMapSettings( NULL )
3167  , mPal( NULL )
3168  , mDrawLabelRectOnly( false )
3169  , mShowingCandidates( false )
3170  , mShowingAllLabels( false )
3171  , mShowingShadowRects( false )
3172  , mDrawOutlineLabels( true )
3173  , mResults( 0 )
3174 {
3175 
3176  // find out engine defaults
3177  Pal p;
3178  mCandPoint = p.getPointP();
3179  mCandLine = p.getLineP();
3180  mCandPolygon = p.getPolyP();
3182 
3183  switch ( p.getSearch() )
3184  {
3185  case CHAIN: mSearch = Chain; break;
3186  case POPMUSIC_TABU: mSearch = Popmusic_Tabu; break;
3187  case POPMUSIC_CHAIN: mSearch = Popmusic_Chain; break;
3189  case FALP: mSearch = Falp; break;
3190  }
3191 }
3192 
3194 {
3195  // make sure we've freed everything
3196  exit();
3197 
3199 
3200  delete mResults;
3201  mResults = 0;
3202 }
3203 
3205 {
3206  return staticWillUseLayer( layer );
3207 }
3208 
3210 {
3211  QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( layerID ) );
3212  if ( !layer )
3213  return false;
3214  return staticWillUseLayer( layer );
3215 }
3216 
3217 
3219 {
3220  // don't do QgsPalLayerSettings::readFromLayer( layer ) if not needed
3221  bool enabled = false;
3222  if ( layer->customProperty( "labeling" ).toString() == QString( "pal" ) )
3223  enabled = layer->labelsEnabled() || layer->diagramsEnabled();
3224 
3225  return enabled;
3226 }
3227 
3228 
3230 {
3232  for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
3233  {
3234  clearActiveLayer( lit.key() );
3235  }
3236  mActiveLayers.clear();
3237 }
3238 
3240 {
3241  QgsPalLayerSettings& lyr = mActiveLayers[layerID];
3242 
3243  // delete all QgsDataDefined objects (which also deletes their QgsExpression object)
3245  for ( ; it != lyr.dataDefinedProperties.constEnd(); ++it )
3246  {
3247  delete( it.value() );
3248  it.value() = 0;
3249  }
3251 }
3252 
3254 {
3255  Q_ASSERT( mMapSettings != NULL );
3256 
3257  if ( !willUseLayer( layer ) || !layer->labelsEnabled() )
3258  {
3259  return 0;
3260  }
3261 
3262  QgsDebugMsgLevel( "PREPARE LAYER " + layer->id(), 4 );
3263 
3264  // start with a temporary settings class, find out labeling info
3265  QgsPalLayerSettings lyrTmp;
3266  lyrTmp.readFromLayer( layer );
3267 
3268  if ( lyrTmp.drawLabels )
3269  {
3270  if ( lyrTmp.fieldName.isEmpty() )
3271  {
3272  return 0;
3273  }
3274 
3275  if ( lyrTmp.isExpression )
3276  {
3277  QgsExpression exp( lyrTmp.fieldName );
3278  if ( exp.hasEvalError() )
3279  {
3280  QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 );
3281  return 0;
3282  }
3283  }
3284  else
3285  {
3286  // If we aren't an expression, we check to see if we can find the column.
3287  if ( layer->fieldNameIndex( lyrTmp.fieldName ) == -1 )
3288  {
3289  return 0;
3290  }
3291  }
3292  }
3293 
3294  // add layer settings to the pallabeling hashtable: <QgsVectorLayer*, QgsPalLayerSettings>
3295  mActiveLayers.insert( layer->id(), lyrTmp );
3296  // start using the reference to the layer in hashtable instead of local instance
3297  QgsPalLayerSettings& lyr = mActiveLayers[layer->id()];
3298 
3299  lyr.mCurFields = layer->fields();
3300 
3301  if ( lyrTmp.drawLabels )
3302  {
3303  // add field indices for label's text, from expression or field
3304  if ( lyr.isExpression )
3305  {
3306  // prepare expression for use in QgsPalLayerSettings::registerFeature()
3307  QgsExpression* exp = lyr.getLabelExpression();
3308  exp->prepare( &ctx.expressionContext() );
3309  if ( exp->hasEvalError() )
3310  {
3311  QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 );
3312  }
3313  foreach ( QString name, exp->referencedColumns() )
3314  {
3315  QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 );
3316  attrNames.append( name );
3317  }
3318  }
3319  else
3320  {
3321  attrNames.append( lyr.fieldName );
3322  }
3323 
3324  // add field indices of data defined expression or field
3326  for ( ; dIt != lyr.dataDefinedProperties.constEnd(); ++dIt )
3327  {
3328  QgsDataDefined* dd = dIt.value();
3329  if ( !dd->isActive() )
3330  {
3331  continue;
3332  }
3333 
3334  // NOTE: the following also prepares any expressions for later use
3335 
3336  // store parameters for data defined expressions
3337  QMap<QString, QVariant> exprParams;
3338  exprParams.insert( "scale", ctx.rendererScale() );
3339 
3340  dd->setExpressionParams( exprParams );
3341 
3342  // this will return columns for expressions or field name, depending upon what is set to be used
3343  QStringList cols = dd->referencedColumns( ctx.expressionContext() ); // <-- prepares any expressions, too
3344 
3345  //QgsDebugMsgLevel( QString( "Data defined referenced columns:" ) + cols.join( "," ), 4 );
3346  foreach ( QString name, cols )
3347  {
3348  attrNames.append( name );
3349  }
3350  }
3351  }
3352 
3353  // how to place the labels
3354  Arrangement arrangement;
3355  switch ( lyr.placement )
3356  {
3357  case QgsPalLayerSettings::AroundPoint: arrangement = P_POINT; break;
3358  case QgsPalLayerSettings::OverPoint: arrangement = P_POINT_OVER; break;
3359  case QgsPalLayerSettings::Line: arrangement = P_LINE; break;
3360  case QgsPalLayerSettings::Curved: arrangement = P_CURVED; break;
3361  case QgsPalLayerSettings::Horizontal: arrangement = P_HORIZ; break;
3362  case QgsPalLayerSettings::Free: arrangement = P_FREE; break;
3363  default: Q_ASSERT( "unsupported placement" && 0 ); return 0;
3364  }
3365 
3366  // create the pal layer
3367  double priority = 1 - lyr.priority / 10.0; // convert 0..10 --> 1..0
3368 
3369  Layer* l = mPal->addLayer( layer->id(),
3370  arrangement,
3371  priority, lyr.obstacle, true, lyr.drawLabels,
3372  lyr.displayAll );
3373 
3374  if ( lyr.placementFlags )
3375  l->setArrangementFlags(( LineArrangementFlags )lyr.placementFlags );
3376 
3377  // set label mode (label per feature is the default)
3378  l->setLabelMode( lyr.labelPerPart ? Layer::LabelPerFeaturePart : Layer::LabelPerFeature );
3379 
3380  // set whether adjacent lines should be merged
3382 
3383  // set obstacle type
3384  switch ( lyr.obstacleType )
3385  {
3386  case PolygonInterior:
3388  break;
3389  case PolygonBoundary:
3391  break;
3392  }
3393 
3394  // set whether location of centroid must be inside of polygons
3396 
3397  // set whether labels must fall completely within the polygon
3399 
3400  // set how to show upside-down labels
3401  Layer::UpsideDownLabels upsdnlabels;
3402  switch ( lyr.upsidedownLabels )
3403  {
3404  case QgsPalLayerSettings::Upright: upsdnlabels = Layer::Upright; break;
3405  case QgsPalLayerSettings::ShowDefined: upsdnlabels = Layer::ShowDefined; break;
3406  case QgsPalLayerSettings::ShowAll: upsdnlabels = Layer::ShowAll; break;
3407  default: Q_ASSERT( "unsupported upside-down label setting" && 0 ); return 0;
3408  }
3409  l->setUpsidedownLabels( upsdnlabels );
3410 
3411 // // fix for font size in map units causing font to show pointsize at small map scales
3412 // int pixelFontSize = lyr.sizeToPixel( lyr.textFont.pointSizeF(), ctx,
3413 // lyr.fontSizeInMapUnits ? QgsPalLayerSettings::MapUnits : QgsPalLayerSettings::Points,
3414 // true );
3415 
3416 // if ( pixelFontSize < 1 )
3417 // {
3418 // lyr.textFont.setPointSize( 1 );
3419 // lyr.textFont.setPixelSize( 1 );
3420 // }
3421 // else
3422 // {
3423 // lyr.textFont.setPixelSize( pixelFontSize );
3424 // }
3425 
3426 // // scale spacing sizes if using map units
3427 // if ( lyr.fontSizeInMapUnits )
3428 // {
3429 // double spacingPixelSize;
3430 // if ( lyr.textFont.wordSpacing() != 0 )
3431 // {
3432 // spacingPixelSize = lyr.textFont.wordSpacing() / ctx.mapToPixel().mapUnitsPerPixel() * ctx.rasterScaleFactor();
3433 // lyr.textFont.setWordSpacing( spacingPixelSize );
3434 // }
3435 
3436 // if ( lyr.textFont.letterSpacing() != 0 )
3437 // {
3438 // spacingPixelSize = lyr.textFont.letterSpacing() / ctx.mapToPixel().mapUnitsPerPixel() * ctx.rasterScaleFactor();
3439 // lyr.textFont.setLetterSpacing( QFont::AbsoluteSpacing, spacingPixelSize );
3440 // }
3441 // }
3442 
3443  //raster and vector scale factors
3444  lyr.vectorScaleFactor = ctx.scaleFactor();
3446 
3447  // save the pal layer to our layer context (with some additional info)
3448  lyr.palLayer = l;
3449  lyr.fieldIndex = layer->fieldNameIndex( lyr.fieldName );
3450 
3451  lyr.xform = &mMapSettings->mapToPixel();
3452  lyr.ct = 0;
3454  lyr.ct = ctx.coordinateTransform()->clone();
3455  lyr.ptZero = lyr.xform->toMapCoordinates( 0, 0 );
3456  lyr.ptOne = lyr.xform->toMapCoordinates( 1, 0 );
3457 
3458  // rect for clipping
3460  if ( !qgsDoubleNear( mMapSettings->rotation(), 0.0 ) )
3461  {
3462  //PAL features are prerotated, so extent also needs to be unrotated
3464  }
3465 
3466  lyr.mFeatsSendingToPal = 0;
3467 
3468  return 1; // init successful
3469 }
3470 
3472 {
3473  double priority = 1 - s->priority / 10.0; // convert 0..10 --> 1..0
3474  Layer* l = mPal->addLayer( layer->id().append( "d" ).toUtf8().data(), pal::Arrangement( s->placement ), priority, s->obstacle, true, true );
3475  l->setArrangementFlags(( LineArrangementFlags )s->placementFlags );
3476 
3477  mActiveDiagramLayers.insert( layer->id(), *s );
3478  // initialize the local copy
3480 
3481  s2.palLayer = l;
3482  s2.ct = 0;
3484  s2.ct = new QgsCoordinateTransform( layer->crs(), mMapSettings->destinationCrs() );
3485 
3486  s2.xform = &mMapSettings->mapToPixel();
3487 
3488  s2.fields = layer->fields();
3489 
3490  s2.renderer = layer->diagramRenderer()->clone();
3491 
3492  return 1;
3493 }
3494 
3495 void QgsPalLabeling::registerFeature( const QString& layerID, QgsFeature& f, const QgsRenderContext& context, QString dxfLayer )
3496 {
3497  QgsPalLayerSettings& lyr = mActiveLayers[layerID];
3498  lyr.registerFeature( f, context, dxfLayer );
3499 }
3500 
3501 bool QgsPalLabeling::geometryRequiresPreparation( const QgsGeometry* geometry, const QgsRenderContext& context, const QgsCoordinateTransform* ct, QgsGeometry* clipGeometry )
3502 {
3503  if ( !geometry )
3504  {
3505  return false;
3506  }
3507 
3508  //requires reprojection
3509  if ( ct )
3510  return true;
3511 
3512  //requires fixing
3513  if ( geometry->type() == QGis::Polygon && !geometry->isGeosValid() )
3514  return true;
3515 
3516  //requires rotation
3517  const QgsMapToPixel& m2p = context.mapToPixel();
3518  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
3519  return true;
3520 
3521  //requires clip
3522  if ( clipGeometry && !clipGeometry->contains( geometry ) )
3523  return true;
3524 
3525  return false;
3526 }
3527 
3528 QStringList QgsPalLabeling::splitToLines( const QString &text, const QString &wrapCharacter )
3529 {
3530  QStringList multiLineSplit;
3531  if ( !wrapCharacter.isEmpty() && wrapCharacter != QString( "\n" ) )
3532  {
3533  //wrap on both the wrapchr and new line characters
3534  foreach ( QString line, text.split( wrapCharacter ) )
3535  {
3536  multiLineSplit.append( line.split( QString( "\n" ) ) );
3537  }
3538  }
3539  else
3540  {
3541  multiLineSplit = text.split( "\n" );
3542  }
3543 
3544  return multiLineSplit;
3545 }
3546 
3548 {
3549  QStringList graphemes;
3550  QTextBoundaryFinder boundaryFinder( QTextBoundaryFinder::Grapheme, text );
3551  int currentBoundary = -1;
3552  int previousBoundary = 0;
3553  while (( currentBoundary = boundaryFinder.toNextBoundary() ) > 0 )
3554  {
3555  graphemes << text.mid( previousBoundary, currentBoundary - previousBoundary );
3556  previousBoundary = currentBoundary;
3557  }
3558  return graphemes;
3559 }
3560 
3562 {
3563  if ( !geometry )
3564  {
3565  return 0;
3566  }
3567 
3568  //don't modify the feature's geometry so that geometry based expressions keep working
3569  QgsGeometry* geom = new QgsGeometry( *geometry );
3570  QScopedPointer<QgsGeometry> clonedGeometry( geom );
3571 
3572  //reproject the geometry if necessary
3573  if ( ct )
3574  {
3575  try
3576  {
3577  geom->transform( *ct );
3578  }
3579  catch ( QgsCsException &cse )
3580  {
3581  Q_UNUSED( cse );
3582  QgsDebugMsgLevel( QString( "Ignoring feature due to transformation exception" ), 4 );
3583  return 0;
3584  }
3585  }
3586 
3587  // Rotate the geometry if needed, before clipping
3588  const QgsMapToPixel& m2p = context.mapToPixel();
3589  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
3590  {
3591  QgsPoint center = context.extent().center();
3592 
3593  if ( ct )
3594  {
3595  try
3596  {
3597  center = ct->transform( center );
3598  }
3599  catch ( QgsCsException &cse )
3600  {
3601  Q_UNUSED( cse );
3602  QgsDebugMsgLevel( QString( "Ignoring feature due to transformation exception" ), 4 );
3603  return 0;
3604  }
3605  }
3606 
3607  if ( geom->rotate( m2p.mapRotation(), center ) )
3608  {
3609  QgsDebugMsg( QString( "Error rotating geometry" ).arg( geom->exportToWkt() ) );
3610  return 0;
3611  }
3612  }
3613 
3614  if ( !geom->asGeos() )
3615  return 0; // there is something really wrong with the geometry
3616 
3617  // fix invalid polygons
3618  if ( geom->type() == QGis::Polygon && !geom->isGeosValid() )
3619  {
3620  QgsGeometry* bufferGeom = geom->buffer( 0, 0 );
3621  if ( !bufferGeom )
3622  {
3623  return 0;
3624  }
3625  geom = bufferGeom;
3626  clonedGeometry.reset( geom );
3627  }
3628 
3629  if ( clipGeometry && !clipGeometry->contains( geom ) )
3630  {
3631  QgsGeometry* clipGeom = geom->intersection( clipGeometry ); // creates new geometry
3632  if ( !clipGeom )
3633  {
3634  return 0;
3635  }
3636  geom = clipGeom;
3637  clonedGeometry.reset( geom );
3638  }
3639 
3640  return clonedGeometry.take();
3641 }
3642 
3643 bool QgsPalLabeling::checkMinimumSizeMM( const QgsRenderContext& context, const QgsGeometry* geom, double minSize )
3644 {
3645  if ( minSize <= 0 )
3646  {
3647  return true;
3648  }
3649 
3650  if ( !geom )
3651  {
3652  return false;
3653  }
3654 
3655  QGis::GeometryType featureType = geom->type();
3656  if ( featureType == QGis::Point ) //minimum size does not apply to point features
3657  {
3658  return true;
3659  }
3660 
3661  double mapUnitsPerMM = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
3662  if ( featureType == QGis::Line )
3663  {
3664  double length = geom->length();
3665  if ( length >= 0.0 )
3666  {
3667  return ( length >= ( minSize * mapUnitsPerMM ) );
3668  }
3669  }
3670  else if ( featureType == QGis::Polygon )
3671  {
3672  double area = geom->area();
3673  if ( area >= 0.0 )
3674  {
3675  return ( sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
3676  }
3677  }
3678  return true; //should never be reached. Return true in this case to label such geometries anyway.
3679 }
3680 
3681 void QgsPalLabeling::registerDiagramFeature( const QString& layerID, QgsFeature& feat, const QgsRenderContext& context )
3682 {
3683  //get diagram layer settings, diagram renderer
3685  if ( layerIt == mActiveDiagramLayers.constEnd() )
3686  {
3687  return;
3688  }
3689 
3690  QgsDiagramRendererV2* dr = layerIt.value().renderer;
3691  if ( dr )
3692  {
3693  QList<QgsDiagramSettings> settingList = dr->diagramSettings();
3694  if ( settingList.size() > 0 )
3695  {
3696  double minScale = settingList.at( 0 ).minScaleDenominator;
3697  if ( minScale > 0 && context.rendererScale() < minScale )
3698  {
3699  return;
3700  }
3701 
3702  double maxScale = settingList.at( 0 ).maxScaleDenominator;
3703  if ( maxScale > 0 && context.rendererScale() > maxScale )
3704  {
3705  return;
3706  }
3707  }
3708  }
3709 
3710  //convert geom to geos
3711  const QgsGeometry* geom = feat.constGeometry();
3713  if ( !qgsDoubleNear( mMapSettings->rotation(), 0.0 ) )
3714  {
3715  //PAL features are prerotated, so extent also needs to be unrotated
3716  extentGeom->rotate( -mMapSettings->rotation(), mMapSettings->visibleExtent().center() );
3717  }
3718 
3719  const GEOSGeometry* geos_geom = 0;
3720  QScopedPointer<QgsGeometry> preparedGeom;
3721  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, layerIt.value().ct, extentGeom.data() ) )
3722  {
3723  preparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, layerIt.value().ct, extentGeom.data() ) );
3724  if ( !preparedGeom.data() )
3725  return;
3726  geos_geom = preparedGeom.data()->asGeos();
3727  }
3728  else
3729  {
3730  geos_geom = geom->asGeos();
3731  }
3732 
3733  if ( geos_geom == 0 )
3734  {
3735  return; // invalid geometry
3736  }
3737 
3738  //create PALGeometry with diagram = true
3739  QgsPalGeometry* lbl = new QgsPalGeometry( feat.id(), "", GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom ) );
3740  lbl->setIsDiagram( true );
3741 
3742  // record the created geometry - it will be deleted at the end.
3743  layerIt.value().geometries.append( lbl );
3744 
3745  double diagramWidth = 0;
3746  double diagramHeight = 0;
3747  if ( dr )
3748  {
3749  QSizeF diagSize = dr->sizeMapUnits( feat, context );
3750  if ( diagSize.isValid() )
3751  {
3752  diagramWidth = diagSize.width();
3753  diagramHeight = diagSize.height();
3754  }
3755 
3756  //append the diagram attributes to lbl
3757  lbl->setDiagramAttributes( feat.attributes() );
3758  }
3759 
3760  // feature to the layer
3761  bool alwaysShow = layerIt.value().showAll;
3762  int ddColX = layerIt.value().xPosColumn;
3763  int ddColY = layerIt.value().yPosColumn;
3764  double ddPosX = 0.0;
3765  double ddPosY = 0.0;
3766  bool ddPos = ( ddColX >= 0 && ddColY >= 0 );
3767  if ( ddPos )
3768  {
3769  bool posXOk, posYOk;
3770  ddPosX = feat.attribute( ddColX ).toDouble( &posXOk );
3771  ddPosY = feat.attribute( ddColY ).toDouble( &posYOk );
3772  if ( !posXOk || !posYOk )
3773  {
3774  ddPos = false;
3775  }
3776  else
3777  {
3778  const QgsCoordinateTransform* ct = layerIt.value().ct;
3779  if ( ct )
3780  {
3781  double z = 0;
3782  ct->transformInPlace( ddPosX, ddPosY, z );
3783  }
3784  //data defined diagram position is always centered
3785  ddPosX -= diagramWidth / 2.0;
3786  ddPosY -= diagramHeight / 2.0;
3787  }
3788  }
3789 
3790  try
3791  {
3792  if ( !layerIt.value().palLayer->registerFeature( lbl->strId(), lbl, diagramWidth, diagramHeight, "", ddPosX, ddPosY, ddPos, 0.0, true, 0, 0, 0, 0, alwaysShow ) )
3793  {
3794  return;
3795  }
3796  }
3797  catch ( std::exception &e )
3798  {
3799  Q_UNUSED( e );
3800  QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( feat.id() ) + QString::fromLatin1( e.what() ), 4 );
3801  return;
3802  }
3803 
3804  pal::Feature* palFeat = layerIt.value().palLayer->getFeature( lbl->strId() );
3805  QgsPoint ptZero = layerIt.value().xform->toMapCoordinates( 0, 0 );
3806  QgsPoint ptOne = layerIt.value().xform->toMapCoordinates( 1, 0 );
3807  palFeat->setDistLabel( qAbs( ptOne.x() - ptZero.x() ) * layerIt.value().dist );
3808 }
3809 
3810 
3812 {
3813  init( mr->mapSettings() );
3814 }
3815 
3816 void QgsPalLabeling::init( const QgsMapSettings& mapSettings )
3817 {
3818  mMapSettings = &mapSettings;
3819 
3820  // delete if exists already
3821  if ( mPal )
3822  delete mPal;
3823 
3824  mPal = new Pal;
3825 
3826  SearchMethod s;
3827  switch ( mSearch )
3828  {
3829  default:
3830  case Chain: s = CHAIN; break;
3831  case Popmusic_Tabu: s = POPMUSIC_TABU; break;
3832  case Popmusic_Chain: s = POPMUSIC_CHAIN; break;
3833  case Popmusic_Tabu_Chain: s = POPMUSIC_TABU_CHAIN; break;
3834  case Falp: s = FALP; break;
3835  }
3836  mPal->setSearch( s );
3837 
3838  // set number of candidates generated per feature
3840  mPal->setLineP( mCandLine );
3842 
3844 
3845  clearActiveLayers(); // free any previous QgsDataDefined objects
3847 }
3848 
3850 {
3851  delete mPal;
3852  mPal = NULL;
3853  mMapSettings = NULL;
3854 }
3855 
3857 {
3859  for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
3860  {
3861  if ( lit.key() == layerName )
3862  {
3863  return lit.value();
3864  }
3865  }
3866  return mInvalidLayerSettings;
3867 }
3868 
3871 {
3872  //font color
3873  if ( ddValues.contains( QgsPalLayerSettings::Color ) )
3874  {
3875  QVariant ddColor = ddValues.value( QgsPalLayerSettings::Color );
3876  tmpLyr.textColor = ddColor.value<QColor>();
3877  }
3878 
3879  //font transparency
3880  if ( ddValues.contains( QgsPalLayerSettings::FontTransp ) )
3881  {
3882  tmpLyr.textTransp = ddValues.value( QgsPalLayerSettings::FontTransp ).toInt();
3883  }
3884 
3885  tmpLyr.textColor.setAlphaF(( 100.0 - ( double )( tmpLyr.textTransp ) ) / 100.0 );
3886 
3887  //font blend mode
3888  if ( ddValues.contains( QgsPalLayerSettings::FontBlendMode ) )
3889  {
3890  tmpLyr.blendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::FontBlendMode ).toInt();
3891  }
3892 }
3893 
3896 {
3898  {
3899  tmpLyr.wrapChar = ddValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
3900  }
3901 
3902  if ( !tmpLyr.wrapChar.isEmpty() || tmpLyr.getLabelExpression()->expression().contains( "wordwrap" ) )
3903  {
3904 
3906  {
3907  tmpLyr.multilineHeight = ddValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
3908  }
3909 
3911  {
3913  }
3914 
3915  }
3916 
3917  if ( ddValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
3918  {
3919  tmpLyr.addDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
3920  }
3921 
3922  if ( tmpLyr.addDirectionSymbol )
3923  {
3924 
3925  if ( ddValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
3926  {
3927  tmpLyr.leftDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
3928  }
3929  if ( ddValues.contains( QgsPalLayerSettings::DirSymbRight ) )
3930  {
3931  tmpLyr.rightDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
3932  }
3933 
3935  {
3937  }
3938 
3940  {
3942  }
3943 
3944  }
3945 }
3946 
3949 {
3950  //buffer draw
3951  if ( ddValues.contains( QgsPalLayerSettings::BufferDraw ) )
3952  {
3953  tmpLyr.bufferDraw = ddValues.value( QgsPalLayerSettings::BufferDraw ).toBool();
3954  }
3955 
3956  if ( !tmpLyr.bufferDraw )
3957  {
3958  // tmpLyr.bufferSize > 0.0 && tmpLyr.bufferTransp < 100 figured in during evaluation
3959  return; // don't continue looking for unused values
3960  }
3961 
3962  //buffer size
3963  if ( ddValues.contains( QgsPalLayerSettings::BufferSize ) )
3964  {
3965  tmpLyr.bufferSize = ddValues.value( QgsPalLayerSettings::BufferSize ).toDouble();
3966  }
3967 
3968  //buffer transparency
3969  if ( ddValues.contains( QgsPalLayerSettings::BufferTransp ) )
3970  {
3971  tmpLyr.bufferTransp = ddValues.value( QgsPalLayerSettings::BufferTransp ).toInt();
3972  }
3973 
3974  //buffer size units
3975  if ( ddValues.contains( QgsPalLayerSettings::BufferUnit ) )
3976  {
3978  tmpLyr.bufferSizeInMapUnits = ( bufunit == QgsPalLayerSettings::MapUnits );
3979  }
3980 
3981  //buffer color
3982  if ( ddValues.contains( QgsPalLayerSettings::BufferColor ) )
3983  {
3984  QVariant ddColor = ddValues.value( QgsPalLayerSettings::BufferColor );
3985  tmpLyr.bufferColor = ddColor.value<QColor>();
3986  }
3987 
3988  // apply any transparency
3989  tmpLyr.bufferColor.setAlphaF(( 100.0 - ( double )( tmpLyr.bufferTransp ) ) / 100.0 );
3990 
3991  //buffer pen join style
3993  {
3994  tmpLyr.bufferJoinStyle = ( Qt::PenJoinStyle )ddValues.value( QgsPalLayerSettings::BufferJoinStyle ).toInt();
3995  }
3996 
3997  //buffer blend mode
3999  {
4000  tmpLyr.bufferBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::BufferBlendMode ).toInt();
4001  }
4002 }
4003 
4006 {
4007  //shape draw
4008  if ( ddValues.contains( QgsPalLayerSettings::ShapeDraw ) )
4009  {
4010  tmpLyr.shapeDraw = ddValues.value( QgsPalLayerSettings::ShapeDraw ).toBool();
4011  }
4012 
4013  if ( !tmpLyr.shapeDraw )
4014  {
4015  return; // don't continue looking for unused values
4016  }
4017 
4018  if ( ddValues.contains( QgsPalLayerSettings::ShapeKind ) )
4019  {
4021  }
4022 
4023  if ( ddValues.contains( QgsPalLayerSettings::ShapeSVGFile ) )
4024  {
4025  tmpLyr.shapeSVGFile = ddValues.value( QgsPalLayerSettings::ShapeSVGFile ).toString();
4026  }
4027 
4028  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeType ) )
4029  {
4031  }
4032 
4033  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeX ) )
4034  {
4035  tmpLyr.shapeSize.setX( ddValues.value( QgsPalLayerSettings::ShapeSizeX ).toDouble() );
4036  }
4037  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeY ) )
4038  {
4039  tmpLyr.shapeSize.setY( ddValues.value( QgsPalLayerSettings::ShapeSizeY ).toDouble() );
4040  }
4041 
4043  {
4045  }
4046 
4048  {
4050  }
4051 
4052  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotation ) )
4053  {
4054  tmpLyr.shapeRotation = ddValues.value( QgsPalLayerSettings::ShapeRotation ).toDouble();
4055  }
4056 
4057  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffset ) )
4058  {
4059  tmpLyr.shapeOffset = ddValues.value( QgsPalLayerSettings::ShapeOffset ).toPointF();
4060  }
4061 
4063  {
4065  }
4066 
4067  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadii ) )
4068  {
4069  tmpLyr.shapeRadii = ddValues.value( QgsPalLayerSettings::ShapeRadii ).toPointF();
4070  }
4071 
4073  {
4075  }
4076 
4078  {
4079  tmpLyr.shapeTransparency = ddValues.value( QgsPalLayerSettings::ShapeTransparency ).toInt();
4080  }
4081 
4083  {
4084  tmpLyr.shapeBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::ShapeBlendMode ).toInt();
4085  }
4086 
4088  {
4089  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeFillColor );
4090  tmpLyr.shapeFillColor = ddColor.value<QColor>();
4091  }
4092 
4094  {
4096  tmpLyr.shapeBorderColor = ddColor.value<QColor>();
4097  }
4098 
4100  {
4101  tmpLyr.shapeBorderWidth = ddValues.value( QgsPalLayerSettings::ShapeBorderWidth ).toDouble();
4102  }
4103 
4105  {
4107  }
4108 
4110  {
4111  tmpLyr.shapeJoinStyle = ( Qt::PenJoinStyle )ddValues.value( QgsPalLayerSettings::ShapeJoinStyle ).toInt();
4112  }
4113 }
4114 
4117 {
4118  //shadow draw
4119  if ( ddValues.contains( QgsPalLayerSettings::ShadowDraw ) )
4120  {
4121  tmpLyr.shadowDraw = ddValues.value( QgsPalLayerSettings::ShadowDraw ).toBool();
4122  }
4123 
4124  if ( !tmpLyr.shadowDraw )
4125  {
4126  return; // don't continue looking for unused values
4127  }
4128 
4129  if ( ddValues.contains( QgsPalLayerSettings::ShadowUnder ) )
4130  {
4132  }
4133 
4135  {
4136  tmpLyr.shadowOffsetAngle = ddValues.value( QgsPalLayerSettings::ShadowOffsetAngle ).toInt();
4137  }
4138 
4140  {
4141  tmpLyr.shadowOffsetDist = ddValues.value( QgsPalLayerSettings::ShadowOffsetDist ).toDouble();
4142  }
4143 
4145  {
4147  }
4148 
4149  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadius ) )
4150  {
4151  tmpLyr.shadowRadius = ddValues.value( QgsPalLayerSettings::ShadowRadius ).toDouble();
4152  }
4153 
4155  {
4157  }
4158 
4160  {
4162  }
4163 
4164  if ( ddValues.contains( QgsPalLayerSettings::ShadowScale ) )
4165  {
4166  tmpLyr.shadowScale = ddValues.value( QgsPalLayerSettings::ShadowScale ).toInt();
4167  }
4168 
4169  if ( ddValues.contains( QgsPalLayerSettings::ShadowColor ) )
4170  {
4171  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShadowColor );
4172  tmpLyr.shadowColor = ddColor.value<QColor>();
4173  }
4174 
4176  {
4177  tmpLyr.shadowBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::ShadowBlendMode ).toInt();
4178  }
4179 }
4180 
4181 
4182 // helper function for checking for job cancellation within PAL
4183 static bool _palIsCancelled( void* ctx )
4184 {
4185  return (( QgsRenderContext* ) ctx )->renderingStopped();
4186 }
4187 
4189 {
4190  Q_ASSERT( mMapSettings != NULL );
4191  QPainter* painter = context.painter();
4192 
4194  if ( !qgsDoubleNear( mMapSettings->rotation(), 0.0 ) )
4195  {
4196  //PAL features are prerotated, so extent also needs to be unrotated
4197  extentGeom->rotate( -mMapSettings->rotation(), mMapSettings->visibleExtent().center() );
4198  }
4199 
4200  QgsRectangle extent = extentGeom->boundingBox();
4201  delete extentGeom;
4202 
4204 
4205  delete mResults;
4207 
4208  QTime t;
4209  t.start();
4210 
4211  // do the labeling itself
4212  double bbox[] = { extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() };
4213 
4214  std::list<LabelPosition*>* labels;
4215  pal::Problem *problem;
4216  try
4217  {
4218  problem = mPal->extractProblem( bbox );
4219  }
4220  catch ( std::exception& e )
4221  {
4222  Q_UNUSED( e );
4223  QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
4224  //mActiveLayers.clear(); // clean up
4225  return;
4226  }
4227 
4228  if ( context.renderingStopped() )
4229  {
4230  delete problem;
4232  return; // it has been cancelled
4233  }
4234 
4235 #if 1 // XXX strk
4236  // features are pre-rotated but not scaled/translated,
4237  // so we only disable rotation here. Ideally, they'd be
4238  // also pre-scaled/translated, as suggested here:
4239  // http://hub.qgis.org/issues/11856
4241  xform.setMapRotation( 0, 0, 0 );
4242 #else
4243  const QgsMapToPixel& xform = mMapSettings->mapToPixel();
4244 #endif
4245 
4246  // draw rectangles with all candidates
4247  // this is done before actual solution of the problem
4248  // before number of candidates gets reduced
4249  mCandidates.clear();
4250  if ( mShowingCandidates && problem )
4251  {
4252  painter->setBrush( Qt::NoBrush );
4253  for ( int i = 0; i < problem->getNumFeatures(); i++ )
4254  {
4255  for ( int j = 0; j < problem->getFeatureCandidateCount( i ); j++ )
4256  {
4257  pal::LabelPosition* lp = problem->getFeatureCandidate( i, j );
4258 
4259  drawLabelCandidateRect( lp, painter, &xform );
4260  }
4261  }
4262  }
4263 
4264  // find the solution
4265  labels = mPal->solveProblem( problem, mShowingAllLabels );
4266 
4267  QgsDebugMsgLevel( QString( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( labels->size() ), 4 );
4268  t.restart();
4269 
4270  if ( context.renderingStopped() )
4271  {
4272  delete problem;
4273  delete labels;
4275  return;
4276  }
4277 
4278  painter->setRenderHint( QPainter::Antialiasing );
4279 
4280  // draw the labels
4281  std::list<LabelPosition*>::iterator it = labels->begin();
4282  for ( ; it != labels->end(); ++it )
4283  {
4284  if ( context.renderingStopped() )
4285  break;
4286 
4287  QgsPalGeometry* palGeometry = dynamic_cast< QgsPalGeometry* >(( *it )->getFeaturePart()->getUserGeometry() );
4288  if ( !palGeometry )
4289  {
4290  continue;
4291  }
4292 
4293  //layer names
4294  QString layerName = ( *it )->getLayerName();
4295  if ( palGeometry->isDiagram() )
4296  {
4297  QgsFeature feature;
4298  //render diagram
4300  for ( dit = mActiveDiagramLayers.begin(); dit != mActiveDiagramLayers.end(); ++dit )
4301  {
4302  if ( QString( dit.key() + "d" ) == layerName )
4303  {
4304  feature.setFields( dit.value().fields );
4305  palGeometry->feature( feature );
4306 
4307  //calculate top-left point for diagram
4308  //first, calculate the centroid of the label (accounts for PAL creating
4309  //rotated labels when we do not want to draw the diagrams rotated)
4310  double centerX = 0;
4311  double centerY = 0;
4312  for ( int i = 0; i < 4; ++i )
4313  {
4314  centerX += ( *it )->getX( i );
4315  centerY += ( *it )->getY( i );
4316  }
4317  QgsPoint outPt( centerX / 4.0, centerY / 4.0 );
4318  //then, calculate the top left point for the diagram with this center position
4319  QgsPoint centerPt = xform.transform( outPt.x() - ( *it )->getWidth() / 2,
4320  outPt.y() - ( *it )->getHeight() / 2 );
4321 
4322  dit.value().renderer->renderDiagram( feature, context, centerPt.toQPointF() );
4323  }
4324  }
4325 
4326  //insert into label search tree to manipulate position interactively
4327  if ( mResults->mLabelSearchTree )
4328  {
4329  //for diagrams, remove the additional 'd' at the end of the layer id
4330  QString layerId = layerName;
4331  layerId.chop( 1 );
4332  mResults->mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), layerId, QString( "" ), QFont(), true, false );
4333  }
4334  continue;
4335  }
4336 
4337  const QgsPalLayerSettings& lyr = layer( layerName );
4338  if ( !lyr.drawLabels )
4339  continue;
4340 
4341  // Copy to temp, editable layer settings
4342  // these settings will be changed by any data defined values, then used for rendering label components
4343  // settings may be adjusted during rendering of components
4344  QgsPalLayerSettings tmpLyr( lyr );
4345 
4346  // apply any previously applied data defined settings for the label
4348 
4349  //font
4350  QFont dFont = palGeometry->definedFont();
4351  QgsDebugMsgLevel( QString( "PAL font tmpLyr: %1, Style: %2" ).arg( tmpLyr.textFont.toString() ).arg( tmpLyr.textFont.styleName() ), 4 );
4352  QgsDebugMsgLevel( QString( "PAL font definedFont: %1, Style: %2" ).arg( dFont.toString() ).arg( dFont.styleName() ), 4 );
4353  tmpLyr.textFont = dFont;
4354 
4356  {
4357  //calculate font alignment based on label quadrant
4358  switch (( *it )->getQuadrant() )
4359  {
4360  case LabelPosition::QuadrantAboveLeft:
4361  case LabelPosition::QuadrantLeft:
4362  case LabelPosition::QuadrantBelowLeft:
4364  break;
4365  case LabelPosition::QuadrantAbove:
4366  case LabelPosition::QuadrantOver:
4367  case LabelPosition::QuadrantBelow:
4369  break;
4370  case LabelPosition::QuadrantAboveRight:
4371  case LabelPosition::QuadrantRight:
4372  case LabelPosition::QuadrantBelowRight:
4374  break;
4375  }
4376  }
4377 
4378  // update tmpLyr with any data defined text style values
4379  dataDefinedTextStyle( tmpLyr, ddValues );
4380 
4381  // update tmpLyr with any data defined text buffer values
4382  dataDefinedTextBuffer( tmpLyr, ddValues );
4383 
4384  // update tmpLyr with any data defined text formatting values
4385  dataDefinedTextFormatting( tmpLyr, ddValues );
4386 
4387  // update tmpLyr with any data defined shape background values
4388  dataDefinedShapeBackground( tmpLyr, ddValues );
4389 
4390  // update tmpLyr with any data defined drop shadow values
4391  dataDefinedDropShadow( tmpLyr, ddValues );
4392 
4393 
4395 
4396  // Render the components of a label in reverse order
4397  // (backgrounds -> text)
4398 
4399  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowLowest )
4400  {
4401  if ( tmpLyr.shapeDraw )
4402  {
4404  }
4405  else if ( tmpLyr.bufferDraw )
4406  {
4408  }
4409  else
4410  {
4412  }
4413  }
4414 
4415  if ( tmpLyr.shapeDraw )
4416  {
4417  drawLabel( *it, context, tmpLyr, LabelShape );
4418  }
4419 
4420  if ( tmpLyr.bufferDraw )
4421  {
4422  drawLabel( *it, context, tmpLyr, LabelBuffer );
4423  }
4424 
4425  drawLabel( *it, context, tmpLyr, LabelText );
4426 
4427  if ( mResults->mLabelSearchTree )
4428  {
4429