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  // CLIP the geometry if it is bigger than the extent
1784  // don't clip if centroid is requested for whole feature
1785  bool do_clip = false;
1786  if ( !centroidPoly || ( centroidPoly && !wholeCentroid ) )
1787  {
1788  do_clip = !extentGeom->contains( geom );
1789  if ( do_clip )
1790  {
1791  QgsGeometry* clipGeom = geom->intersection( extentGeom ); // creates new geometry
1792  if ( !clipGeom )
1793  {
1794  return;
1795  }
1796  geom = clipGeom;
1797  clonedGeometry.reset( geom );
1798  }
1799  }
1800 
1801  const GEOSGeometry* geos_geom = geom->asGeos();
1802 
1803  if ( geos_geom == NULL )
1804  return; // invalid geometry
1805 
1806  // likelihood exists label will be registered with PAL and may be drawn
1807  // check if max number of features to label (already registered with PAL) has been reached
1808  // Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
1809  if ( limitNumLabels )
1810  {
1811  if ( !maxNumLabels )
1812  {
1813  return;
1814  }
1816  if ( mFeatsRegPal >= maxNumLabels )
1817  {
1818  return;
1819  }
1820 
1821  int divNum = ( int )(( mFeaturesToLabel / maxNumLabels ) + 0.5 );
1822  if ( divNum && ( mFeatsRegPal == ( int )( mFeatsSendingToPal / divNum ) ) )
1823  {
1824  mFeatsSendingToPal += 1;
1825  if ( divNum && mFeatsSendingToPal % divNum )
1826  {
1827  return;
1828  }
1829  }
1830  }
1831 
1832  GEOSGeometry* geos_geom_clone;
1833  if ( GEOSGeomTypeId_r( QgsGeometry::getGEOSHandler(), geos_geom ) == GEOS_POLYGON && repeatDistance > 0 && placement == Line )
1834  {
1835  geos_geom_clone = GEOSBoundary_r( QgsGeometry::getGEOSHandler(), geos_geom );
1836  }
1837  else
1838  {
1839  geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
1840  }
1841 
1842  //data defined position / alignment / rotation?
1843  bool dataDefinedPosition = false;
1844  bool labelIsPinned = false;
1845  bool layerDefinedRotation = false;
1846  bool dataDefinedRotation = false;
1847  double xPos = 0.0, yPos = 0.0, angle = 0.0;
1848  bool ddXPos = false, ddYPos = false;
1849  double quadOffsetX = 0.0, quadOffsetY = 0.0;
1850  double offsetX = 0.0, offsetY = 0.0;
1851 
1852  //data defined quadrant offset?
1853  QuadrantPosition quadOff = quadOffset;
1855  {
1856  bool ok;
1857  int quadInt = exprVal.toInt( &ok );
1858  QgsDebugMsgLevel( QString( "exprVal OffsetQuad:%1" ).arg( quadInt ), 4 );
1859  if ( ok && 0 <= quadInt && quadInt <= 8 )
1860  {
1861  quadOff = ( QuadrantPosition )quadInt;
1862  }
1863  }
1864 
1865  // adjust quadrant offset of labels
1866  switch ( quadOff )
1867  {
1868  case QuadrantAboveLeft:
1869  quadOffsetX = -1.0;
1870  quadOffsetY = 1.0;
1871  break;
1872  case QuadrantAbove:
1873  quadOffsetX = 0.0;
1874  quadOffsetY = 1.0;
1875  break;
1876  case QuadrantAboveRight:
1877  quadOffsetX = 1.0;
1878  quadOffsetY = 1.0;
1879  break;
1880  case QuadrantLeft:
1881  quadOffsetX = -1.0;
1882  quadOffsetY = 0.0;
1883  break;
1884  case QuadrantRight:
1885  quadOffsetX = 1.0;
1886  quadOffsetY = 0.0;
1887  break;
1888  case QuadrantBelowLeft:
1889  quadOffsetX = -1.0;
1890  quadOffsetY = -1.0;
1891  break;
1892  case QuadrantBelow:
1893  quadOffsetX = 0.0;
1894  quadOffsetY = -1.0;
1895  break;
1896  case QuadrantBelowRight:
1897  quadOffsetX = 1.0;
1898  quadOffsetY = -1.0;
1899  break;
1900  case QuadrantOver:
1901  default:
1902  break;
1903  }
1904 
1905  //data defined label offset?
1906  double xOff = xOffset;
1907  double yOff = yOffset;
1909  {
1910  QString ptstr = exprVal.toString().trimmed();
1911  QgsDebugMsgLevel( QString( "exprVal OffsetXY:%1" ).arg( ptstr ), 4 );
1912 
1913  if ( !ptstr.isEmpty() )
1914  {
1915  QPointF ddOffPt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
1916  xOff = ddOffPt.x();
1917  yOff = ddOffPt.y();
1918  }
1919  }
1920 
1921  // data defined label offset units?
1922  bool offinmapunits = labelOffsetInMapUnits;
1924  {
1925  QString units = exprVal.toString().trimmed();
1926  QgsDebugMsgLevel( QString( "exprVal OffsetUnits:%1" ).arg( units ), 4 );
1927  if ( !units.isEmpty() )
1928  {
1929  offinmapunits = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
1930  }
1931  }
1932 
1933  // adjust offset of labels to match chosen unit and map scale
1934  // offsets match those of symbology: -x = left, -y = up
1935  double mapUntsPerMM = labelOffsetMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor();
1936  if ( xOff != 0 )
1937  {
1938  offsetX = xOff; // must be positive to match symbology offset direction
1939  if ( !offinmapunits )
1940  {
1941  offsetX *= mapUntsPerMM; //convert offset from mm to map units
1942  }
1943  }
1944  if ( yOff != 0 )
1945  {
1946  offsetY = -yOff; // must be negative to match symbology offset direction
1947  if ( !offinmapunits )
1948  {
1949  offsetY *= mapUntsPerMM; //convert offset from mm to map units
1950  }
1951  }
1952 
1953  // layer defined rotation?
1954  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
1955  if ( placement == QgsPalLayerSettings::OverPoint && angleOffset != 0 )
1956  {
1957  layerDefinedRotation = true;
1958  angle = angleOffset * M_PI / 180; // convert to radians
1959  }
1960 
1961  //data defined rotation?
1963  {
1964  bool ok;
1965  double rotD = exprVal.toDouble( &ok );
1966  QgsDebugMsgLevel( QString( "exprVal Rotation:%1" ).arg( rotD ), 4 );
1967  if ( ok )
1968  {
1969  dataDefinedRotation = true;
1970  angle = rotD * M_PI / 180.0;
1971  }
1972  }
1973 
1975  {
1976  if ( !exprVal.isNull() )
1977  xPos = exprVal.toDouble( &ddXPos );
1978  QgsDebugMsgLevel( QString( "exprVal PositionX:%1" ).arg( xPos ), 4 );
1979 
1981  {
1982  //data defined position. But field values could be NULL -> positions will be generated by PAL
1983  if ( !exprVal.isNull() )
1984  yPos = exprVal.toDouble( &ddYPos );
1985  QgsDebugMsgLevel( QString( "exprVal PositionY:%1" ).arg( yPos ), 4 );
1986 
1987  if ( ddXPos && ddYPos )
1988  {
1989  dataDefinedPosition = true;
1990  labelIsPinned = true;
1991  // layer rotation set, but don't rotate pinned labels unless data defined
1992  if ( layerDefinedRotation && !dataDefinedRotation )
1993  {
1994  angle = 0.0;
1995  }
1996 
1997  //x/y shift in case of alignment
1998  double xdiff = 0.0;
1999  double ydiff = 0.0;
2000 
2001  //horizontal alignment
2003  {
2004  QString haliString = exprVal.toString();
2005  QgsDebugMsgLevel( QString( "exprVal Hali:%1" ).arg( haliString ), 4 );
2006  if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
2007  {
2008  xdiff -= labelX / 2.0;
2009  }
2010  else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
2011  {
2012  xdiff -= labelX;
2013  }
2014  }
2015 
2016  //vertical alignment
2018  {
2019  QString valiString = exprVal.toString();
2020  QgsDebugMsgLevel( QString( "exprVal Vali:%1" ).arg( valiString ), 4 );
2021 
2022  if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
2023  {
2024  if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0 )
2025  {
2026  ydiff -= labelY;
2027  }
2028  else
2029  {
2030  double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
2031  if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
2032  {
2033  ydiff -= labelY * descentRatio;
2034  }
2035  else //'Cap' or 'Half'
2036  {
2037  double capHeightRatio = ( labelFontMetrics->boundingRect( 'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
2038  ydiff -= labelY * capHeightRatio;
2039  if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
2040  {
2041  ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
2042  }
2043  }
2044  }
2045  }
2046  }
2047 
2048  if ( dataDefinedRotation )
2049  {
2050  //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
2051  double xd = xdiff * cos( angle ) - ydiff * sin( angle );
2052  double yd = xdiff * sin( angle ) + ydiff * cos( angle );
2053  xdiff = xd;
2054  ydiff = yd;
2055  }
2056 
2057  //project xPos and yPos from layer to map CRS
2058  double z = 0;
2059  if ( ct )
2060  {
2061  try
2062  {
2063  ct->transformInPlace( xPos, yPos, z );
2064  }
2065  catch ( QgsCsException &e )
2066  {
2067  Q_UNUSED( e );
2068  QgsDebugMsgLevel( QString( "Ignoring feature %1 due transformation exception on data-defined position" ).arg( f.id() ), 4 );
2069  return;
2070  }
2071  }
2072 
2073  xPos += xdiff;
2074  yPos += ydiff;
2075  }
2076  else
2077  {
2078  // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
2079  if ( dataDefinedRotation && placement != QgsPalLayerSettings::OverPoint )
2080  {
2081  angle = 0.0;
2082  }
2083  }
2084  }
2085  }
2086 
2087  // data defined always show?
2088  bool alwaysShow = false;
2090  {
2091  alwaysShow = exprVal.toBool();
2092  }
2093 
2094  QgsPalGeometry* lbl = new QgsPalGeometry(
2095  f.id(),
2096  labelText,
2097  geos_geom_clone,
2098  labelFont.letterSpacing(),
2099  labelFont.wordSpacing(),
2100  placement == QgsPalLayerSettings::Curved );
2101 
2102  lbl->setDxfLayer( dxfLayer );
2103 
2104  // record the created geometry - it will be deleted at the end.
2105  geometries.append( lbl );
2106 
2107  // store the label's calculated font for later use during painting
2108 #if QT_VERSION >= 0x040800
2109  QgsDebugMsgLevel( QString( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString() ).arg( labelFont.styleName() ), 4 );
2110 #endif
2111  lbl->setDefinedFont( labelFont );
2112 
2113  // set repeat distance
2114  // data defined repeat distance?
2115  double repeatDist = repeatDistance;
2117  {
2118  bool ok;
2119  double distD = exprVal.toDouble( &ok );
2120  if ( ok )
2121  {
2122  repeatDist = distD;
2123  }
2124  }
2125 
2126  // data defined label-repeat distance units?
2127  bool repeatdistinmapunit = repeatDistanceUnit == QgsPalLayerSettings::MapUnits;
2129  {
2130  QString units = exprVal.toString().trimmed();
2131  QgsDebugMsgLevel( QString( "exprVal RepeatDistanceUnits:%1" ).arg( units ), 4 );
2132  if ( !units.isEmpty() )
2133  {
2134  repeatdistinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2135  }
2136  }
2137 
2138  if ( repeatDist != 0 )
2139  {
2140  if ( repeatdistinmapunit ) //convert distance from mm/map units to pixels
2141  {
2142  repeatDist /= repeatDistanceMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor();
2143  }
2144  else //mm
2145  {
2146  repeatDist *= vectorScaleFactor;
2147  }
2148  repeatDist *= qAbs( ptOne.x() - ptZero.x() );
2149  }
2150 
2151  // feature to the layer
2152  try
2153  {
2154  if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(),
2155  xPos, yPos, dataDefinedPosition, angle, dataDefinedRotation,
2156  quadOffsetX, quadOffsetY, offsetX, offsetY, alwaysShow, repeatDist ) )
2157  return;
2158  }
2159  catch ( std::exception &e )
2160  {
2161  Q_UNUSED( e );
2162  QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( f.id() ) + QString::fromLatin1( e.what() ), 4 );
2163  return;
2164  }
2165 
2166  // TODO: only for placement which needs character info
2167  pal::Feature* feat = palLayer->getFeature( lbl->strId() );
2168  // account for any data defined font metrics adjustments
2169  feat->setLabelInfo( lbl->info( labelFontMetrics, xform, rasterCompressFactor, maxcharanglein, maxcharangleout ) );
2170  delete labelFontMetrics;
2171 
2172  // TODO: allow layer-wide feature dist in PAL...?
2173 
2174  // data defined label-feature distance?
2175  double distance = dist;
2177  {
2178  bool ok;
2179  double distD = exprVal.toDouble( &ok );
2180  if ( ok )
2181  {
2182  distance = distD;
2183  }
2184  }
2185 
2186  // data defined label-feature distance units?
2187  bool distinmapunit = distInMapUnits;
2189  {
2190  QString units = exprVal.toString().trimmed();
2191  QgsDebugMsgLevel( QString( "exprVal DistanceUnits:%1" ).arg( units ), 4 );
2192  if ( !units.isEmpty() )
2193  {
2194  distinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2195  }
2196  }
2197 
2198  if ( distance != 0 )
2199  {
2200  if ( distinmapunit ) //convert distance from mm/map units to pixels
2201  {
2202  distance /= distMapUnitScale.computeMapUnitsPerPixel( context );
2203  }
2204  else //mm
2205  {
2206  distance *= vectorScaleFactor;
2207  }
2208  feat->setDistLabel( qAbs( ptOne.x() - ptZero.x() )* distance );
2209  }
2210 
2211 
2212  //add parameters for data defined labeling to QgsPalGeometry
2213  QMap< DataDefinedProperties, QVariant >::const_iterator dIt = dataDefinedValues.constBegin();
2214  for ( ; dIt != dataDefinedValues.constEnd(); ++dIt )
2215  {
2216  lbl->addDataDefinedValue( dIt.key(), dIt.value() );
2217  }
2218 
2219  // set geometry's pinned property
2220  lbl->setIsPinned( labelIsPinned );
2221 }
2222 
2223 bool QgsPalLayerSettings::dataDefinedValEval( const QString& valType,
2225  QVariant& exprVal )
2226 {
2227  if ( dataDefinedEvaluate( p, exprVal ) )
2228  {
2229  QString dbgStr = QString( "exprVal %1:" ).arg( mDataDefinedNames.value( p ).first ) + "%1";
2230 
2231  if ( valType == QString( "bool" ) )
2232  {
2233  bool bol = exprVal.toBool();
2234  QgsDebugMsgLevel( dbgStr.arg( bol ? "true" : "false" ), 4 );
2235  dataDefinedValues.insert( p, QVariant( bol ) );
2236  return true;
2237  }
2238  if ( valType == QString( "int" ) )
2239  {
2240  bool ok;
2241  int size = exprVal.toInt( &ok );
2242  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2243 
2244  if ( ok )
2245  {
2246  dataDefinedValues.insert( p, QVariant( size ) );
2247  return true;
2248  }
2249  }
2250  if ( valType == QString( "intpos" ) )
2251  {
2252  bool ok;
2253  int size = exprVal.toInt( &ok );
2254  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2255 
2256  if ( ok && size > 0 )
2257  {
2258  dataDefinedValues.insert( p, QVariant( size ) );
2259  return true;
2260  }
2261  }
2262  if ( valType == QString( "double" ) )
2263  {
2264  bool ok;
2265  double size = exprVal.toDouble( &ok );
2266  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2267 
2268  if ( ok )
2269  {
2270  dataDefinedValues.insert( p, QVariant( size ) );
2271  return true;
2272  }
2273  }
2274  if ( valType == QString( "doublepos" ) )
2275  {
2276  bool ok;
2277  double size = exprVal.toDouble( &ok );
2278  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2279 
2280  if ( ok && size > 0.0 )
2281  {
2282  dataDefinedValues.insert( p, QVariant( size ) );
2283  return true;
2284  }
2285  }
2286  if ( valType == QString( "rotation180" ) )
2287  {
2288  bool ok;
2289  double rot = exprVal.toDouble( &ok );
2290  QgsDebugMsgLevel( dbgStr.arg( rot ), 4 );
2291  if ( ok )
2292  {
2293  if ( rot < -180.0 && rot >= -360 )
2294  {
2295  rot += 360;
2296  }
2297  if ( rot > 180.0 && rot <= 360 )
2298  {
2299  rot -= 360;
2300  }
2301  if ( rot >= -180 && rot <= 180 )
2302  {
2303  dataDefinedValues.insert( p, QVariant( rot ) );
2304  return true;
2305  }
2306  }
2307  }
2308  if ( valType == QString( "transp" ) )
2309  {
2310  bool ok;
2311  int size = exprVal.toInt( &ok );
2312  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2313  if ( ok && size >= 0 && size <= 100 )
2314  {
2315  dataDefinedValues.insert( p, QVariant( size ) );
2316  return true;
2317  }
2318  }
2319  if ( valType == QString( "string" ) )
2320  {
2321  QString str = exprVal.toString(); // don't trim whitespace
2322  QgsDebugMsgLevel( dbgStr.arg( str ), 4 );
2323 
2324  dataDefinedValues.insert( p, QVariant( str ) ); // let it stay empty if it is
2325  return true;
2326  }
2327  if ( valType == QString( "units" ) )
2328  {
2329  QString unitstr = exprVal.toString().trimmed();
2330  QgsDebugMsgLevel( dbgStr.arg( unitstr ), 4 );
2331 
2332  if ( !unitstr.isEmpty() )
2333  {
2334  dataDefinedValues.insert( p, QVariant(( int )_decodeUnits( unitstr ) ) );
2335  return true;
2336  }
2337  }
2338  if ( valType == QString( "color" ) )
2339  {
2340  QString colorstr = exprVal.toString().trimmed();
2341  QgsDebugMsgLevel( dbgStr.arg( colorstr ), 4 );
2342  QColor color = QgsSymbolLayerV2Utils::decodeColor( colorstr );
2343 
2344  if ( color.isValid() )
2345  {
2346  dataDefinedValues.insert( p, QVariant( color ) );
2347  return true;
2348  }
2349  }
2350  if ( valType == QString( "joinstyle" ) )
2351  {
2352  QString joinstr = exprVal.toString().trimmed();
2353  QgsDebugMsgLevel( dbgStr.arg( joinstr ), 4 );
2354 
2355  if ( !joinstr.isEmpty() )
2356  {
2357  dataDefinedValues.insert( p, QVariant(( int )_decodePenJoinStyle( joinstr ) ) );
2358  return true;
2359  }
2360  }
2361  if ( valType == QString( "blendmode" ) )
2362  {
2363  QString blendstr = exprVal.toString().trimmed();
2364  QgsDebugMsgLevel( dbgStr.arg( blendstr ), 4 );
2365 
2366  if ( !blendstr.isEmpty() )
2367  {
2368  dataDefinedValues.insert( p, QVariant(( int )QgsSymbolLayerV2Utils::decodeBlendMode( blendstr ) ) );
2369  return true;
2370  }
2371  }
2372  if ( valType == QString( "pointf" ) )
2373  {
2374  QString ptstr = exprVal.toString().trimmed();
2375  QgsDebugMsgLevel( dbgStr.arg( ptstr ), 4 );
2376 
2377  if ( !ptstr.isEmpty() )
2378  {
2379  dataDefinedValues.insert( p, QVariant( QgsSymbolLayerV2Utils::decodePoint( ptstr ) ) );
2380  return true;
2381  }
2382  }
2383  }
2384  return false;
2385 }
2386 
2387 void QgsPalLayerSettings::parseTextStyle( QFont& labelFont,
2389  const QgsRenderContext& context )
2390 {
2391  // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
2392 
2393  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2394 
2395  // Two ways to generate new data defined font:
2396  // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
2397  // 2) Family + named style (bold or italic is ignored)
2398 
2399  // data defined font family?
2400  QString ddFontFamily( "" );
2402  {
2403  QString family = exprVal.toString().trimmed();
2404  QgsDebugMsgLevel( QString( "exprVal Font family:%1" ).arg( family ), 4 );
2405 
2406  if ( labelFont.family() != family )
2407  {
2408  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
2409  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
2410  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
2411  {
2412  ddFontFamily = family;
2413  }
2414  }
2415  }
2416 
2417  // data defined named font style?
2418  QString ddFontStyle( "" );
2420  {
2421  QString fontstyle = exprVal.toString().trimmed();
2422  QgsDebugMsgLevel( QString( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
2423  ddFontStyle = fontstyle;
2424  }
2425 
2426  // data defined bold font style?
2427  bool ddBold = false;
2429  {
2430  bool bold = exprVal.toBool();
2431  QgsDebugMsgLevel( QString( "exprVal Font bold:%1" ).arg( bold ? "true" : "false" ), 4 );
2432  ddBold = bold;
2433  }
2434 
2435  // data defined italic font style?
2436  bool ddItalic = false;
2438  {
2439  bool italic = exprVal.toBool();
2440  QgsDebugMsgLevel( QString( "exprVal Font italic:%1" ).arg( italic ? "true" : "false" ), 4 );
2441  ddItalic = italic;
2442  }
2443 
2444  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
2445  // (currently defaults to what has been read in from layer settings)
2446  QFont newFont;
2447  QFont appFont = QApplication::font();
2448  bool newFontBuilt = false;
2449  if ( ddBold || ddItalic )
2450  {
2451  // new font needs built, since existing style needs removed
2452  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
2453  newFontBuilt = true;
2454  newFont.setBold( ddBold );
2455  newFont.setItalic( ddItalic );
2456  }
2457  else if ( !ddFontStyle.isEmpty()
2458  && ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
2459  {
2460  if ( !ddFontFamily.isEmpty() )
2461  {
2462  // both family and style are different, build font from database
2463  QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
2464  if ( appFont != styledfont )
2465  {
2466  newFont = styledfont;
2467  newFontBuilt = true;
2468  }
2469  }
2470 
2471  // update the font face style
2472  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
2473  }
2474  else if ( !ddFontFamily.isEmpty() )
2475  {
2476  if ( ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
2477  {
2478  // just family is different, build font from database
2479  QFont styledfont = mFontDB.font( ddFontFamily, textNamedStyle, appFont.pointSize() );
2480  if ( appFont != styledfont )
2481  {
2482  newFont = styledfont;
2483  newFontBuilt = true;
2484  }
2485  }
2486  else
2487  {
2488  newFont = QFont( ddFontFamily );
2489  newFontBuilt = true;
2490  }
2491  }
2492 
2493  if ( newFontBuilt )
2494  {
2495  // copy over existing font settings
2496  //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
2497  newFont.setPixelSize( labelFont.pixelSize() );
2498  newFont.setCapitalization( labelFont.capitalization() );
2499  newFont.setUnderline( labelFont.underline() );
2500  newFont.setStrikeOut( labelFont.strikeOut() );
2501  newFont.setWordSpacing( labelFont.wordSpacing() );
2502  newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
2503 
2504  labelFont = newFont;
2505  }
2506 
2507  // data defined word spacing?
2508  double wordspace = labelFont.wordSpacing();
2510  {
2511  bool ok;
2512  double wspacing = exprVal.toDouble( &ok );
2513  QgsDebugMsgLevel( QString( "exprVal FontWordSpacing:%1" ).arg( wspacing ), 4 );
2514  if ( ok )
2515  {
2516  wordspace = wspacing;
2517  }
2518  }
2519  labelFont.setWordSpacing( sizeToPixel( wordspace, context, fontunits, false, fontSizeMapUnitScale ) );
2520 
2521  // data defined letter spacing?
2522  double letterspace = labelFont.letterSpacing();
2524  {
2525  bool ok;
2526  double lspacing = exprVal.toDouble( &ok );
2527  QgsDebugMsgLevel( QString( "exprVal FontLetterSpacing:%1" ).arg( lspacing ), 4 );
2528  if ( ok )
2529  {
2530  letterspace = lspacing;
2531  }
2532  }
2533  labelFont.setLetterSpacing( QFont::AbsoluteSpacing, sizeToPixel( letterspace, context, fontunits, false, fontSizeMapUnitScale ) );
2534 
2535  // data defined font capitalization?
2536  QFont::Capitalization fontcaps = labelFont.capitalization();
2538  {
2539  QString fcase = exprVal.toString().trimmed();
2540  QgsDebugMsgLevel( QString( "exprVal FontCase:%1" ).arg( fcase ), 4 );
2541 
2542  if ( !fcase.isEmpty() )
2543  {
2544  if ( fcase.compare( "NoChange", Qt::CaseInsensitive ) == 0 )
2545  {
2546  fontcaps = QFont::MixedCase;
2547  }
2548  else if ( fcase.compare( "Upper", Qt::CaseInsensitive ) == 0 )
2549  {
2550  fontcaps = QFont::AllUppercase;
2551  }
2552  else if ( fcase.compare( "Lower", Qt::CaseInsensitive ) == 0 )
2553  {
2554  fontcaps = QFont::AllLowercase;
2555  }
2556  else if ( fcase.compare( "Capitalize", Qt::CaseInsensitive ) == 0 )
2557  {
2558  fontcaps = QFont::Capitalize;
2559  }
2560 
2561  if ( fontcaps != labelFont.capitalization() )
2562  {
2563  labelFont.setCapitalization( fontcaps );
2564  }
2565  }
2566  }
2567 
2568  // data defined strikeout font style?
2570  {
2571  bool strikeout = exprVal.toBool();
2572  QgsDebugMsgLevel( QString( "exprVal Font strikeout:%1" ).arg( strikeout ? "true" : "false" ), 4 );
2573  labelFont.setStrikeOut( strikeout );
2574  }
2575 
2576  // data defined underline font style?
2578  {
2579  bool underline = exprVal.toBool();
2580  QgsDebugMsgLevel( QString( "exprVal Font underline:%1" ).arg( underline ? "true" : "false" ), 4 );
2581  labelFont.setUnderline( underline );
2582  }
2583 
2584  // pass the rest on to QgsPalLabeling::drawLabeling
2585 
2586  // data defined font color?
2587  dataDefinedValEval( "color", QgsPalLayerSettings::Color, exprVal );
2588 
2589  // data defined font transparency?
2590  dataDefinedValEval( "transp", QgsPalLayerSettings::FontTransp, exprVal );
2591 
2592  // data defined font blend mode?
2593  dataDefinedValEval( "blendmode", QgsPalLayerSettings::FontBlendMode, exprVal );
2594 
2595 }
2596 
2597 void QgsPalLayerSettings::parseTextBuffer()
2598 {
2599  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2600 
2601  // data defined draw buffer?
2602  bool drawBuffer = bufferDraw;
2603  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::BufferDraw, exprVal ) )
2604  {
2605  drawBuffer = exprVal.toBool();
2606  }
2607 
2608  if ( !drawBuffer )
2609  {
2610  return;
2611  }
2612 
2613  // data defined buffer size?
2614  double bufrSize = bufferSize;
2615  if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::BufferSize, exprVal ) )
2616  {
2617  bufrSize = exprVal.toDouble();
2618  }
2619 
2620  // data defined buffer transparency?
2621  int bufTransp = bufferTransp;
2622  if ( dataDefinedValEval( "transp", QgsPalLayerSettings::BufferTransp, exprVal ) )
2623  {
2624  bufTransp = exprVal.toInt();
2625  }
2626 
2627  drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufTransp < 100 );
2628 
2629  if ( !drawBuffer )
2630  {
2631  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
2632  dataDefinedValues.remove( QgsPalLayerSettings::BufferSize );
2633  dataDefinedValues.remove( QgsPalLayerSettings::BufferTransp );
2634  return; // don't bother evaluating values that won't be used
2635  }
2636 
2637  // data defined buffer units?
2638  dataDefinedValEval( "units", QgsPalLayerSettings::BufferUnit, exprVal );
2639 
2640  // data defined buffer color?
2641  dataDefinedValEval( "color", QgsPalLayerSettings::BufferColor, exprVal );
2642 
2643  // data defined buffer pen join style?
2644  dataDefinedValEval( "joinstyle", QgsPalLayerSettings::BufferJoinStyle, exprVal );
2645 
2646  // data defined buffer blend mode?
2647  dataDefinedValEval( "blendmode", QgsPalLayerSettings::BufferBlendMode, exprVal );
2648 }
2649 
2650 void QgsPalLayerSettings::parseTextFormatting()
2651 {
2652  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2653 
2654  // data defined multiline wrap character?
2655  QString wrapchr = wrapChar;
2656  if ( dataDefinedValEval( "string", QgsPalLayerSettings::MultiLineWrapChar, exprVal ) )
2657  {
2658  wrapchr = exprVal.toString();
2659  }
2660 
2661  // data defined multiline height?
2662  dataDefinedValEval( "double", QgsPalLayerSettings::MultiLineHeight, exprVal );
2663 
2664  // data defined multiline text align?
2666  {
2667  QString str = exprVal.toString().trimmed();
2668  QgsDebugMsgLevel( QString( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
2669 
2670  if ( !str.isEmpty() )
2671  {
2672  // "Left"
2674 
2675  if ( str.compare( "Center", Qt::CaseInsensitive ) == 0 )
2676  {
2678  }
2679  else if ( str.compare( "Right", Qt::CaseInsensitive ) == 0 )
2680  {
2681  aligntype = QgsPalLayerSettings::MultiRight;
2682  }
2683  dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant(( int )aligntype ) );
2684  }
2685  }
2686 
2687  // data defined direction symbol?
2688  bool drawDirSymb = addDirectionSymbol;
2689  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::DirSymbDraw, exprVal ) )
2690  {
2691  drawDirSymb = exprVal.toBool();
2692  }
2693 
2694  if ( drawDirSymb )
2695  {
2696  // data defined direction left symbol?
2697  dataDefinedValEval( "string", QgsPalLayerSettings::DirSymbLeft, exprVal );
2698 
2699  // data defined direction right symbol?
2700  dataDefinedValEval( "string", QgsPalLayerSettings::DirSymbRight, exprVal );
2701 
2702  // data defined direction symbol placement?
2704  {
2705  QString str = exprVal.toString().trimmed();
2706  QgsDebugMsgLevel( QString( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
2707 
2708  if ( !str.isEmpty() )
2709  {
2710  // "LeftRight"
2712 
2713  if ( str.compare( "Above", Qt::CaseInsensitive ) == 0 )
2714  {
2716  }
2717  else if ( str.compare( "Below", Qt::CaseInsensitive ) == 0 )
2718  {
2720  }
2721  dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant(( int )placetype ) );
2722  }
2723  }
2724 
2725  // data defined direction symbol reversed?
2726  dataDefinedValEval( "bool", QgsPalLayerSettings::DirSymbReverse, exprVal );
2727  }
2728 
2729  // formatting for numbers is inline with generation of base label text and not passed to label painting
2730 }
2731 
2732 void QgsPalLayerSettings::parseShapeBackground()
2733 {
2734  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2735 
2736  // data defined draw shape?
2737  bool drawShape = shapeDraw;
2738  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::ShapeDraw, exprVal ) )
2739  {
2740  drawShape = exprVal.toBool();
2741  }
2742 
2743  if ( !drawShape )
2744  {
2745  return;
2746  }
2747 
2748  // data defined shape transparency?
2749  int shapeTransp = shapeTransparency;
2750  if ( dataDefinedValEval( "transp", QgsPalLayerSettings::ShapeTransparency, exprVal ) )
2751  {
2752  shapeTransp = exprVal.toInt();
2753  }
2754 
2755  drawShape = ( drawShape && shapeTransp < 100 ); // size is not taken into account (could be)
2756 
2757  if ( !drawShape )
2758  {
2759  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
2760  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
2761  return; // don't bother evaluating values that won't be used
2762  }
2763 
2764  // data defined shape kind?
2767  {
2768  QString skind = exprVal.toString().trimmed();
2769  QgsDebugMsgLevel( QString( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
2770 
2771  if ( !skind.isEmpty() )
2772  {
2773  // "Rectangle"
2775 
2776  if ( skind.compare( "Square", Qt::CaseInsensitive ) == 0 )
2777  {
2779  }
2780  else if ( skind.compare( "Ellipse", Qt::CaseInsensitive ) == 0 )
2781  {
2783  }
2784  else if ( skind.compare( "Circle", Qt::CaseInsensitive ) == 0 )
2785  {
2787  }
2788  else if ( skind.compare( "SVG", Qt::CaseInsensitive ) == 0 )
2789  {
2791  }
2792  shapeKind = shpkind;
2793  dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant(( int )shpkind ) );
2794  }
2795  }
2796 
2797  // data defined shape SVG path?
2798  QString svgPath = shapeSVGFile;
2800  {
2801  QString svgfile = exprVal.toString().trimmed();
2802  QgsDebugMsgLevel( QString( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
2803 
2804  // '' empty paths are allowed
2805  svgPath = svgfile;
2806  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgfile ) );
2807  }
2808 
2809  // data defined shape size type?
2812  {
2813  QString stype = exprVal.toString().trimmed();
2814  QgsDebugMsgLevel( QString( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
2815 
2816  if ( !stype.isEmpty() )
2817  {
2818  // "Buffer"
2820 
2821  if ( stype.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
2822  {
2824  }
2825  shpSizeType = sizType;
2826  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant(( int )sizType ) );
2827  }
2828  }
2829 
2830  // data defined shape size X? (SVGs only use X for sizing)
2831  double ddShpSizeX = shapeSize.x();
2832  if ( dataDefinedValEval( "double", QgsPalLayerSettings::ShapeSizeX, exprVal ) )
2833  {
2834  ddShpSizeX = exprVal.toDouble();
2835  }
2836 
2837  // data defined shape size Y?
2838  double ddShpSizeY = shapeSize.y();
2839  if ( dataDefinedValEval( "double", QgsPalLayerSettings::ShapeSizeY, exprVal ) )
2840  {
2841  ddShpSizeY = exprVal.toDouble();
2842  }
2843 
2844  // don't continue under certain circumstances (e.g. size is fixed)
2845  bool skip = false;
2846  if ( shapeKind == QgsPalLayerSettings::ShapeSVG
2847  && ( svgPath.isEmpty()
2848  || ( !svgPath.isEmpty()
2849  && shpSizeType == QgsPalLayerSettings::SizeFixed
2850  && ddShpSizeX == 0.0 ) ) )
2851  {
2852  skip = true;
2853  }
2854  if ( shapeKind != QgsPalLayerSettings::ShapeSVG
2855  && shpSizeType == QgsPalLayerSettings::SizeFixed
2856  && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
2857  {
2858  skip = true;
2859  }
2860 
2861  if ( skip )
2862  {
2863  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
2864  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
2865  dataDefinedValues.remove( QgsPalLayerSettings::ShapeKind );
2866  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSVGFile );
2867  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeX );
2868  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeY );
2869  return; // don't bother evaluating values that won't be used
2870  }
2871 
2872  // data defined shape size units?
2873  dataDefinedValEval( "units", QgsPalLayerSettings::ShapeSizeUnits, exprVal );
2874 
2875  // data defined shape rotation type?
2877  {
2878  QString rotstr = exprVal.toString().trimmed();
2879  QgsDebugMsgLevel( QString( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
2880 
2881  if ( !rotstr.isEmpty() )
2882  {
2883  // "Sync"
2885 
2886  if ( rotstr.compare( "Offset", Qt::CaseInsensitive ) == 0 )
2887  {
2889  }
2890  else if ( rotstr.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
2891  {
2893  }
2894  dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant(( int )rottype ) );
2895  }
2896  }
2897 
2898  // data defined shape rotation?
2899  dataDefinedValEval( "rotation180", QgsPalLayerSettings::ShapeRotation, exprVal );
2900 
2901  // data defined shape offset?
2902  dataDefinedValEval( "pointf", QgsPalLayerSettings::ShapeOffset, exprVal );
2903 
2904  // data defined shape offset units?
2905  dataDefinedValEval( "units", QgsPalLayerSettings::ShapeOffsetUnits, exprVal );
2906 
2907  // data defined shape radii?
2908  dataDefinedValEval( "pointf", QgsPalLayerSettings::ShapeRadii, exprVal );
2909 
2910  // data defined shape radii units?
2911  dataDefinedValEval( "units", QgsPalLayerSettings::ShapeRadiiUnits, exprVal );
2912 
2913  // data defined shape blend mode?
2914  dataDefinedValEval( "blendmode", QgsPalLayerSettings::ShapeBlendMode, exprVal );
2915 
2916  // data defined shape fill color?
2917  dataDefinedValEval( "color", QgsPalLayerSettings::ShapeFillColor, exprVal );
2918 
2919  // data defined shape border color?
2920  dataDefinedValEval( "color", QgsPalLayerSettings::ShapeBorderColor, exprVal );
2921 
2922  // data defined shape border width?
2923  dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShapeBorderWidth, exprVal );
2924 
2925  // data defined shape border width units?
2926  dataDefinedValEval( "units", QgsPalLayerSettings::ShapeBorderWidthUnits, exprVal );
2927 
2928  // data defined shape join style?
2929  dataDefinedValEval( "joinstyle", QgsPalLayerSettings::ShapeJoinStyle, exprVal );
2930 
2931 }
2932 
2933 void QgsPalLayerSettings::parseDropShadow()
2934 {
2935  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2936 
2937  // data defined draw shadow?
2938  bool drawShadow = shadowDraw;
2939  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::ShadowDraw, exprVal ) )
2940  {
2941  drawShadow = exprVal.toBool();
2942  }
2943 
2944  if ( !drawShadow )
2945  {
2946  return;
2947  }
2948 
2949  // data defined shadow transparency?
2950  int shadowTransp = shadowTransparency;
2951  if ( dataDefinedValEval( "transp", QgsPalLayerSettings::ShadowTransparency, exprVal ) )
2952  {
2953  shadowTransp = exprVal.toInt();
2954  }
2955 
2956  // data defined shadow offset distance?
2957  double shadowOffDist = shadowOffsetDist;
2958  if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShadowOffsetDist, exprVal ) )
2959  {
2960  shadowOffDist = exprVal.toDouble();
2961  }
2962 
2963  // data defined shadow offset distance?
2964  double shadowRad = shadowRadius;
2965  if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShadowRadius, exprVal ) )
2966  {
2967  shadowRad = exprVal.toDouble();
2968  }
2969 
2970  drawShadow = ( drawShadow && shadowTransp < 100 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
2971 
2972  if ( !drawShadow )
2973  {
2974  dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
2975  dataDefinedValues.remove( QgsPalLayerSettings::ShadowTransparency );
2976  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOffsetDist );
2977  dataDefinedValues.remove( QgsPalLayerSettings::ShadowRadius );
2978  return; // don't bother evaluating values that won't be used
2979  }
2980 
2981  // data defined shadow under type?
2983  {
2984  QString str = exprVal.toString().trimmed();
2985  QgsDebugMsgLevel( QString( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
2986 
2987  if ( !str.isEmpty() )
2988  {
2989  // "Lowest"
2991 
2992  if ( str.compare( "Text", Qt::CaseInsensitive ) == 0 )
2993  {
2995  }
2996  else if ( str.compare( "Buffer", Qt::CaseInsensitive ) == 0 )
2997  {
2999  }
3000  else if ( str.compare( "Background", Qt::CaseInsensitive ) == 0 )
3001  {
3003  }
3004  dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant(( int )shdwtype ) );
3005  }
3006  }
3007 
3008  // data defined shadow offset angle?
3009  dataDefinedValEval( "rotation180", QgsPalLayerSettings::ShadowOffsetAngle, exprVal );
3010 
3011  // data defined shadow offset units?
3012  dataDefinedValEval( "units", QgsPalLayerSettings::ShadowOffsetUnits, exprVal );
3013 
3014  // data defined shadow radius?
3015  dataDefinedValEval( "double", QgsPalLayerSettings::ShadowRadius, exprVal );
3016 
3017  // data defined shadow radius units?
3018  dataDefinedValEval( "units", QgsPalLayerSettings::ShadowRadiusUnits, exprVal );
3019 
3020  // data defined shadow scale? ( gui bounds to 0-2000, no upper bound here )
3021  dataDefinedValEval( "intpos", QgsPalLayerSettings::ShadowScale, exprVal );
3022 
3023  // data defined shadow color?
3024  dataDefinedValEval( "color", QgsPalLayerSettings::ShadowColor, exprVal );
3025 
3026  // data defined shadow blend mode?
3027  dataDefinedValEval( "blendmode", QgsPalLayerSettings::ShadowBlendMode, exprVal );
3028 }
3029 
3030 int QgsPalLayerSettings::sizeToPixel( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor, const QgsMapUnitScale& mapUnitScale ) const
3031 {
3032  return ( int )( scaleToPixelContext( size, c, unit, rasterfactor, mapUnitScale ) + 0.5 );
3033 }
3034 
3035 double QgsPalLayerSettings::scaleToPixelContext( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor, const QgsMapUnitScale& mapUnitScale ) const
3036 {
3037  // if render context is that of device (i.e. not a scaled map), just return size
3038  double mapUnitsPerPixel = mapUnitScale.computeMapUnitsPerPixel( c );
3039 
3040  if ( unit == MapUnits && mapUnitsPerPixel > 0.0 )
3041  {
3042  size = size / mapUnitsPerPixel * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3043  }
3044  else // e.g. in points or mm
3045  {
3046  double ptsTomm = ( unit == Points ? 0.352778 : 1 );
3047  size *= ptsTomm * c.scaleFactor() * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3048  }
3049  return size;
3050 }
3051 
3052 // -------------
3053 
3055  : mMapSettings( NULL ), mPal( NULL )
3056  , mResults( 0 )
3057 {
3058 
3059  // find out engine defaults
3060  Pal p;
3061  mCandPoint = p.getPointP();
3062  mCandLine = p.getLineP();
3063  mCandPolygon = p.getPolyP();
3064 
3065  switch ( p.getSearch() )
3066  {
3067  case CHAIN: mSearch = Chain; break;
3068  case POPMUSIC_TABU: mSearch = Popmusic_Tabu; break;
3069  case POPMUSIC_CHAIN: mSearch = Popmusic_Chain; break;
3071  case FALP: mSearch = Falp; break;
3072  }
3073 
3074  mShowingCandidates = false;
3075  mShowingShadowRects = false;
3076  mShowingAllLabels = false;
3078  mDrawOutlineLabels = true;
3079 }
3080 
3082 {
3083  // make sure we've freed everything
3084  exit();
3085 
3087 
3088  delete mResults;
3089  mResults = 0;
3090 }
3091 
3093 {
3094  return staticWillUseLayer( layer );
3095 }
3096 
3097 bool QgsPalLabeling::staticWillUseLayer( const QString& layerID )
3098 {
3099  QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( layerID ) );
3100  if ( !layer )
3101  return false;
3102  return staticWillUseLayer( layer );
3103 }
3104 
3105 
3107 {
3108  // don't do QgsPalLayerSettings::readFromLayer( layer ) if not needed
3109  bool enabled = false;
3110  if ( layer->customProperty( "labeling" ).toString() == QString( "pal" ) )
3111  enabled = layer->customProperty( "labeling/enabled", QVariant( false ) ).toBool();
3112 
3113  return enabled;
3114 }
3115 
3116 
3118 {
3119  QHash<QString, QgsPalLayerSettings>::iterator lit;
3120  for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
3121  {
3122  clearActiveLayer( lit.key() );
3123  }
3124  mActiveLayers.clear();
3125 }
3126 
3127 void QgsPalLabeling::clearActiveLayer( const QString &layerID )
3128 {
3129  QgsPalLayerSettings& lyr = mActiveLayers[layerID];
3130 
3131  // delete all QgsDataDefined objects (which also deletes their QgsExpression object)
3132  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::iterator it = lyr.dataDefinedProperties.begin();
3133  for ( ; it != lyr.dataDefinedProperties.constEnd(); ++it )
3134  {
3135  delete( it.value() );
3136  it.value() = 0;
3137  }
3138  lyr.dataDefinedProperties.clear();
3139 }
3140 
3141 int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx )
3142 {
3143  Q_ASSERT( mMapSettings != NULL );
3144 
3145  if ( !willUseLayer( layer ) )
3146  {
3147  return 0;
3148  }
3149 
3150  QgsDebugMsgLevel( "PREPARE LAYER " + layer->id(), 4 );
3151 
3152  // start with a temporary settings class, find out labeling info
3153  QgsPalLayerSettings lyrTmp;
3154  lyrTmp.readFromLayer( layer );
3155 
3156  if ( lyrTmp.fieldName.isEmpty() )
3157  {
3158  return 0;
3159  }
3160 
3161  if ( lyrTmp.isExpression )
3162  {
3163  QgsExpression exp( lyrTmp.fieldName );
3164  if ( exp.hasEvalError() )
3165  {
3166  QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 );
3167  return 0;
3168  }
3169  }
3170  else
3171  {
3172  // If we aren't an expression, we check to see if we can find the column.
3173  if ( layer->fieldNameIndex( lyrTmp.fieldName ) == -1 )
3174  {
3175  return 0;
3176  }
3177  }
3178 
3179  // add layer settings to the pallabeling hashtable: <QgsVectorLayer*, QgsPalLayerSettings>
3180  mActiveLayers.insert( layer->id(), lyrTmp );
3181  // start using the reference to the layer in hashtable instead of local instance
3182  QgsPalLayerSettings& lyr = mActiveLayers[layer->id()];
3183 
3184  lyr.mCurFields = &( layer->pendingFields() );
3185 
3186  // add field indices for label's text, from expression or field
3187  if ( lyr.isExpression )
3188  {
3189  // prepare expression for use in QgsPalLayerSettings::registerFeature()
3190  QgsExpression* exp = lyr.getLabelExpression();
3191  exp->prepare( layer->pendingFields() );
3192  if ( exp->hasEvalError() )
3193  {
3194  QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 );
3195  }
3196  foreach ( QString name, exp->referencedColumns() )
3197  {
3198  QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 );
3199  attrNames.append( name );
3200  }
3201  }
3202  else
3203  {
3204  attrNames.append( lyr.fieldName );
3205  }
3206 
3207  // add field indices of data defined expression or field
3208  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator dIt = lyr.dataDefinedProperties.constBegin();
3209  for ( ; dIt != lyr.dataDefinedProperties.constEnd(); ++dIt )
3210  {
3211  QgsDataDefined* dd = dIt.value();
3212  if ( !dd->isActive() )
3213  {
3214  continue;
3215  }
3216 
3217  // NOTE: the following also prepares any expressions for later use
3218 
3219  // store parameters for data defined expressions
3220  QMap<QString, QVariant> exprParams;
3221  exprParams.insert( "scale", ctx.rendererScale() );
3222 
3223  dd->setExpressionParams( exprParams );
3224 
3225  // this will return columns for expressions or field name, depending upon what is set to be used
3226  QStringList cols = dd->referencedColumns( layer ); // <-- prepares any expressions, too
3227 
3228  //QgsDebugMsgLevel( QString( "Data defined referenced columns:" ) + cols.join( "," ), 4 );
3229  foreach ( QString name, cols )
3230  {
3231  attrNames.append( name );
3232  }
3233  }
3234 
3235  // how to place the labels
3236  Arrangement arrangement;
3237  switch ( lyr.placement )
3238  {
3239  case QgsPalLayerSettings::AroundPoint: arrangement = P_POINT; break;
3240  case QgsPalLayerSettings::OverPoint: arrangement = P_POINT_OVER; break;
3241  case QgsPalLayerSettings::Line: arrangement = P_LINE; break;
3242  case QgsPalLayerSettings::Curved: arrangement = P_CURVED; break;
3243  case QgsPalLayerSettings::Horizontal: arrangement = P_HORIZ; break;
3244  case QgsPalLayerSettings::Free: arrangement = P_FREE; break;
3245  default: Q_ASSERT( "unsupported placement" && 0 ); return 0;
3246  }
3247 
3248  // create the pal layer
3249  double priority = 1 - lyr.priority / 10.0; // convert 0..10 --> 1..0
3250  double min_scale = -1, max_scale = -1;
3251 
3252  // handled in QgsPalLayerSettings::registerFeature now
3253  //if ( lyr.scaleVisibility && !lyr.dataDefinedIsActive( QgsPalLayerSettings::ScaleVisibility ) )
3254  //{
3255  // min_scale = lyr.scaleMin;
3256  // max_scale = lyr.scaleMax;
3257  //}
3258 
3259  Layer* l = mPal->addLayer( layer->id().toUtf8().data(),
3260  min_scale, max_scale, arrangement,
3261  METER, priority, lyr.obstacle, true, true,
3262  lyr.displayAll );
3263 
3264  if ( lyr.placementFlags )
3266 
3267  // set label mode (label per feature is the default)
3268  l->setLabelMode( lyr.labelPerPart ? Layer::LabelPerFeaturePart : Layer::LabelPerFeature );
3269 
3270  // set whether adjacent lines should be merged
3272 
3273 
3274  // set whether location of centroid must be inside of polygons
3276 
3277  // set how to show upside-down labels
3278  Layer::UpsideDownLabels upsdnlabels;
3279  switch ( lyr.upsidedownLabels )
3280  {
3281  case QgsPalLayerSettings::Upright: upsdnlabels = Layer::Upright; break;
3282  case QgsPalLayerSettings::ShowDefined: upsdnlabels = Layer::ShowDefined; break;
3283  case QgsPalLayerSettings::ShowAll: upsdnlabels = Layer::ShowAll; break;
3284  default: Q_ASSERT( "unsupported upside-down label setting" && 0 ); return 0;
3285  }
3286  l->setUpsidedownLabels( upsdnlabels );
3287 
3288 // // fix for font size in map units causing font to show pointsize at small map scales
3289 // int pixelFontSize = lyr.sizeToPixel( lyr.textFont.pointSizeF(), ctx,
3290 // lyr.fontSizeInMapUnits ? QgsPalLayerSettings::MapUnits : QgsPalLayerSettings::Points,
3291 // true );
3292 
3293 // if ( pixelFontSize < 1 )
3294 // {
3295 // lyr.textFont.setPointSize( 1 );
3296 // lyr.textFont.setPixelSize( 1 );
3297 // }
3298 // else
3299 // {
3300 // lyr.textFont.setPixelSize( pixelFontSize );
3301 // }
3302 
3303 // // scale spacing sizes if using map units
3304 // if ( lyr.fontSizeInMapUnits )
3305 // {
3306 // double spacingPixelSize;
3307 // if ( lyr.textFont.wordSpacing() != 0 )
3308 // {
3309 // spacingPixelSize = lyr.textFont.wordSpacing() / ctx.mapToPixel().mapUnitsPerPixel() * ctx.rasterScaleFactor();
3310 // lyr.textFont.setWordSpacing( spacingPixelSize );
3311 // }
3312 
3313 // if ( lyr.textFont.letterSpacing() != 0 )
3314 // {
3315 // spacingPixelSize = lyr.textFont.letterSpacing() / ctx.mapToPixel().mapUnitsPerPixel() * ctx.rasterScaleFactor();
3316 // lyr.textFont.setLetterSpacing( QFont::AbsoluteSpacing, spacingPixelSize );
3317 // }
3318 // }
3319 
3320  //raster and vector scale factors
3321  lyr.vectorScaleFactor = ctx.scaleFactor();
3323 
3324  // save the pal layer to our layer context (with some additional info)
3325  lyr.palLayer = l;
3326  lyr.fieldIndex = layer->fieldNameIndex( lyr.fieldName );
3327 
3328  lyr.xform = &mMapSettings->mapToPixel();
3329  lyr.ct = 0;
3331  lyr.ct = new QgsCoordinateTransform( layer->crs(), mMapSettings->destinationCrs() );
3332  lyr.ptZero = lyr.xform->toMapCoordinates( 0, 0 );
3333  lyr.ptOne = lyr.xform->toMapCoordinates( 1, 0 );
3334 
3335  // rect for clipping
3337 
3338  lyr.mFeatsSendingToPal = 0;
3339 
3340  return 1; // init successful
3341 }
3342 
3344 {
3345  Layer* l = mPal->addLayer( layer->id().append( "d" ).toUtf8().data(), -1, -1, pal::Arrangement( s->placement ), METER, s->priority, s->obstacle, true, true );
3347 
3348  mActiveDiagramLayers.insert( layer->id(), *s );
3349  // initialize the local copy
3351 
3352  s2.palLayer = l;
3353  s2.ct = 0;
3355  s2.ct = new QgsCoordinateTransform( layer->crs(), mMapSettings->destinationCrs() );
3356 
3357  s2.xform = &mMapSettings->mapToPixel();
3358 
3359  s2.fields = layer->pendingFields();
3360 
3361  s2.renderer = layer->diagramRenderer()->clone();
3362 
3363  return 1;
3364 }
3365 
3366 void QgsPalLabeling::registerFeature( const QString& layerID, QgsFeature& f, const QgsRenderContext& context, QString dxfLayer )
3367 {
3368  QgsPalLayerSettings& lyr = mActiveLayers[layerID];
3369  lyr.registerFeature( f, context, dxfLayer );
3370 }
3371 
3372 void QgsPalLabeling::registerDiagramFeature( const QString& layerID, QgsFeature& feat, const QgsRenderContext& context )
3373 {
3374  //get diagram layer settings, diagram renderer
3375  QHash<QString, QgsDiagramLayerSettings>::iterator layerIt = mActiveDiagramLayers.find( layerID );
3376  if ( layerIt == mActiveDiagramLayers.constEnd() )
3377  {
3378  return;
3379  }
3380 
3381  //convert geom to geos
3382  QgsGeometry* geom = feat.geometry();
3383 
3384  if ( layerIt.value().ct && staticWillUseLayer( layerID ) ) // reproject the geometry if feature not already transformed for labeling
3385  {
3386  geom->transform( *( layerIt.value().ct ) );
3387  }
3388 
3389  const GEOSGeometry* geos_geom = geom->asGeos();
3390  if ( geos_geom == 0 )
3391  {
3392  return; // invalid geometry
3393  }
3394 
3395  //create PALGeometry with diagram = true
3396  QgsPalGeometry* lbl = new QgsPalGeometry( feat.id(), "", GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom ) );
3397  lbl->setIsDiagram( true );
3398 
3399  // record the created geometry - it will be deleted at the end.
3400  layerIt.value().geometries.append( lbl );
3401 
3402  double diagramWidth = 0;
3403  double diagramHeight = 0;
3404  QgsDiagramRendererV2* dr = layerIt.value().renderer;
3405  if ( dr )
3406  {
3407  QSizeF diagSize = dr->sizeMapUnits( feat, context );
3408  if ( diagSize.isValid() )
3409  {
3410  diagramWidth = diagSize.width();
3411  diagramHeight = diagSize.height();
3412  }
3413 
3414  //append the diagram attributes to lbl
3415  lbl->setDiagramAttributes( feat.attributes() );
3416  }
3417 
3418  // feature to the layer
3419  int ddColX = layerIt.value().xPosColumn;
3420  int ddColY = layerIt.value().yPosColumn;
3421  double ddPosX = 0.0;
3422  double ddPosY = 0.0;
3423  bool ddPos = ( ddColX >= 0 && ddColY >= 0 );
3424  if ( ddPos )
3425  {
3426  bool posXOk, posYOk;
3427  //data defined diagram position is always centered
3428  ddPosX = feat.attribute( ddColX ).toDouble( &posXOk ) - diagramWidth / 2.0;
3429  ddPosY = feat.attribute( ddColY ).toDouble( &posYOk ) - diagramHeight / 2.0;
3430  if ( !posXOk || !posYOk )
3431  {
3432  ddPos = false;
3433  }
3434  else
3435  {
3436  const QgsCoordinateTransform* ct = layerIt.value().ct;
3437  if ( ct )
3438  {
3439  double z = 0;
3440  ct->transformInPlace( ddPosX, ddPosY, z );
3441  }
3442  }
3443  }
3444 
3445  try
3446  {
3447  if ( !layerIt.value().palLayer->registerFeature( lbl->strId(), lbl, diagramWidth, diagramHeight, "", ddPosX, ddPosY, ddPos ) )
3448  {
3449  return;
3450  }
3451  }
3452  catch ( std::exception &e )
3453  {
3454  Q_UNUSED( e );
3455  QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( feat.id() ) + QString::fromLatin1( e.what() ), 4 );
3456  return;
3457  }
3458 
3459  pal::Feature* palFeat = layerIt.value().palLayer->getFeature( lbl->strId() );
3460  QgsPoint ptZero = layerIt.value().xform->toMapCoordinates( 0, 0 );
3461  QgsPoint ptOne = layerIt.value().xform->toMapCoordinates( 1, 0 );
3462  palFeat->setDistLabel( qAbs( ptOne.x() - ptZero.x() ) * layerIt.value().dist );
3463 }
3464 
3465 
3467 {
3468  init( mr->mapSettings() );
3469 }
3470 
3471 void QgsPalLabeling::init( const QgsMapSettings& mapSettings )
3472 {
3473  mMapSettings = &mapSettings;
3474 
3475  // delete if exists already
3476  if ( mPal )
3477  delete mPal;
3478 
3479  mPal = new Pal;
3480 
3481  SearchMethod s;
3482  switch ( mSearch )
3483  {
3484  default:
3485  case Chain: s = CHAIN; break;
3486  case Popmusic_Tabu: s = POPMUSIC_TABU; break;
3487  case Popmusic_Chain: s = POPMUSIC_CHAIN; break;
3488  case Popmusic_Tabu_Chain: s = POPMUSIC_TABU_CHAIN; break;
3489  case Falp: s = FALP; break;
3490  }
3491  mPal->setSearch( s );
3492 
3493  // set number of candidates generated per feature
3495  mPal->setLineP( mCandLine );
3497 
3499 
3500  clearActiveLayers(); // free any previous QgsDataDefined objects
3501  mActiveDiagramLayers.clear();
3502 }
3503 
3505 {
3506  delete mPal;
3507  mPal = NULL;
3508  mMapSettings = NULL;
3509 }
3510 
3511 QgsPalLayerSettings& QgsPalLabeling::layer( const QString& layerName )
3512 {
3513  QHash<QString, QgsPalLayerSettings>::iterator lit;
3514  for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
3515  {
3516  if ( lit.key() == layerName )
3517  {
3518  return lit.value();
3519  }
3520  }
3521  return mInvalidLayerSettings;
3522 }
3523 
3525  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3526 {
3527  //font color
3528  if ( ddValues.contains( QgsPalLayerSettings::Color ) )
3529  {
3530  QVariant ddColor = ddValues.value( QgsPalLayerSettings::Color );
3531  tmpLyr.textColor = ddColor.value<QColor>();
3532  }
3533 
3534  //font transparency
3535  if ( ddValues.contains( QgsPalLayerSettings::FontTransp ) )
3536  {
3537  tmpLyr.textTransp = ddValues.value( QgsPalLayerSettings::FontTransp ).toInt();
3538  }
3539 
3540  tmpLyr.textColor.setAlphaF(( 100.0 - ( double )( tmpLyr.textTransp ) ) / 100.0 );
3541 
3542  //font blend mode
3543  if ( ddValues.contains( QgsPalLayerSettings::FontBlendMode ) )
3544  {
3545  tmpLyr.blendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::FontBlendMode ).toInt();
3546  }
3547 }
3548 
3550  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3551 {
3552  if ( ddValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
3553  {
3554  tmpLyr.wrapChar = ddValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
3555  }
3556 
3557  if ( !tmpLyr.wrapChar.isEmpty() || tmpLyr.getLabelExpression()->expression().contains( "wordwrap" ) )
3558  {
3559 
3560  if ( ddValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
3561  {
3562  tmpLyr.multilineHeight = ddValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
3563  }
3564 
3565  if ( ddValues.contains( QgsPalLayerSettings::MultiLineAlignment ) )
3566  {
3568  }
3569 
3570  }
3571 
3572  if ( ddValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
3573  {
3574  tmpLyr.addDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
3575  }
3576 
3577  if ( tmpLyr.addDirectionSymbol )
3578  {
3579 
3580  if ( ddValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
3581  {
3582  tmpLyr.leftDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
3583  }
3584  if ( ddValues.contains( QgsPalLayerSettings::DirSymbRight ) )
3585  {
3586  tmpLyr.rightDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
3587  }
3588 
3589  if ( ddValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
3590  {
3592  }
3593 
3594  if ( ddValues.contains( QgsPalLayerSettings::DirSymbReverse ) )
3595  {
3596  tmpLyr.reverseDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbReverse ).toBool();
3597  }
3598 
3599  }
3600 }
3601 
3603  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3604 {
3605  //buffer draw
3606  if ( ddValues.contains( QgsPalLayerSettings::BufferDraw ) )
3607  {
3608  tmpLyr.bufferDraw = ddValues.value( QgsPalLayerSettings::BufferDraw ).toBool();
3609  }
3610 
3611  if ( !tmpLyr.bufferDraw )
3612  {
3613  // tmpLyr.bufferSize > 0.0 && tmpLyr.bufferTransp < 100 figured in during evaluation
3614  return; // don't continue looking for unused values
3615  }
3616 
3617  //buffer size
3618  if ( ddValues.contains( QgsPalLayerSettings::BufferSize ) )
3619  {
3620  tmpLyr.bufferSize = ddValues.value( QgsPalLayerSettings::BufferSize ).toDouble();
3621  }
3622 
3623  //buffer transparency
3624  if ( ddValues.contains( QgsPalLayerSettings::BufferTransp ) )
3625  {
3626  tmpLyr.bufferTransp = ddValues.value( QgsPalLayerSettings::BufferTransp ).toInt();
3627  }
3628 
3629  //buffer size units
3630  if ( ddValues.contains( QgsPalLayerSettings::BufferUnit ) )
3631  {
3633  tmpLyr.bufferSizeInMapUnits = ( bufunit == QgsPalLayerSettings::MapUnits );
3634  }
3635 
3636  //buffer color
3637  if ( ddValues.contains( QgsPalLayerSettings::BufferColor ) )
3638  {
3639  QVariant ddColor = ddValues.value( QgsPalLayerSettings::BufferColor );
3640  tmpLyr.bufferColor = ddColor.value<QColor>();
3641  }
3642 
3643  // apply any transparency
3644  tmpLyr.bufferColor.setAlphaF(( 100.0 - ( double )( tmpLyr.bufferTransp ) ) / 100.0 );
3645 
3646  //buffer pen join style
3647  if ( ddValues.contains( QgsPalLayerSettings::BufferJoinStyle ) )
3648  {
3649  tmpLyr.bufferJoinStyle = ( Qt::PenJoinStyle )ddValues.value( QgsPalLayerSettings::BufferJoinStyle ).toInt();
3650  }
3651 
3652  //buffer blend mode
3653  if ( ddValues.contains( QgsPalLayerSettings::BufferBlendMode ) )
3654  {
3655  tmpLyr.bufferBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::BufferBlendMode ).toInt();
3656  }
3657 }
3658 
3660  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3661 {
3662  //shape draw
3663  if ( ddValues.contains( QgsPalLayerSettings::ShapeDraw ) )
3664  {
3665  tmpLyr.shapeDraw = ddValues.value( QgsPalLayerSettings::ShapeDraw ).toBool();
3666  }
3667 
3668  if ( !tmpLyr.shapeDraw )
3669  {
3670  return; // don't continue looking for unused values
3671  }
3672 
3673  if ( ddValues.contains( QgsPalLayerSettings::ShapeKind ) )
3674  {
3675  tmpLyr.shapeType = ( QgsPalLayerSettings::ShapeType )ddValues.value( QgsPalLayerSettings::ShapeKind ).toInt();
3676  }
3677 
3678  if ( ddValues.contains( QgsPalLayerSettings::ShapeSVGFile ) )
3679  {
3680  tmpLyr.shapeSVGFile = ddValues.value( QgsPalLayerSettings::ShapeSVGFile ).toString();
3681  }
3682 
3683  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeType ) )
3684  {
3686  }
3687 
3688  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeX ) )
3689  {
3690  tmpLyr.shapeSize.setX( ddValues.value( QgsPalLayerSettings::ShapeSizeX ).toDouble() );
3691  }
3692  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeY ) )
3693  {
3694  tmpLyr.shapeSize.setY( ddValues.value( QgsPalLayerSettings::ShapeSizeY ).toDouble() );
3695  }
3696 
3697  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeUnits ) )
3698  {
3700  }
3701 
3702  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotationType ) )
3703  {
3705  }
3706 
3707  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotation ) )
3708  {
3709  tmpLyr.shapeRotation = ddValues.value( QgsPalLayerSettings::ShapeRotation ).toDouble();
3710  }
3711 
3712  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffset ) )
3713  {
3714  tmpLyr.shapeOffset = ddValues.value( QgsPalLayerSettings::ShapeOffset ).toPointF();
3715  }
3716 
3717  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffsetUnits ) )
3718  {
3720  }
3721 
3722  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadii ) )
3723  {
3724  tmpLyr.shapeRadii = ddValues.value( QgsPalLayerSettings::ShapeRadii ).toPointF();
3725  }
3726 
3727  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadiiUnits ) )
3728  {
3730  }
3731 
3732  if ( ddValues.contains( QgsPalLayerSettings::ShapeTransparency ) )
3733  {
3734  tmpLyr.shapeTransparency = ddValues.value( QgsPalLayerSettings::ShapeTransparency ).toInt();
3735  }
3736 
3737  if ( ddValues.contains( QgsPalLayerSettings::ShapeBlendMode ) )
3738  {
3739  tmpLyr.shapeBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::ShapeBlendMode ).toInt();
3740  }
3741 
3742  if ( ddValues.contains( QgsPalLayerSettings::ShapeFillColor ) )
3743  {
3744  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeFillColor );
3745  tmpLyr.shapeFillColor = ddColor.value<QColor>();
3746  }
3747 
3748  if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderColor ) )
3749  {
3750  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeBorderColor );
3751  tmpLyr.shapeBorderColor = ddColor.value<QColor>();
3752  }
3753 
3754  if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderWidth ) )
3755  {
3756  tmpLyr.shapeBorderWidth = ddValues.value( QgsPalLayerSettings::ShapeBorderWidth ).toDouble();
3757  }
3758 
3759  if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderWidthUnits ) )
3760  {
3762  }
3763 
3764  if ( ddValues.contains( QgsPalLayerSettings::ShapeJoinStyle ) )
3765  {
3766  tmpLyr.shapeJoinStyle = ( Qt::PenJoinStyle )ddValues.value( QgsPalLayerSettings::ShapeJoinStyle ).toInt();
3767  }
3768 }
3769 
3771  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3772 {
3773  //shadow draw
3774  if ( ddValues.contains( QgsPalLayerSettings::ShadowDraw ) )
3775  {
3776  tmpLyr.shadowDraw = ddValues.value( QgsPalLayerSettings::ShadowDraw ).toBool();
3777  }
3778 
3779  if ( !tmpLyr.shadowDraw )
3780  {
3781  return; // don't continue looking for unused values
3782  }
3783 
3784  if ( ddValues.contains( QgsPalLayerSettings::ShadowUnder ) )
3785  {
3787  }
3788 
3789  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetAngle ) )
3790  {
3791  tmpLyr.shadowOffsetAngle = ddValues.value( QgsPalLayerSettings::ShadowOffsetAngle ).toInt();
3792  }
3793 
3794  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetDist ) )
3795  {
3796  tmpLyr.shadowOffsetDist = ddValues.value( QgsPalLayerSettings::ShadowOffsetDist ).toDouble();
3797  }
3798 
3799  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetUnits ) )
3800  {
3802  }
3803 
3804  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadius ) )
3805  {
3806  tmpLyr.shadowRadius = ddValues.value( QgsPalLayerSettings::ShadowRadius ).toDouble();
3807  }
3808 
3809  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadiusUnits ) )
3810  {
3812  }
3813 
3814  if ( ddValues.contains( QgsPalLayerSettings::ShadowTransparency ) )
3815  {
3816  tmpLyr.shadowTransparency = ddValues.value( QgsPalLayerSettings::ShadowTransparency ).toInt();
3817  }
3818 
3819  if ( ddValues.contains( QgsPalLayerSettings::ShadowScale ) )
3820  {
3821  tmpLyr.shadowScale = ddValues.value( QgsPalLayerSettings::ShadowScale ).toInt();
3822  }
3823 
3824  if ( ddValues.contains( QgsPalLayerSettings::ShadowColor ) )
3825  {
3826  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShadowColor );
3827  tmpLyr.shadowColor = ddColor.value<QColor>();
3828  }
3829 
3830  if ( ddValues.contains( QgsPalLayerSettings::ShadowBlendMode ) )
3831  {
3832  tmpLyr.shadowBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::ShadowBlendMode ).toInt();
3833  }
3834 }
3835 
3836 
3837 // helper function for checking for job cancellation within PAL
3838 static bool _palIsCancelled( void* ctx )
3839 {
3840  return (( QgsRenderContext* ) ctx )->renderingStopped();
3841 }
3842 
3844 {
3845  Q_ASSERT( mMapSettings != NULL );
3846  QPainter* painter = context.painter();
3847  QgsRectangle extent = context.extent();
3848 
3850 
3851  delete mResults;
3853 
3854  QTime t;
3855  t.start();
3856 
3857  // do the labeling itself
3858  double scale = mMapSettings->scale(); // scale denominator
3859  QgsRectangle r = extent;
3860  double bbox[] = { r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum() };
3861 
3862  std::list<LabelPosition*>* labels;
3863  pal::Problem* problem;
3864  try
3865  {
3866  problem = mPal->extractProblem( scale, bbox );
3867  }
3868  catch ( std::exception& e )
3869  {
3870  Q_UNUSED( e );
3871  QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
3872  //mActiveLayers.clear(); // clean up
3873  return;
3874  }
3875 
3876  if ( context.renderingStopped() )
3877  return; // it has been cancelled
3878 
3879  const QgsMapToPixel& xform = mMapSettings->mapToPixel();
3880 
3881  // draw rectangles with all candidates
3882  // this is done before actual solution of the problem
3883  // before number of candidates gets reduced
3884  mCandidates.clear();
3885  if ( mShowingCandidates && problem )
3886  {
3887  painter->setPen( QColor( 0, 0, 0, 64 ) );
3888  painter->setBrush( Qt::NoBrush );
3889  for ( int i = 0; i < problem->getNumFeatures(); i++ )
3890  {
3891  for ( int j = 0; j < problem->getFeatureCandidateCount( i ); j++ )
3892  {
3893  pal::LabelPosition* lp = problem->getFeatureCandidate( i, j );
3894 
3895  drawLabelCandidateRect( lp, painter, &xform );
3896  }
3897  }
3898  }
3899 
3900  // find the solution
3901  labels = mPal->solveProblem( problem, mShowingAllLabels );
3902 
3903  QgsDebugMsgLevel( QString( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( labels->size() ), 4 );
3904  t.restart();
3905 
3906  if ( context.renderingStopped() )
3907  {
3908  delete problem;
3909  delete labels;
3911  return;
3912  }
3913 
3914  painter->setRenderHint( QPainter::Antialiasing );
3915 
3916  // draw the labels
3917  std::list<LabelPosition*>::iterator it = labels->begin();
3918  for ( ; it != labels->end(); ++it )
3919  {
3920  if ( context.renderingStopped() )
3921  break;
3922 
3923  QgsPalGeometry* palGeometry = dynamic_cast< QgsPalGeometry* >(( *it )->getFeaturePart()->getUserGeometry() );
3924  if ( !palGeometry )
3925  {
3926  continue;
3927  }
3928 
3929  //layer names
3930  QString layerName = QString::fromUtf8(( *it )->getLayerName() );
3931  if ( palGeometry->isDiagram() )
3932  {
3933  QgsFeature feature;
3934  //render diagram
3935  QHash<QString, QgsDiagramLayerSettings>::iterator dit = mActiveDiagramLayers.begin();
3936  for ( dit = mActiveDiagramLayers.begin(); dit != mActiveDiagramLayers.end(); ++dit )
3937  {
3938  if ( QString( dit.key() + "d" ) == layerName )
3939  {
3940  feature.setFields( &dit.value().fields );
3941  palGeometry->feature( feature );
3942  QgsPoint outPt = xform.transform(( *it )->getX(), ( *it )->getY() );
3943  dit.value().renderer->renderDiagram( feature, context, QPointF( outPt.x(), outPt.y() ) );
3944  }
3945  }
3946 
3947  //insert into label search tree to manipulate position interactively
3948  if ( mResults->mLabelSearchTree )
3949  {
3950  //for diagrams, remove the additional 'd' at the end of the layer id
3951  QString layerId = layerName;
3952  layerId.chop( 1 );
3953  mResults->mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), QString( "" ), layerId, QFont(), true, false );
3954  }
3955  continue;
3956  }
3957 
3958  const QgsPalLayerSettings& lyr = layer( layerName );
3959 
3960  // Copy to temp, editable layer settings
3961  // these settings will be changed by any data defined values, then used for rendering label components
3962  // settings may be adjusted during rendering of components
3963  QgsPalLayerSettings tmpLyr( lyr );
3964 
3965  // apply any previously applied data defined settings for the label
3966  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues = palGeometry->dataDefinedValues();
3967 
3968  //font
3969  QFont dFont = palGeometry->definedFont();
3970  // following debug is >= Qt 4.8 only ( because of QFont::styleName() )
3971 #if QT_VERSION >= 0x040800
3972  QgsDebugMsgLevel( QString( "PAL font tmpLyr: %1, Style: %2" ).arg( tmpLyr.textFont.toString() ).arg( QFontInfo( tmpLyr.textFont ).styleName() ), 4 );
3973  QgsDebugMsgLevel( QString( "PAL font definedFont: %1, Style: %2" ).arg( dFont.toString() ).arg( dFont.styleName() ), 4 );
3974 #endif
3975  tmpLyr.textFont = dFont;
3976 
3977  // update tmpLyr with any data defined text style values
3978  dataDefinedTextStyle( tmpLyr, ddValues );
3979 
3980  // update tmpLyr with any data defined text buffer values
3981  dataDefinedTextBuffer( tmpLyr, ddValues );
3982 
3983  // update tmpLyr with any data defined text formatting values
3984  dataDefinedTextFormatting( tmpLyr, ddValues );
3985 
3986  // update tmpLyr with any data defined shape background values
3987  dataDefinedShapeBackground( tmpLyr, ddValues );
3988 
3989  // update tmpLyr with any data defined drop shadow values
3990  dataDefinedDropShadow( tmpLyr, ddValues );
3991 
3992 
3994 
3995  // Render the components of a label in reverse order
3996  // (backgrounds -> text)
3997 
3998  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowLowest )
3999  {
4000  if ( tmpLyr.shapeDraw )
4001  {
4003  }
4004  else if ( tmpLyr.bufferDraw )
4005  {
4007  }
4008  else
4009  {
4011  }
4012  }
4013 
4014  if ( tmpLyr.shapeDraw )
4015  {
4016  drawLabel( *it, context, tmpLyr, LabelShape );
4017  }
4018 
4019  if ( tmpLyr.bufferDraw )
4020  {
4021  drawLabel( *it, context, tmpLyr, LabelBuffer );
4022  }
4023 
4024  drawLabel( *it, context, tmpLyr, LabelText );
4025 
4026  if ( mResults->mLabelSearchTree )
4027  {
4028  QString labeltext = (( QgsPalGeometry* )( *it )->getFeaturePart()->getUserGeometry() )->text();
4029  mResults->mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), layerName, labeltext, dFont, false, palGeometry->isPinned() );
4030  }
4031  }
4032 
4033  // Reset composition mode for further drawing operations
4034  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
4035 
4036  QgsDebugMsgLevel( QString( "LABELING draw: %1 ms" ).arg( t.elapsed() ), 4 );
4037 
4038  delete problem;
4039  delete labels;
4041 }
4042 
4044 {
4045  // delete all allocated geometries for features
4046  QHash<QString, QgsPalLayerSettings>::iterator lit;
4047  for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
4048  {
4049  QgsPalLayerSettings& lyr = lit.value();
4050  for ( QList<QgsPalGeometry*>::iterator git = lyr.geometries.begin(); git != lyr.geometries.end(); ++git )
4051  delete *git;
4052  if ( lyr.limitNumLabels )
4053  {
4054  QgsDebugMsgLevel( QString( "mFeaturesToLabel: %1" ).arg( lyr.mFeaturesToLabel ), 4 );
4055  QgsDebugMsgLevel( QString( "maxNumLabels: %1" ).arg( lyr.maxNumLabels ), 4 );
4056  QgsDebugMsgLevel( QString( "mFeatsSendingToPal: %1" ).arg( lyr.mFeatsSendingToPal ), 4 );
4057  QgsDebugMsgLevel( QString( "mFeatsRegPal: %1" ).arg( lyr.geometries.count() ), 4 );
4058  }
4059  lyr.geometries.clear();
4060  }
4061 
4062  //delete all allocated geometries for diagrams
4063  QHash<QString, QgsDiagramLayerSettings>::iterator dIt = mActiveDiagramLayers.begin();
4064  for ( ; dIt != mActiveDiagramLayers.end(); ++dIt )
4065  {
4066  QgsDiagramLayerSettings& dls = dIt.value();
4067  for ( QList<QgsPalGeometry*>::iterator git = dls.geometries.begin(); git != dls.geometries.end(); ++git )
4068  {
4069  delete *git;
4070  }
4071  dls.geometries.clear();
4072  }
4073 }
4074 
4075 QList<QgsLabelPosition> QgsPalLabeling::labelsAtPosition( const QgsPoint& p )
4076 {
4077  return mResults ? mResults->labelsAtPosition( p ) : QList<QgsLabelPosition>();
4078 }
4079 
4080 QList<QgsLabelPosition> QgsPalLabeling::labelsWithinRect( const QgsRectangle& r )
4081 {
4082  return mResults ? mResults->labelsWithinRect( r ) : QList<QgsLabelPosition>();
4083 }
4084 
4086 {
4087  if ( mResults )
4088  {
4090  mResults = 0;
4091  return tmp; // ownership passed to the caller
4092  }
4093  else
4094  return 0;
4095 }
4096 
4097 void QgsPalLabeling::numCandidatePositions( int& candPoint, int& candLine, int& candPolygon )
4098 {
4099  candPoint = mCandPoint;
4100  candLine = mCandLine;
4101  candPolygon = mCandPolygon;
4102 }
4103 
4104 void QgsPalLabeling::setNumCandidatePositions( int candPoint, int candLine, int candPolygon )
4105 {
4106  mCandPoint = candPoint;
4107  mCandLine = candLine;
4108  mCandPolygon = candPolygon;
4109 }
4110 
4112 {
4113  mSearch = s;
4114 }
4115 
4117 {
4118  return mSearch;
4119 }
4120 
4122 {
4123  QgsPoint outPt = xform->transform( lp->getX(), lp->getY() );
4124 
4125  painter->save();
4126 
4127 #if 1 // TODO: generalize some of this
4128  double w = lp->getWidth();
4129  double h = lp->getHeight();
4130  double cx = lp->getX() + w/2.0;
4131  double cy = lp->getY() + h/2.0;
4132  double scale = 1.0/xform->mapUnitsPerPixel();
4133  double rotation = xform->mapRotation();
4134  double sw = w * scale;
4135  double sh = h * scale;
4136  QRectF rect( -sw/2, -sh/2, sw, sh );
4137 
4138  painter->translate( xform->transform( QPointF(cx, cy) ).toQPointF() );
4139  if ( rotation ) {
4140  // Only if not horizontal
4141  if ( lp->getFeaturePart()->getLayer()->getArrangement() != P_HORIZ ) {
4142  painter->rotate( rotation );
4143  }
4144  }
4145  painter->translate( rect.bottomLeft() );
4146  painter->rotate( -lp->getAlpha() * 180 / M_PI );
4147  painter->translate( -rect.bottomLeft() );
4148 #else
4149  QgsPoint outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
4150  QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
4151  painter->translate( QPointF( outPt.x(), outPt.y() ) );
4152  painter->rotate( -lp->getAlpha() * 180 / M_PI );
4153 #endif
4154 
4155  painter->drawRect( rect );
4156  painter->restore();
4157 
4158  // save the rect
4159  rect.moveTo( outPt.x(), outPt.y() );
4160  mCandidates.append( QgsLabelCandidate( rect, lp->getCost() * 1000 ) );
4161 
4162  // show all parts of the multipart label
4163  if ( lp->getNextPart() )
4164  drawLabelCandidateRect( lp->getNextPart(), painter, xform );
4165 }
4166 
4167 void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QgsRenderContext& context, QgsPalLayerSettings& tmpLyr, DrawLabelType drawType, double dpiRatio )
4168 {
4169  // NOTE: this is repeatedly called for multi-part labels
4170  QPainter* painter = context.painter();
4171  const QgsMapToPixel* xform = &context.mapToPixel();
4172 
4173  QgsLabelComponent component;
4174  component.setDpiRatio( dpiRatio );
4175 
4176  QgsPoint outPt = xform->transform( label->getX(), label->getY() );
4177 // QgsPoint outPt2 = xform->transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() );
4178 // QRectF labelRect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
4179 
4180  component.setOrigin( outPt );
4181  component.setRotation( label->getAlpha() );
4182 
4183  if ( drawType == QgsPalLabeling::LabelShape )
4184  {
4185  // get rotated label's center point
4186  QgsPoint centerPt( outPt );
4187  QgsPoint outPt2 = xform->transform( label->getX() + label->getWidth() / 2,
4188  label->getY() + label->getHeight() / 2 );
4189 
4190  double xc = outPt2.x() - outPt.x();
4191  double yc = outPt2.y() - outPt.y();
4192 
4193  double angle = -label->getAlpha();
4194  double xd = xc * cos( angle ) - yc * sin( angle );
4195  double yd = xc * sin( angle ) + yc * cos( angle );
4196 
4197  centerPt.setX( centerPt.x() + xd );
4198  centerPt.setY( centerPt.y() + yd );
4199 
4200  component.setCenter( centerPt );
4201  component.setSize( QgsPoint( label->getWidth(), label->getHeight() ) );
4202 
4203  drawLabelBackground( context, component, tmpLyr );
4204  }
4205 
4206  else if ( drawType == QgsPalLabeling::LabelBuffer
4207  || drawType == QgsPalLabeling::LabelText )
4208  {
4209 
4210  // TODO: optimize access :)
4211  QString text = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->text();
4212  QString txt = ( label->getPartId() == -1 ? text : QString( text[label->getPartId()] ) );
4213  QFontMetricsF* labelfm = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->getLabelFontMetrics();
4214 
4215  QString wrapchr = !tmpLyr.wrapChar.isEmpty() ? tmpLyr.wrapChar : QString( "\n" );
4216 
4217  //add the direction symbol if needed
4218  if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line &&
4219  tmpLyr.addDirectionSymbol )
4220  {
4221  bool prependSymb = false;
4222  QString symb = tmpLyr.rightDirectionSymbol;
4223 
4224  if ( label->getReversed() )
4225  {
4226  prependSymb = true;
4227  symb = tmpLyr.leftDirectionSymbol;
4228  }
4229 
4230  if ( tmpLyr.reverseDirectionSymbol )
4231  {
4232  if ( symb == tmpLyr.rightDirectionSymbol )
4233  {
4234  prependSymb = true;
4235  symb = tmpLyr.leftDirectionSymbol;
4236  }
4237  else
4238  {
4239  prependSymb = false;
4240  symb = tmpLyr.rightDirectionSymbol;
4241  }
4242  }
4243 
4245  {
4246  prependSymb = true;
4247  symb = symb + wrapchr;
4248  }
4250  {
4251  prependSymb = false;
4252  symb = wrapchr + symb;
4253  }
4254 
4255  if ( prependSymb )
4256  {
4257  txt.prepend( symb );
4258  }
4259  else
4260  {
4261  txt.append( symb );
4262  }
4263  }
4264 
4265  //QgsDebugMsgLevel( "drawLabel " + txt, 4 );
4266 
4267  QStringList multiLineList = txt.split( wrapchr );
4268  int lines = multiLineList.size();
4269 
4270  double labelWidest = 0.0;
4271  for ( int i = 0; i < lines; ++i )
4272  {
4273  double labelWidth = labelfm->width( multiLineList.at( i ) );
4274  if ( labelWidth > labelWidest )
4275  {
4276  labelWidest = labelWidth;
4277  }
4278  }
4279 
4280  double labelHeight = labelfm->ascent() + labelfm->descent(); // ignore +1 for baseline
4281  // double labelHighest = labelfm->height() + ( double )(( lines - 1 ) * labelHeight * tmpLyr.multilineHeight );
4282 
4283  // needed to move bottom of text's descender to within bottom edge of label
4284  double ascentOffset = 0.25 * labelfm->ascent(); // labelfm->descent() is not enough
4285 
4286  for ( int i = 0; i < lines; ++i )
4287  {
4288  painter->save();
4289 #if 1 // TODO: generalize some of this
4290  LabelPosition* lp = label;
4291  double w = lp->getWidth();
4292  double h = lp->getHeight();
4293  double cx = lp->getX() + w/2.0;
4294  double cy = lp->getY() + h/2.0;
4295  double scale = 1.0/xform->mapUnitsPerPixel();
4296  double rotation = xform->mapRotation();
4297  double sw = w * scale;
4298  double sh = h * scale;
4299  QRectF rect( -sw/2, -sh/2, sw, sh );
4300  painter->translate( xform->transform( QPointF(cx, cy) ).toQPointF() );
4301  if ( rotation ) {
4302  // Only if not horizontal
4303  if ( lp->getFeaturePart()->getLayer()->getArrangement() != P_HORIZ ) {
4304  painter->rotate( rotation );
4305  }
4306  }
4307  painter->translate( rect.bottomLeft() );
4308  painter->rotate( -lp->getAlpha() * 180 / M_PI );
4309 #else
4310  painter->translate( QPointF( outPt.x(), outPt.y() ) );
4311  painter->rotate( -label->getAlpha() * 180 / M_PI );
4312 #endif
4313 
4314  // scale down painter: the font size has been multiplied by raster scale factor
4315  // to workaround a Qt font scaling bug with small font sizes
4316  painter->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
4317 
4318  // figure x offset for horizontal alignment of multiple lines
4319  double xMultiLineOffset = 0.0;
4320  double labelWidth = labelfm->width( multiLineList.at( i ) );
4321  if ( lines > 1 && tmpLyr.multilineAlign != QgsPalLayerSettings::MultiLeft )
4322  {
4323  double labelWidthDiff = labelWidest - labelWidth;
4325  {
4326  labelWidthDiff /= 2;
4327  }
4328  xMultiLineOffset = labelWidthDiff;
4329  //QgsDebugMsgLevel( QString( "xMultiLineOffset: %1" ).arg( xMultiLineOffset ), 4 );
4330  }
4331 
4332  double yMultiLineOffset = ( lines - 1 - i ) * labelHeight * tmpLyr.multilineHeight;
4333  painter->translate( QPointF( xMultiLineOffset, - ascentOffset - yMultiLineOffset ) );
4334 
4335  component.setText( multiLineList.at( i ) );
4336  component.setSize( QgsPoint( labelWidth, labelHeight ) );
4337  component.setOffset( QgsPoint( 0.0, -ascentOffset ) );
4338  component.setRotation( -component.rotation() * 180 / M_PI );
4339  component.setRotationOffset( 0.0 );
4340 
4341  if ( drawType == QgsPalLabeling::LabelBuffer )
4342  {
4343  // draw label's buffer
4344  drawLabelBuffer( context, component, tmpLyr );
4345  }
4346  else
4347  {
4348  // draw label's text, QPainterPath method
4349  QPainterPath path;
4350  path.addText( 0, 0, tmpLyr.textFont, component.text() );
4351 
4352  // store text's drawing in QPicture for drop shadow call
4353  QPicture textPict;
4354  QPainter textp;
4355  textp.begin( &textPict );
4356  textp.setPen( Qt::NoPen );
4357  textp.setBrush( tmpLyr.textColor );
4358  textp.drawPath( path );
4359  // TODO: why are some font settings lost on drawPicture() when using drawText() inside QPicture?
4360  // e.g. some capitalization options, but not others
4361  //textp.setFont( tmpLyr.textFont );
4362  //textp.setPen( tmpLyr.textColor );
4363  //textp.drawText( 0, 0, component.text() );
4364  textp.end();
4365 
4366  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowText )
4367  {
4368  component.setPicture( &textPict );
4369  component.setPictureBuffer( 0.0 ); // no pen width to deal with
4370  component.setOrigin( QgsPoint( 0.0, 0.0 ) );
4371 
4372  drawLabelShadow( context, component, tmpLyr );
4373  }
4374 
4375  // paint the text
4376  if ( context.useAdvancedEffects() )
4377  {
4378  painter->setCompositionMode( tmpLyr.blendMode );
4379  }
4380 
4381  // scale for any print output or image saving @ specific dpi
4382  painter->scale( component.dpiRatio(), component.dpiRatio() );
4383 
4384  if ( mDrawOutlineLabels )
4385  {
4386  // draw outlined text
4387  _fixQPictureDPI( painter );
4388  painter->drawPicture( 0, 0, textPict );
4389  }
4390  else
4391  {
4392  // draw text as text (for SVG and PDF exports)
4393  painter->setFont( tmpLyr.textFont );
4394  painter->setPen( tmpLyr.textColor );
4395  painter->setRenderHint( QPainter::TextAntialiasing );
4396  painter->drawText( 0, 0, component.text() );
4397  }
4398  }
4399  painter->restore();
4400  }
4401  }
4402 
4403  // NOTE: this used to be within above multi-line loop block, at end. (a mistake since 2010? [LS])
4404  if ( label->getNextPart() )
4405  drawLabel( label->getNextPart(), context, tmpLyr, drawType, dpiRatio );
4406 }
4407 
4409  QgsLabelComponent component,
4410  const QgsPalLayerSettings& tmpLyr )
4411 {
4412  QPainter* p = context.painter();
4413 
4414  double penSize = tmpLyr.scaleToPixelContext( tmpLyr.bufferSize, context,
4416 
4417  QPainterPath path;
4418  path.addText( 0, 0, tmpLyr.textFont, component.text() );
4419  QPen pen( tmpLyr.bufferColor );
4420  pen.setWidthF( penSize );
4421  pen.setJoinStyle( tmpLyr.bufferJoinStyle );
4422  QColor tmpColor( tmpLyr.bufferColor );
4423  // honor pref for whether to fill buffer interior
4424  if ( tmpLyr.bufferNoFill )
4425  {
4426  tmpColor.setAlpha( 0 );
4427  }
4428 
4429  // store buffer's drawing in QPicture for drop shadow call
4430  QPicture buffPict;
4431  QPainter buffp;
4432  buffp.begin( &buffPict );
4433  buffp.setPen( pen );
4434  buffp.setBrush( tmpColor );
4435  buffp.drawPath( path );
4436  buffp.end();
4437 
4438  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowBuffer )
4439  {
4440  component.setOrigin( QgsPoint( 0.0, 0.0 ) );
4441  component.setPicture( &buffPict );
4442  component.setPictureBuffer( penSize / 2.0 );
4443 
4444  drawLabelShadow( context, component, tmpLyr );
4445  }
4446 
4447  p->save();
4448  if ( context.useAdvancedEffects() )
4449  {
4450  p->setCompositionMode( tmpLyr.bufferBlendMode );
4451  }
4452 // p->setPen( pen );
4453 // p->setBrush( tmpColor );
4454 // p->drawPath( path );
4455 
4456  // scale for any print output or image saving @ specific dpi
4457  p->scale( component.dpiRatio(), component.dpiRatio() );
4458  _fixQPictureDPI( p );
4459  p->drawPicture( 0, 0, buffPict );
4460  p->restore();
4461 }
4462 
4464  QgsLabelComponent component,
4465  const QgsPalLayerSettings& tmpLyr )
4466 {
4467  QPainter* p = context.painter();
4468  double labelWidth = component.size().x(), labelHeight = component.size().y();
4469  //QgsDebugMsgLevel( QString( "Background label rotation: %1" ).arg( component.rotation() ), 4 );
4470 
4471  // shared calculations between shapes and SVG
4472 
4473  // configure angles, set component rotation and rotationOffset
4475  {
4476  component.setRotation( -( component.rotation() * 180 / M_PI ) ); // RotationSync
4477  component.setRotationOffset(
4479  }
4480  else // RotationFixed
4481  {
4482  component.setRotation( 0.0 ); // don't use label's rotation
4483  component.setRotationOffset( tmpLyr.shapeRotation );
4484  }
4485 
4486  // mm to map units conversion factor
4487  double mmToMapUnits = tmpLyr.shapeSizeMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor();
4488 
4489  // TODO: the following label-buffered generated shapes and SVG symbols should be moved into marker symbology classes
4490 
4491  if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeSVG )
4492  {
4493  // all calculations done in shapeSizeUnits, which are then passed to symbology class for painting
4494 
4495  if ( tmpLyr.shapeSVGFile.isEmpty() )
4496  return;
4497 
4498  double sizeOut = 0.0;
4499  // only one size used for SVG sizing/scaling (no use of shapeSize.y() or Y field in gui)
4501  {
4502  sizeOut = tmpLyr.shapeSize.x();
4503  }
4504  else if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeBuffer )
4505  {
4506  // add buffer to greatest dimension of label
4507  if ( labelWidth >= labelHeight )
4508  sizeOut = labelWidth;
4509  else if ( labelHeight > labelWidth )
4510  sizeOut = labelHeight;
4511 
4512  // label size in map units, convert to shapeSizeUnits, if different
4513  if ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM )
4514  {
4515  sizeOut /= mmToMapUnits;
4516  }
4517 
4518  // add buffer
4519  sizeOut += tmpLyr.shapeSize.x() * 2;
4520  }
4521 
4522  // don't bother rendering symbols smaller than 1x1 pixels in size
4523  // TODO: add option to not show any svgs under/over a certian size
4524  if ( tmpLyr.scaleToPixelContext( sizeOut, context, tmpLyr.shapeSizeUnits, false, tmpLyr.shapeSizeMapUnitScale ) < 1.0 )
4525  return;
4526 
4527  QgsStringMap map; // for SVG symbology marker
4528  map["name"] = QgsSymbolLayerV2Utils::symbolNameToPath( tmpLyr.shapeSVGFile.trimmed() );
4529  map["size"] = QString::number( sizeOut );
4530  map["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
4532  map["angle"] = QString::number( 0.0 ); // angle is handled by this local painter
4533 
4534  // offset is handled by this local painter
4535  // TODO: see why the marker renderer doesn't seem to translate offset *after* applying rotation
4536  //map["offset"] = QgsSymbolLayerV2Utils::encodePoint( tmpLyr.shapeOffset );
4537  //map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
4538  // tmpLyr.shapeOffsetUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
4539 
4540  map["fill"] = tmpLyr.shapeFillColor.name();
4541  map["outline"] = tmpLyr.shapeBorderColor.name();
4542  map["outline-width"] = QString::number( tmpLyr.shapeBorderWidth );
4543 
4544  // TODO: fix overriding SVG symbol's border width/units in QgsSvgCache
4545  // currently broken, fall back to symbol's
4546  //map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
4547  // tmpLyr.shapeBorderWidthUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
4548 
4549  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowShape )
4550  {
4551  // configure SVG shadow specs
4552  QgsStringMap shdwmap( map );
4553  shdwmap["fill"] = tmpLyr.shadowColor.name();
4554  shdwmap["outline"] = tmpLyr.shadowColor.name();
4555  shdwmap["size"] = QString::number( sizeOut * tmpLyr.rasterCompressFactor );
4556 
4557  // store SVG's drawing in QPicture for drop shadow call
4558  QPicture svgPict;
4559  QPainter svgp;
4560  svgp.begin( &svgPict );
4561 
4562  // draw shadow symbol
4563 
4564  // clone current render context map unit/mm conversion factors, but not
4565  // other map canvas parameters, then substitute this painter for use in symbology painting
4566  // NOTE: this is because the shadow needs to be scaled correctly for output to map canvas,
4567  // but will be created relative to the SVG's computed size, not the current map canvas
4568  QgsRenderContext shdwContext;
4569  shdwContext.setMapToPixel( context.mapToPixel() );
4570  shdwContext.setScaleFactor( context.scaleFactor() );
4571  shdwContext.setPainter( &svgp );
4572 
4573  QgsSymbolLayerV2* symShdwL = QgsSvgMarkerSymbolLayerV2::create( shdwmap );
4574  QgsSvgMarkerSymbolLayerV2* svgShdwM = static_cast<QgsSvgMarkerSymbolLayerV2*>( symShdwL );
4575  QgsSymbolV2RenderContext svgShdwContext( shdwContext, QgsSymbolV2::Mixed,
4576  ( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
4577 
4578  double svgSize = tmpLyr.scaleToPixelContext( sizeOut, context, tmpLyr.shapeSizeUnits, true, tmpLyr.shapeSizeMapUnitScale );
4579  svgShdwM->renderPoint( QPointF( svgSize / 2, -svgSize / 2 ), svgShdwContext );
4580  svgp.end();
4581 
4582  component.setPicture( &svgPict );
4583  // TODO: when SVG symbol's border width/units is fixed in QgsSvgCache, adjust for it here
4584  component.setPictureBuffer( 0.0 );
4585 
4586  component.setSize( QgsPoint( svgSize, svgSize ) );
4587  component.setOffset( QgsPoint( 0.0, 0.0 ) );
4588 
4589  // rotate about origin center of SVG
4590  p->save();
4591  p->translate( component.center().x(), component.center().y() );
4592  p->rotate( component.rotation() );
4593  p->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
4594  double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits, true, tmpLyr.shapeOffsetMapUnitScale );
4595  double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits, true, tmpLyr.shapeOffsetMapUnitScale );
4596  p->translate( QPointF( xoff, yoff ) );
4597  p->rotate( component.rotationOffset() );
4598  p->translate( -svgSize / 2, svgSize / 2 );
4599 
4600  drawLabelShadow( context, component, tmpLyr );
4601  p->restore();
4602 
4603  delete svgShdwM;
4604  svgShdwM = 0;
4605  }
4606 
4607  // draw the actual symbol
4609  QgsSvgMarkerSymbolLayerV2* svgM = static_cast<QgsSvgMarkerSymbolLayerV2*>( symL );
4610  QgsSymbolV2RenderContext svgContext( context, QgsSymbolV2::Mixed,
4611  ( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
4612 
4613  p->save();
4614  if ( context.useAdvancedEffects() )
4615  {
4616  p->setCompositionMode( tmpLyr.shapeBlendMode );
4617  }
4618  p->translate( component.center().x(), component.center().y() );
4619  p->rotate( component.rotation() );
4620  double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits, false, tmpLyr.shapeOffsetMapUnitScale );
4621  double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits, false, tmpLyr.shapeOffsetMapUnitScale );
4622  p->translate( QPointF( xoff, yoff ) );
4623  p->rotate( component.rotationOffset() );
4624  svgM->renderPoint( QPointF( 0, 0 ), svgContext );
4625  p->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
4626  p->restore();
4627 
4628  delete svgM;
4629  svgM = 0;
4630 
4631  }
4632  else // Generated Shapes
4633  {
4634  // all calculations done in shapeSizeUnits
4635 
4636  double w = labelWidth / ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM ? mmToMapUnits : 1 );
4637  double h = labelHeight / ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM ? mmToMapUnits : 1 );
4638 
4639  double xsize = tmpLyr.shapeSize.x();
4640  double ysize = tmpLyr.shapeSize.y();
4641 
4643  {
4644  w = xsize;
4645  h = ysize;
4646  }
4647  else if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeBuffer )
4648  {
4650  {
4651  if ( w > h )
4652  h = w;
4653  else if ( h > w )
4654  w = h;
4655  }
4656  else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeCircle )
4657  {
4658  // start with label bound by circle
4659  h = sqrt( pow( w, 2 ) + pow( h, 2 ) );
4660  w = h;
4661  }
4662  else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeEllipse )
4663  {
4664  // start with label bound by ellipse
4665  h = h / sqrt( 2.0 ) * 2;
4666  w = w / sqrt( 2.0 ) * 2;
4667  }
4668 
4669  w += xsize * 2;
4670  h += ysize * 2;
4671  }
4672 
4673  // convert everything over to map pixels from here on
4674  w = tmpLyr.scaleToPixelContext( w, context, tmpLyr.shapeSizeUnits, true, tmpLyr.shapeSizeMapUnitScale );
4675  h = tmpLyr.scaleToPixelContext( h, context, tmpLyr.shapeSizeUnits, true, tmpLyr.shapeSizeMapUnitScale );
4676 
4677  // offsets match those of symbology: -x = left, -y = up
4678  QRectF rect( -w / 2.0, - h / 2.0, w, h );
4679 
4680  if ( rect.isNull() )
4681  return;
4682 
4683  p->save();
4684  p->translate( QPointF( component.center().x(), component.center().y() ) );
4685  p->rotate( component.rotation() );
4686  double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits, false, tmpLyr.shapeOffsetMapUnitScale );
4687  double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits, false, tmpLyr.shapeOffsetMapUnitScale );
4688  p->translate( QPointF( xoff, yoff ) );
4689  p->rotate( component.rotationOffset() );
4690 
4691  double penSize = tmpLyr.scaleToPixelContext( tmpLyr.shapeBorderWidth, context, tmpLyr.shapeBorderWidthUnits, true, tmpLyr.shapeBorderWidthMapUnitScale );
4692 
4693  QPen pen;
4694  if ( tmpLyr.shapeBorderWidth > 0 )
4695  {
4696  pen.setColor( tmpLyr.shapeBorderColor );
4697  pen.setWidthF( penSize );
4699  pen.setJoinStyle( tmpLyr.shapeJoinStyle );
4700  }
4701  else
4702  {
4703  pen = Qt::NoPen;
4704  }
4705 
4706  // store painting in QPicture for shadow drawing
4707  QPicture shapePict;
4708  QPainter shapep;
4709  shapep.begin( &shapePict );
4710  shapep.setPen( pen );
4711  shapep.setBrush( tmpLyr.shapeFillColor );
4712 
4715  {
4717  {
4718  shapep.drawRoundedRect( rect, tmpLyr.shapeRadii.x(), tmpLyr.shapeRadii.y(), Qt::RelativeSize );
4719  }
4720  else
4721  {
4722  double xRadius = tmpLyr.scaleToPixelContext( tmpLyr.shapeRadii.x(), context, tmpLyr.shapeRadiiUnits, true, tmpLyr.shapeRadiiMapUnitScale );
4723  double yRadius = tmpLyr.scaleToPixelContext( tmpLyr.shapeRadii.y(), context, tmpLyr.shapeRadiiUnits, true, tmpLyr.shapeRadiiMapUnitScale );
4724  shapep.drawRoundedRect( rect, xRadius, yRadius );
4725  }
4726  }
4727  else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeEllipse
4729  {
4730  shapep.drawEllipse( rect );
4731  }
4732  shapep.end();
4733 
4734  p->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
4735 
4736  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowShape )
4737  {
4738  component.setPicture( &shapePict );
4739  component.setPictureBuffer( penSize / 2.0 );
4740 
4741  component.setSize( QgsPoint( rect.width(), rect.height() ) );
4742  component.setOffset( QgsPoint( rect.width() / 2, -rect.height() / 2 ) );
4743  drawLabelShadow( context, component, tmpLyr );
4744  }
4745 
4746  p->setOpacity(( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
4747  if ( context.useAdvancedEffects() )
4748  {
4749  p->setCompositionMode( tmpLyr.shapeBlendMode );
4750  }
4751 
4752  // scale for any print output or image saving @ specific dpi
4753  p->scale( component.dpiRatio(), component.dpiRatio() );
4754  _fixQPictureDPI( p );
4755  p->drawPicture( 0, 0, shapePict );
4756  p->restore();
4757  }
4758 }
4759 
4761  QgsLabelComponent component,
4762  const QgsPalLayerSettings& tmpLyr )
4763 {
4764  // incoming component sizes should be multiplied by rasterCompressFactor, as
4765  // this allows shadows to be created at paint device dpi (e.g. high resolution),
4766  // then scale device painter by 1.0 / rasterCompressFactor for output
4767 
4768  QPainter* p = context.painter();
4769  double componentWidth = component.size().x(), componentHeight = component.size().y();
4770  double xOffset = component.offset().x(), yOffset = component.offset().y();
4771  double pictbuffer = component.pictureBuffer();
4772 
4773  // generate pixmap representation of label component drawing
4774  bool mapUnits = ( tmpLyr.shadowRadiusUnits == QgsPalLayerSettings::MapUnits );
4775  double radius = tmpLyr.scaleToPixelContext( tmpLyr.shadowRadius, context, tmpLyr.shadowRadiusUnits, !mapUnits, tmpLyr.shadowRadiusMapUnitScale );
4776  radius /= ( mapUnits ? tmpLyr.vectorScaleFactor / component.dpiRatio() : 1 );
4777  radius = ( int )( radius + 0.5 );
4778 
4779  // TODO: add labeling gui option to adjust blurBufferClippingScale to minimize pixels, or
4780  // to ensure shadow isn't clipped too tight. (Or, find a better method of buffering)
4781  double blurBufferClippingScale = 3.75;
4782  int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
4783 
4784  QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
4785  componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
4786  QImage::Format_ARGB32_Premultiplied );
4787 
4788  // TODO: add labeling gui option to not show any shadows under/over a certian size
4789  // keep very small QImages from causing paint device issues, i.e. must be at least > 1
4790  int minBlurImgSize = 1;
4791  // max limitation on QgsSvgCache is 10,000 for screen, which will probably be reasonable for future caching here, too
4792  // 4 x QgsSvgCache limit for output to print/image at higher dpi
4793  // TODO: should it be higher, scale with dpi, or have no limit? Needs testing with very large labels rendered at high dpi output
4794  int maxBlurImgSize = 40000;
4795  if ( blurImg.isNull()
4796  || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
4797  || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
4798  return;
4799 
4800  blurImg.fill( QColor( Qt::transparent ).rgba() );
4801  QPainter pictp;
4802  if ( !pictp.begin( &blurImg ) )
4803  return;
4804  pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
4805  QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
4806  blurbuffer + pictbuffer + componentHeight + yOffset );
4807 
4808  pictp.drawPicture( imgOffset,
4809  *component.picture() );
4810 
4811  // overlay shadow color
4812  pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
4813  pictp.fillRect( blurImg.rect(), tmpLyr.shadowColor );
4814  pictp.end();
4815 
4816  // blur the QImage in-place
4817  if ( tmpLyr.shadowRadius > 0.0 && radius > 0 )
4818  {
4819  QgsSymbolLayerV2Utils::blurImageInPlace( blurImg, blurImg.rect(), radius, tmpLyr.shadowRadiusAlphaOnly );
4820  }
4821 
4822  if ( tmpLyr.showingShadowRects ) // engine setting, not per layer
4823  {
4824  // debug rect for QImage shadow registration and clipping visualization
4825  QPainter picti;
4826  picti.begin( &blurImg );
4827  picti.setBrush( Qt::Dense7Pattern );
4828  QPen imgPen( QColor( 0, 0, 255, 255 ) );
4829  imgPen.setWidth( 1 );
4830  picti.setPen( imgPen );
4831  picti.setOpacity( 0.1 );
4832  picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
4833  picti.end();
4834  }
4835 
4836  double offsetDist = tmpLyr.scaleToPixelContext( tmpLyr.shadowOffsetDist, context, tmpLyr.shadowOffsetUnits, true, tmpLyr.shadowOffsetMapUnitScale );
4837  double angleRad = tmpLyr.shadowOffsetAngle * M_PI / 180; // to radians
4838  if ( tmpLyr.shadowOffsetGlobal )
4839  {
4840  // TODO: check for differences in rotation origin and cw/ccw direction,
4841  // when this shadow function is used for something other than labels
4842 
4843  // it's 0-->cw-->360 for labels
4844  //QgsDebugMsgLevel( QString( "Shadow aggregated label rotation (degrees): %1" ).arg( component.rotation() + component.rotationOffset() ), 4 );
4845  angleRad -= ( component.rotation() * M_PI / 180 + component.rotationOffset() * M_PI / 180 );
4846  }
4847 
4848  QPointF transPt( -offsetDist * cos( angleRad + M_PI / 2 ),
4849  -offsetDist * sin( angleRad + M_PI / 2 ) );
4850 
4851  p->save();
4852  p->setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
4853  if ( context.useAdvancedEffects() )
4854  {
4855  p->setCompositionMode( tmpLyr.shadowBlendMode );
4856  }
4857  p->setOpacity(( 100.0 - ( double )( tmpLyr.shadowTransparency ) ) / 100.0 );
4858 
4859  double scale = ( double )tmpLyr.shadowScale / 100.0;
4860  // TODO: scale from center/center, left/center or left/top, instead of default left/bottom?
4861  p->scale( scale, scale );
4862  if ( component.useOrigin() )
4863  {
4864  p->translate( component.origin().x(), component.origin().y() );
4865  }
4866  p->translate( transPt );
4867  p->translate( -imgOffset.x(),
4868  -imgOffset.y() );
4869  p->drawImage( 0, 0, blurImg );
4870  p->restore();
4871 
4872  // debug rects
4873  if ( tmpLyr.showingShadowRects ) // engine setting, not per layer
4874  {
4875  // draw debug rect for QImage painting registration
4876  p->save();
4877  p->setBrush( Qt::NoBrush );
4878  QPen imgPen( QColor( 255, 0, 0, 10 ) );
4879  imgPen.setWidth( 2 );
4880  imgPen.setStyle( Qt::DashLine );
4881  p->setPen( imgPen );
4882  p->scale( scale, scale );
4883  if ( component.useOrigin() )
4884  {
4885  p->translate( component.origin().x(), component.origin().y() );
4886  }
4887  p->translate( transPt );
4888  p->translate( -imgOffset.x(),
4889  -imgOffset.y() );
4890  p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
4891  p->restore();
4892 
4893  // draw debug rect for passed in component dimensions
4894  p->save();
4895  p->setBrush( Qt::NoBrush );
4896  QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
4897  componentRectPen.setWidth( 1 );
4898  if ( component.useOrigin() )
4899  {
4900  p->translate( component.origin().x(), component.origin().y() );
4901  }
4902  p->setPen( componentRectPen );
4903  p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
4904  p->restore();
4905  }
4906 }
4907 
4909 {
4910  // start with engine defaults for new project, or project that has no saved settings
4911  Pal p;
4912  bool saved = false;
4914  "PAL", "/SearchMethod", ( int )p.getSearch(), &saved ) );
4916  "PAL", "/CandidatesPoint", p.getPointP(), &saved );
4918  "PAL", "/CandidatesLine", p.getLineP(), &saved );
4920  "PAL", "/CandidatesPolygon", p.getPolyP(), &saved );
4922  "PAL", "/ShowingCandidates", false, &saved );
4924  "PAL", "/ShowingShadowRects", false, &saved );
4926  "PAL", "/ShowingAllLabels", false, &saved );
4928  "PAL", "/ShowingPartialsLabels", p.getShowPartial(), &saved );
4930  "PAL", "/DrawOutlineLabels", true, &saved );
4931 }
4932 
4934 {
4935  QgsProject::instance()->writeEntry( "PAL", "/SearchMethod", ( int )mSearch );
4936  QgsProject::instance()->writeEntry( "PAL", "/CandidatesPoint", mCandPoint );
4937  QgsProject::instance()->writeEntry( "PAL", "/CandidatesLine", mCandLine );
4938  QgsProject::instance()->writeEntry( "PAL", "/CandidatesPolygon", mCandPolygon );
4939  QgsProject::instance()->writeEntry( "PAL", "/ShowingCandidates", mShowingCandidates );
4940  QgsProject::instance()->writeEntry( "PAL", "/ShowingShadowRects", mShowingShadowRects );
4941  QgsProject::instance()->writeEntry( "PAL", "/ShowingAllLabels", mShowingAllLabels );
4942  QgsProject::instance()->writeEntry( "PAL", "/ShowingPartialsLabels", mShowingPartialsLabels );
4943  QgsProject::instance()->writeEntry( "PAL", "/DrawOutlineLabels", mDrawOutlineLabels );
4944 }
4945 
4947 {
4948  QgsProject::instance()->removeEntry( "PAL", "/SearchMethod" );
4949  QgsProject::instance()->removeEntry( "PAL", "/CandidatesPoint" );
4950  QgsProject::instance()->removeEntry( "PAL", "/CandidatesLine" );
4951  QgsProject::instance()->removeEntry( "PAL", "/CandidatesPolygon" );
4952  QgsProject::instance()->removeEntry( "PAL", "/ShowingCandidates" );
4953  QgsProject::instance()->removeEntry( "PAL", "/ShowingShadowRects" );
4954  QgsProject::instance()->removeEntry( "PAL", "/ShowingAllLabels" );
4955  QgsProject::instance()->removeEntry( "PAL", "/ShowingPartialsLabels" );
4956  QgsProject::instance()->removeEntry( "PAL", "/DrawOutlineLabels" );
4957 }
4958 
4960 {
4961  QgsPalLabeling* lbl = new QgsPalLabeling();
4967  return lbl;
4968 }
4969 
4970 
4972 {
4973  mLabelSearchTree = new QgsLabelSearchTree();
4974 }
4975 
4977 {
4978  delete mLabelSearchTree;
4979  mLabelSearchTree = NULL;
4980 }
4981 
4982 QList<QgsLabelPosition> QgsLabelingResults::labelsAtPosition( const QgsPoint& p ) const
4983 {
4984  QList<QgsLabelPosition> positions;
4985 
4986  QList<QgsLabelPosition*> positionPointers;
4987  if ( mLabelSearchTree )
4988  {
4989  mLabelSearchTree->label( p, positionPointers );
4990  QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
4991  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
4992  {
4993  positions.push_back( QgsLabelPosition( **pointerIt ) );
4994  }
4995  }
4996 
4997  return positions;
4998 }
4999 
5000 QList<QgsLabelPosition> QgsLabelingResults::labelsWithinRect( const QgsRectangle& r ) const
5001 {
5002  QList<QgsLabelPosition> positions;
5003 
5004  QList<QgsLabelPosition*> positionPointers;
5005  if ( mLabelSearchTree )
5006  {
5007  mLabelSearchTree->labelsInRect( r, positionPointers );
5008  QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
5009  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
5010  {
5011  positions.push_back( QgsLabelPosition( **pointerIt ) );
5012  }
5013  }
5014 
5015  return positions;
5016 }
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
QgsPalLayerSettings & layer(const QString &layerName)
returns PAL layer settings for a registered layer
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
virtual Q_DECL_DEPRECATED QList< QgsLabelPosition > labelsWithinRect(const QgsRectangle &r)
return infos about labels within a given (map) rectangle
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
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
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
virtual void clearActiveLayer(const QString &layerID)
clears data defined objects from PAL layer settings for a registered layer
void setSearchMethod(Search s)
virtual Q_DECL_DEPRECATED void init(QgsMapRenderer *mr)
called when we're going to start with rendering
void loadEngineSettings()
load/save engine settings to project file
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 Q_DECL_DEPRECATED QList< QgsLabelPosition > labelsAtPosition(const QgsPoint &p)
return infos about labels at a given (map) position
Pal main class.
Definition: pal.h:126
void numCandidatePositions(int &candPoint, int &candLine, int &candPolygon)
static bool _palIsCancelled(void *ctx)
UpsideDownLabels
Definition: layer.h:78
bool hasCrsTransformEnabled() const
returns true if projections are enabled for this layer set
static QPointF decodePoint(QString str)
Container of fields for a vector layer.
Definition: qgsfield.h:172
static QgsPalLayerSettings fromLayer(QgsVectorLayer *layer)
void readFromLayer(QgsVectorLayer *layer)
void setUpsidedownLabels(UpsideDownLabels ud)
Definition: layer.h:296
static QColor decodeColor(QString str)
QString expressionString() const
GeometryType
Definition: qgis.h:155
const QgsRectangle & extent() const
const QgsMapToPixel & mapToPixel() const
virtual void exit()
called when we're done with rendering