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