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