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