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