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