QGIS API Documentation  2.9.0-Master
qgspallabeling.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspallabeling.cpp
3  Smart labeling for vector layers
4  -------------------
5  begin : June 2009
6  copyright : (C) Martin Dobias
7  email : wonder dot sk at gmail dot com
8 
9  ***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgspallabeling.h"
19 #include "qgspalgeometry.h"
20 
21 #include <list>
22 
23 #include <pal/pal.h>
24 #include <pal/feature.h>
25 #include <pal/layer.h>
26 #include <pal/palgeometry.h>
27 #include <pal/palexception.h>
28 #include <pal/problem.h>
29 #include <pal/labelposition.h>
30 
31 #include <geos_c.h>
32 
33 #include <cmath>
34 
35 #include <QApplication>
36 #include <QByteArray>
37 #include <QString>
38 #include <QFontMetrics>
39 #include <QTime>
40 #include <QPainter>
41 
42 #include "diagram/qgsdiagram.h"
43 #include "qgsdiagramrendererv2.h"
44 #include "qgsfontutils.h"
45 #include "qgslabelsearchtree.h"
46 #include "qgsexpression.h"
47 #include "qgsdatadefined.h"
48 
49 #include <qgslogger.h>
50 #include <qgsvectorlayer.h>
51 #include <qgsmaplayerregistry.h>
52 #include <qgsvectordataprovider.h>
53 #include <qgsgeometry.h>
54 #include <qgsmaprenderer.h>
55 #include <qgsmarkersymbollayerv2.h>
56 #include <qgsproject.h>
57 #include "qgssymbolv2.h"
58 #include "qgssymbollayerv2utils.h"
59 #include <QMessageBox>
60 
61 
62 Q_GUI_EXPORT extern int qt_defaultDpiX();
63 Q_GUI_EXPORT extern int qt_defaultDpiY();
64 
65 static void _fixQPictureDPI( QPainter* p )
66 {
67  // QPicture makes an assumption that we drawing to it with system DPI.
68  // Then when being drawn, it scales the painter. The following call
69  // negates the effect. There is no way of setting QPicture's DPI.
70  // See QTBUG-20361
71  p->scale(( double )qt_defaultDpiX() / p->device()->logicalDpiX(),
72  ( double )qt_defaultDpiY() / p->device()->logicalDpiY() );
73 }
74 
75 
76 using namespace pal;
77 
78 // -------------
79 
81  : upsidedownLabels( Upright )
82  , palLayer( NULL )
83  , mCurFeat( 0 )
84  , mCurFields( 0 )
85  , xform( NULL )
86  , ct( NULL )
87  , extentGeom( NULL )
88  , mFeaturesToLabel( 0 )
89  , mFeatsSendingToPal( 0 )
90  , mFeatsRegPal( 0 )
91  , expression( 0 )
92 {
93  enabled = false;
94  isExpression = false;
95  fieldIndex = 0;
96 
97  // text style
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 + QString( "\n" ) ); // SymbolAbove or SymbolBelow
1409  }
1410  }
1411 
1412  double w = 0.0, h = 0.0;
1413  QStringList multiLineSplit = QgsPalLabeling::splitToLines( text, 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  const QgsGeometry* geom = f.constGeometry();
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 ( !qgsDoubleNear( repeatDist, 0.0 ) )
2131  {
2132  if ( !repeatdistinmapunit )
2133  {
2134  repeatDist *= mapUntsPerMM; //convert repeat distance from mm to map units
2135  }
2136  }
2137 
2138  // feature to the layer
2139  try
2140  {
2141  if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(),
2142  xPos, yPos, dataDefinedPosition, angle, dataDefinedRotation,
2143  quadOffsetX, quadOffsetY, offsetX, offsetY, alwaysShow, repeatDist ) )
2144  return;
2145  }
2146  catch ( std::exception &e )
2147  {
2148  Q_UNUSED( e );
2149  QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( f.id() ) + QString::fromLatin1( e.what() ), 4 );
2150  return;
2151  }
2152 
2153  // TODO: only for placement which needs character info
2154  pal::Feature* feat = palLayer->getFeature( lbl->strId() );
2155  // account for any data defined font metrics adjustments
2156  feat->setLabelInfo( lbl->info( labelFontMetrics, xform, rasterCompressFactor, maxcharanglein, maxcharangleout ) );
2157  delete labelFontMetrics;
2158 
2159  // TODO: allow layer-wide feature dist in PAL...?
2160 
2161  // data defined label-feature distance?
2162  double distance = dist;
2164  {
2165  bool ok;
2166  double distD = exprVal.toDouble( &ok );
2167  if ( ok )
2168  {
2169  distance = distD;
2170  }
2171  }
2172 
2173  // data defined label-feature distance units?
2174  bool distinmapunit = distInMapUnits;
2176  {
2177  QString units = exprVal.toString().trimmed();
2178  QgsDebugMsgLevel( QString( "exprVal DistanceUnits:%1" ).arg( units ), 4 );
2179  if ( !units.isEmpty() )
2180  {
2181  distinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
2182  }
2183  }
2184 
2185  if ( distance != 0 )
2186  {
2187  if ( distinmapunit ) //convert distance from mm/map units to pixels
2188  {
2189  distance /= distMapUnitScale.computeMapUnitsPerPixel( context );
2190  }
2191  else //mm
2192  {
2193  distance *= vectorScaleFactor;
2194  }
2195  feat->setDistLabel( qAbs( ptOne.x() - ptZero.x() )* distance );
2196  }
2197 
2198 
2199  //add parameters for data defined labeling to QgsPalGeometry
2200  QMap< DataDefinedProperties, QVariant >::const_iterator dIt = dataDefinedValues.constBegin();
2201  for ( ; dIt != dataDefinedValues.constEnd(); ++dIt )
2202  {
2203  lbl->addDataDefinedValue( dIt.key(), dIt.value() );
2204  }
2205 
2206  // set geometry's pinned property
2207  lbl->setIsPinned( labelIsPinned );
2208 }
2209 
2210 bool QgsPalLayerSettings::dataDefinedValEval( const QString& valType,
2212  QVariant& exprVal )
2213 {
2214  if ( dataDefinedEvaluate( p, exprVal ) )
2215  {
2216  QString dbgStr = QString( "exprVal %1:" ).arg( mDataDefinedNames.value( p ).first ) + "%1";
2217 
2218  if ( valType == QString( "bool" ) )
2219  {
2220  bool bol = exprVal.toBool();
2221  QgsDebugMsgLevel( dbgStr.arg( bol ? "true" : "false" ), 4 );
2222  dataDefinedValues.insert( p, QVariant( bol ) );
2223  return true;
2224  }
2225  if ( valType == QString( "int" ) )
2226  {
2227  bool ok;
2228  int size = exprVal.toInt( &ok );
2229  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2230 
2231  if ( ok )
2232  {
2233  dataDefinedValues.insert( p, QVariant( size ) );
2234  return true;
2235  }
2236  }
2237  if ( valType == QString( "intpos" ) )
2238  {
2239  bool ok;
2240  int size = exprVal.toInt( &ok );
2241  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2242 
2243  if ( ok && size > 0 )
2244  {
2245  dataDefinedValues.insert( p, QVariant( size ) );
2246  return true;
2247  }
2248  }
2249  if ( valType == QString( "double" ) )
2250  {
2251  bool ok;
2252  double size = exprVal.toDouble( &ok );
2253  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2254 
2255  if ( ok )
2256  {
2257  dataDefinedValues.insert( p, QVariant( size ) );
2258  return true;
2259  }
2260  }
2261  if ( valType == QString( "doublepos" ) )
2262  {
2263  bool ok;
2264  double size = exprVal.toDouble( &ok );
2265  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2266 
2267  if ( ok && size > 0.0 )
2268  {
2269  dataDefinedValues.insert( p, QVariant( size ) );
2270  return true;
2271  }
2272  }
2273  if ( valType == QString( "rotation180" ) )
2274  {
2275  bool ok;
2276  double rot = exprVal.toDouble( &ok );
2277  QgsDebugMsgLevel( dbgStr.arg( rot ), 4 );
2278  if ( ok )
2279  {
2280  if ( rot < -180.0 && rot >= -360 )
2281  {
2282  rot += 360;
2283  }
2284  if ( rot > 180.0 && rot <= 360 )
2285  {
2286  rot -= 360;
2287  }
2288  if ( rot >= -180 && rot <= 180 )
2289  {
2290  dataDefinedValues.insert( p, QVariant( rot ) );
2291  return true;
2292  }
2293  }
2294  }
2295  if ( valType == QString( "transp" ) )
2296  {
2297  bool ok;
2298  int size = exprVal.toInt( &ok );
2299  QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
2300  if ( ok && size >= 0 && size <= 100 )
2301  {
2302  dataDefinedValues.insert( p, QVariant( size ) );
2303  return true;
2304  }
2305  }
2306  if ( valType == QString( "string" ) )
2307  {
2308  QString str = exprVal.toString(); // don't trim whitespace
2309  QgsDebugMsgLevel( dbgStr.arg( str ), 4 );
2310 
2311  dataDefinedValues.insert( p, QVariant( str ) ); // let it stay empty if it is
2312  return true;
2313  }
2314  if ( valType == QString( "units" ) )
2315  {
2316  QString unitstr = exprVal.toString().trimmed();
2317  QgsDebugMsgLevel( dbgStr.arg( unitstr ), 4 );
2318 
2319  if ( !unitstr.isEmpty() )
2320  {
2321  dataDefinedValues.insert( p, QVariant(( int )_decodeUnits( unitstr ) ) );
2322  return true;
2323  }
2324  }
2325  if ( valType == QString( "color" ) )
2326  {
2327  QString colorstr = exprVal.toString().trimmed();
2328  QgsDebugMsgLevel( dbgStr.arg( colorstr ), 4 );
2329  QColor color = QgsSymbolLayerV2Utils::decodeColor( colorstr );
2330 
2331  if ( color.isValid() )
2332  {
2333  dataDefinedValues.insert( p, QVariant( color ) );
2334  return true;
2335  }
2336  }
2337  if ( valType == QString( "joinstyle" ) )
2338  {
2339  QString joinstr = exprVal.toString().trimmed();
2340  QgsDebugMsgLevel( dbgStr.arg( joinstr ), 4 );
2341 
2342  if ( !joinstr.isEmpty() )
2343  {
2344  dataDefinedValues.insert( p, QVariant(( int )_decodePenJoinStyle( joinstr ) ) );
2345  return true;
2346  }
2347  }
2348  if ( valType == QString( "blendmode" ) )
2349  {
2350  QString blendstr = exprVal.toString().trimmed();
2351  QgsDebugMsgLevel( dbgStr.arg( blendstr ), 4 );
2352 
2353  if ( !blendstr.isEmpty() )
2354  {
2355  dataDefinedValues.insert( p, QVariant(( int )QgsSymbolLayerV2Utils::decodeBlendMode( blendstr ) ) );
2356  return true;
2357  }
2358  }
2359  if ( valType == QString( "pointf" ) )
2360  {
2361  QString ptstr = exprVal.toString().trimmed();
2362  QgsDebugMsgLevel( dbgStr.arg( ptstr ), 4 );
2363 
2364  if ( !ptstr.isEmpty() )
2365  {
2366  dataDefinedValues.insert( p, QVariant( QgsSymbolLayerV2Utils::decodePoint( ptstr ) ) );
2367  return true;
2368  }
2369  }
2370  }
2371  return false;
2372 }
2373 
2374 void QgsPalLayerSettings::parseTextStyle( QFont& labelFont,
2376  const QgsRenderContext& context )
2377 {
2378  // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
2379 
2380  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2381 
2382  // Two ways to generate new data defined font:
2383  // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
2384  // 2) Family + named style (bold or italic is ignored)
2385 
2386  // data defined font family?
2387  QString ddFontFamily( "" );
2389  {
2390  QString family = exprVal.toString().trimmed();
2391  QgsDebugMsgLevel( QString( "exprVal Font family:%1" ).arg( family ), 4 );
2392 
2393  if ( labelFont.family() != family )
2394  {
2395  // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
2396  // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
2397  if ( QgsFontUtils::fontFamilyOnSystem( family ) )
2398  {
2399  ddFontFamily = family;
2400  }
2401  }
2402  }
2403 
2404  // data defined named font style?
2405  QString ddFontStyle( "" );
2407  {
2408  QString fontstyle = exprVal.toString().trimmed();
2409  QgsDebugMsgLevel( QString( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
2410  ddFontStyle = fontstyle;
2411  }
2412 
2413  // data defined bold font style?
2414  bool ddBold = false;
2416  {
2417  bool bold = exprVal.toBool();
2418  QgsDebugMsgLevel( QString( "exprVal Font bold:%1" ).arg( bold ? "true" : "false" ), 4 );
2419  ddBold = bold;
2420  }
2421 
2422  // data defined italic font style?
2423  bool ddItalic = false;
2425  {
2426  bool italic = exprVal.toBool();
2427  QgsDebugMsgLevel( QString( "exprVal Font italic:%1" ).arg( italic ? "true" : "false" ), 4 );
2428  ddItalic = italic;
2429  }
2430 
2431  // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
2432  // (currently defaults to what has been read in from layer settings)
2433  QFont newFont;
2434  QFont appFont = QApplication::font();
2435  bool newFontBuilt = false;
2436  if ( ddBold || ddItalic )
2437  {
2438  // new font needs built, since existing style needs removed
2439  newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
2440  newFontBuilt = true;
2441  newFont.setBold( ddBold );
2442  newFont.setItalic( ddItalic );
2443  }
2444  else if ( !ddFontStyle.isEmpty()
2445  && ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
2446  {
2447  if ( !ddFontFamily.isEmpty() )
2448  {
2449  // both family and style are different, build font from database
2450  QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
2451  if ( appFont != styledfont )
2452  {
2453  newFont = styledfont;
2454  newFontBuilt = true;
2455  }
2456  }
2457 
2458  // update the font face style
2459  QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
2460  }
2461  else if ( !ddFontFamily.isEmpty() )
2462  {
2463  if ( ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
2464  {
2465  // just family is different, build font from database
2466  QFont styledfont = mFontDB.font( ddFontFamily, textNamedStyle, appFont.pointSize() );
2467  if ( appFont != styledfont )
2468  {
2469  newFont = styledfont;
2470  newFontBuilt = true;
2471  }
2472  }
2473  else
2474  {
2475  newFont = QFont( ddFontFamily );
2476  newFontBuilt = true;
2477  }
2478  }
2479 
2480  if ( newFontBuilt )
2481  {
2482  // copy over existing font settings
2483  //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
2484  newFont.setPixelSize( labelFont.pixelSize() );
2485  newFont.setCapitalization( labelFont.capitalization() );
2486  newFont.setUnderline( labelFont.underline() );
2487  newFont.setStrikeOut( labelFont.strikeOut() );
2488  newFont.setWordSpacing( labelFont.wordSpacing() );
2489  newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
2490 
2491  labelFont = newFont;
2492  }
2493 
2494  // data defined word spacing?
2495  double wordspace = labelFont.wordSpacing();
2497  {
2498  bool ok;
2499  double wspacing = exprVal.toDouble( &ok );
2500  QgsDebugMsgLevel( QString( "exprVal FontWordSpacing:%1" ).arg( wspacing ), 4 );
2501  if ( ok )
2502  {
2503  wordspace = wspacing;
2504  }
2505  }
2506  labelFont.setWordSpacing( sizeToPixel( wordspace, context, fontunits, false, fontSizeMapUnitScale ) );
2507 
2508  // data defined letter spacing?
2509  double letterspace = labelFont.letterSpacing();
2511  {
2512  bool ok;
2513  double lspacing = exprVal.toDouble( &ok );
2514  QgsDebugMsgLevel( QString( "exprVal FontLetterSpacing:%1" ).arg( lspacing ), 4 );
2515  if ( ok )
2516  {
2517  letterspace = lspacing;
2518  }
2519  }
2520  labelFont.setLetterSpacing( QFont::AbsoluteSpacing, sizeToPixel( letterspace, context, fontunits, false, fontSizeMapUnitScale ) );
2521 
2522  // data defined font capitalization?
2523  QFont::Capitalization fontcaps = labelFont.capitalization();
2525  {
2526  QString fcase = exprVal.toString().trimmed();
2527  QgsDebugMsgLevel( QString( "exprVal FontCase:%1" ).arg( fcase ), 4 );
2528 
2529  if ( !fcase.isEmpty() )
2530  {
2531  if ( fcase.compare( "NoChange", Qt::CaseInsensitive ) == 0 )
2532  {
2533  fontcaps = QFont::MixedCase;
2534  }
2535  else if ( fcase.compare( "Upper", Qt::CaseInsensitive ) == 0 )
2536  {
2537  fontcaps = QFont::AllUppercase;
2538  }
2539  else if ( fcase.compare( "Lower", Qt::CaseInsensitive ) == 0 )
2540  {
2541  fontcaps = QFont::AllLowercase;
2542  }
2543  else if ( fcase.compare( "Capitalize", Qt::CaseInsensitive ) == 0 )
2544  {
2545  fontcaps = QFont::Capitalize;
2546  }
2547 
2548  if ( fontcaps != labelFont.capitalization() )
2549  {
2550  labelFont.setCapitalization( fontcaps );
2551  }
2552  }
2553  }
2554 
2555  // data defined strikeout font style?
2557  {
2558  bool strikeout = exprVal.toBool();
2559  QgsDebugMsgLevel( QString( "exprVal Font strikeout:%1" ).arg( strikeout ? "true" : "false" ), 4 );
2560  labelFont.setStrikeOut( strikeout );
2561  }
2562 
2563  // data defined underline font style?
2565  {
2566  bool underline = exprVal.toBool();
2567  QgsDebugMsgLevel( QString( "exprVal Font underline:%1" ).arg( underline ? "true" : "false" ), 4 );
2568  labelFont.setUnderline( underline );
2569  }
2570 
2571  // pass the rest on to QgsPalLabeling::drawLabeling
2572 
2573  // data defined font color?
2574  dataDefinedValEval( "color", QgsPalLayerSettings::Color, exprVal );
2575 
2576  // data defined font transparency?
2577  dataDefinedValEval( "transp", QgsPalLayerSettings::FontTransp, exprVal );
2578 
2579  // data defined font blend mode?
2580  dataDefinedValEval( "blendmode", QgsPalLayerSettings::FontBlendMode, exprVal );
2581 
2582 }
2583 
2584 void QgsPalLayerSettings::parseTextBuffer()
2585 {
2586  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2587 
2588  // data defined draw buffer?
2589  bool drawBuffer = bufferDraw;
2590  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::BufferDraw, exprVal ) )
2591  {
2592  drawBuffer = exprVal.toBool();
2593  }
2594 
2595  if ( !drawBuffer )
2596  {
2597  return;
2598  }
2599 
2600  // data defined buffer size?
2601  double bufrSize = bufferSize;
2602  if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::BufferSize, exprVal ) )
2603  {
2604  bufrSize = exprVal.toDouble();
2605  }
2606 
2607  // data defined buffer transparency?
2608  int bufTransp = bufferTransp;
2609  if ( dataDefinedValEval( "transp", QgsPalLayerSettings::BufferTransp, exprVal ) )
2610  {
2611  bufTransp = exprVal.toInt();
2612  }
2613 
2614  drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufTransp < 100 );
2615 
2616  if ( !drawBuffer )
2617  {
2618  dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
2619  dataDefinedValues.remove( QgsPalLayerSettings::BufferSize );
2620  dataDefinedValues.remove( QgsPalLayerSettings::BufferTransp );
2621  return; // don't bother evaluating values that won't be used
2622  }
2623 
2624  // data defined buffer units?
2625  dataDefinedValEval( "units", QgsPalLayerSettings::BufferUnit, exprVal );
2626 
2627  // data defined buffer color?
2628  dataDefinedValEval( "color", QgsPalLayerSettings::BufferColor, exprVal );
2629 
2630  // data defined buffer pen join style?
2631  dataDefinedValEval( "joinstyle", QgsPalLayerSettings::BufferJoinStyle, exprVal );
2632 
2633  // data defined buffer blend mode?
2634  dataDefinedValEval( "blendmode", QgsPalLayerSettings::BufferBlendMode, exprVal );
2635 }
2636 
2637 void QgsPalLayerSettings::parseTextFormatting()
2638 {
2639  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2640 
2641  // data defined multiline wrap character?
2642  QString wrapchr = wrapChar;
2643  if ( dataDefinedValEval( "string", QgsPalLayerSettings::MultiLineWrapChar, exprVal ) )
2644  {
2645  wrapchr = exprVal.toString();
2646  }
2647 
2648  // data defined multiline height?
2649  dataDefinedValEval( "double", QgsPalLayerSettings::MultiLineHeight, exprVal );
2650 
2651  // data defined multiline text align?
2653  {
2654  QString str = exprVal.toString().trimmed();
2655  QgsDebugMsgLevel( QString( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
2656 
2657  if ( !str.isEmpty() )
2658  {
2659  // "Left"
2661 
2662  if ( str.compare( "Center", Qt::CaseInsensitive ) == 0 )
2663  {
2665  }
2666  else if ( str.compare( "Right", Qt::CaseInsensitive ) == 0 )
2667  {
2668  aligntype = QgsPalLayerSettings::MultiRight;
2669  }
2670  else if ( str.compare( "Follow", Qt::CaseInsensitive ) == 0 )
2671  {
2673  }
2674  dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant(( int )aligntype ) );
2675  }
2676  }
2677 
2678  // data defined direction symbol?
2679  bool drawDirSymb = addDirectionSymbol;
2680  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::DirSymbDraw, exprVal ) )
2681  {
2682  drawDirSymb = exprVal.toBool();
2683  }
2684 
2685  if ( drawDirSymb )
2686  {
2687  // data defined direction left symbol?
2688  dataDefinedValEval( "string", QgsPalLayerSettings::DirSymbLeft, exprVal );
2689 
2690  // data defined direction right symbol?
2691  dataDefinedValEval( "string", QgsPalLayerSettings::DirSymbRight, exprVal );
2692 
2693  // data defined direction symbol placement?
2695  {
2696  QString str = exprVal.toString().trimmed();
2697  QgsDebugMsgLevel( QString( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
2698 
2699  if ( !str.isEmpty() )
2700  {
2701  // "LeftRight"
2703 
2704  if ( str.compare( "Above", Qt::CaseInsensitive ) == 0 )
2705  {
2707  }
2708  else if ( str.compare( "Below", Qt::CaseInsensitive ) == 0 )
2709  {
2711  }
2712  dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant(( int )placetype ) );
2713  }
2714  }
2715 
2716  // data defined direction symbol reversed?
2717  dataDefinedValEval( "bool", QgsPalLayerSettings::DirSymbReverse, exprVal );
2718  }
2719 
2720  // formatting for numbers is inline with generation of base label text and not passed to label painting
2721 }
2722 
2723 void QgsPalLayerSettings::parseShapeBackground()
2724 {
2725  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2726 
2727  // data defined draw shape?
2728  bool drawShape = shapeDraw;
2729  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::ShapeDraw, exprVal ) )
2730  {
2731  drawShape = exprVal.toBool();
2732  }
2733 
2734  if ( !drawShape )
2735  {
2736  return;
2737  }
2738 
2739  // data defined shape transparency?
2740  int shapeTransp = shapeTransparency;
2741  if ( dataDefinedValEval( "transp", QgsPalLayerSettings::ShapeTransparency, exprVal ) )
2742  {
2743  shapeTransp = exprVal.toInt();
2744  }
2745 
2746  drawShape = ( drawShape && shapeTransp < 100 ); // size is not taken into account (could be)
2747 
2748  if ( !drawShape )
2749  {
2750  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
2751  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
2752  return; // don't bother evaluating values that won't be used
2753  }
2754 
2755  // data defined shape kind?
2758  {
2759  QString skind = exprVal.toString().trimmed();
2760  QgsDebugMsgLevel( QString( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
2761 
2762  if ( !skind.isEmpty() )
2763  {
2764  // "Rectangle"
2766 
2767  if ( skind.compare( "Square", Qt::CaseInsensitive ) == 0 )
2768  {
2770  }
2771  else if ( skind.compare( "Ellipse", Qt::CaseInsensitive ) == 0 )
2772  {
2774  }
2775  else if ( skind.compare( "Circle", Qt::CaseInsensitive ) == 0 )
2776  {
2778  }
2779  else if ( skind.compare( "SVG", Qt::CaseInsensitive ) == 0 )
2780  {
2782  }
2783  shapeKind = shpkind;
2784  dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant(( int )shpkind ) );
2785  }
2786  }
2787 
2788  // data defined shape SVG path?
2789  QString svgPath = shapeSVGFile;
2791  {
2792  QString svgfile = exprVal.toString().trimmed();
2793  QgsDebugMsgLevel( QString( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
2794 
2795  // '' empty paths are allowed
2796  svgPath = svgfile;
2797  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgfile ) );
2798  }
2799 
2800  // data defined shape size type?
2803  {
2804  QString stype = exprVal.toString().trimmed();
2805  QgsDebugMsgLevel( QString( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
2806 
2807  if ( !stype.isEmpty() )
2808  {
2809  // "Buffer"
2811 
2812  if ( stype.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
2813  {
2815  }
2816  shpSizeType = sizType;
2817  dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant(( int )sizType ) );
2818  }
2819  }
2820 
2821  // data defined shape size X? (SVGs only use X for sizing)
2822  double ddShpSizeX = shapeSize.x();
2823  if ( dataDefinedValEval( "double", QgsPalLayerSettings::ShapeSizeX, exprVal ) )
2824  {
2825  ddShpSizeX = exprVal.toDouble();
2826  }
2827 
2828  // data defined shape size Y?
2829  double ddShpSizeY = shapeSize.y();
2830  if ( dataDefinedValEval( "double", QgsPalLayerSettings::ShapeSizeY, exprVal ) )
2831  {
2832  ddShpSizeY = exprVal.toDouble();
2833  }
2834 
2835  // don't continue under certain circumstances (e.g. size is fixed)
2836  bool skip = false;
2837  if ( shapeKind == QgsPalLayerSettings::ShapeSVG
2838  && ( svgPath.isEmpty()
2839  || ( !svgPath.isEmpty()
2840  && shpSizeType == QgsPalLayerSettings::SizeFixed
2841  && ddShpSizeX == 0.0 ) ) )
2842  {
2843  skip = true;
2844  }
2845  if ( shapeKind != QgsPalLayerSettings::ShapeSVG
2846  && shpSizeType == QgsPalLayerSettings::SizeFixed
2847  && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
2848  {
2849  skip = true;
2850  }
2851 
2852  if ( skip )
2853  {
2854  dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
2855  dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
2856  dataDefinedValues.remove( QgsPalLayerSettings::ShapeKind );
2857  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSVGFile );
2858  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeX );
2859  dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeY );
2860  return; // don't bother evaluating values that won't be used
2861  }
2862 
2863  // data defined shape size units?
2864  dataDefinedValEval( "units", QgsPalLayerSettings::ShapeSizeUnits, exprVal );
2865 
2866  // data defined shape rotation type?
2868  {
2869  QString rotstr = exprVal.toString().trimmed();
2870  QgsDebugMsgLevel( QString( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
2871 
2872  if ( !rotstr.isEmpty() )
2873  {
2874  // "Sync"
2876 
2877  if ( rotstr.compare( "Offset", Qt::CaseInsensitive ) == 0 )
2878  {
2880  }
2881  else if ( rotstr.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
2882  {
2884  }
2885  dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant(( int )rottype ) );
2886  }
2887  }
2888 
2889  // data defined shape rotation?
2890  dataDefinedValEval( "rotation180", QgsPalLayerSettings::ShapeRotation, exprVal );
2891 
2892  // data defined shape offset?
2893  dataDefinedValEval( "pointf", QgsPalLayerSettings::ShapeOffset, exprVal );
2894 
2895  // data defined shape offset units?
2896  dataDefinedValEval( "units", QgsPalLayerSettings::ShapeOffsetUnits, exprVal );
2897 
2898  // data defined shape radii?
2899  dataDefinedValEval( "pointf", QgsPalLayerSettings::ShapeRadii, exprVal );
2900 
2901  // data defined shape radii units?
2902  dataDefinedValEval( "units", QgsPalLayerSettings::ShapeRadiiUnits, exprVal );
2903 
2904  // data defined shape blend mode?
2905  dataDefinedValEval( "blendmode", QgsPalLayerSettings::ShapeBlendMode, exprVal );
2906 
2907  // data defined shape fill color?
2908  dataDefinedValEval( "color", QgsPalLayerSettings::ShapeFillColor, exprVal );
2909 
2910  // data defined shape border color?
2911  dataDefinedValEval( "color", QgsPalLayerSettings::ShapeBorderColor, exprVal );
2912 
2913  // data defined shape border width?
2914  dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShapeBorderWidth, exprVal );
2915 
2916  // data defined shape border width units?
2917  dataDefinedValEval( "units", QgsPalLayerSettings::ShapeBorderWidthUnits, exprVal );
2918 
2919  // data defined shape join style?
2920  dataDefinedValEval( "joinstyle", QgsPalLayerSettings::ShapeJoinStyle, exprVal );
2921 
2922 }
2923 
2924 void QgsPalLayerSettings::parseDropShadow()
2925 {
2926  QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
2927 
2928  // data defined draw shadow?
2929  bool drawShadow = shadowDraw;
2930  if ( dataDefinedValEval( "bool", QgsPalLayerSettings::ShadowDraw, exprVal ) )
2931  {
2932  drawShadow = exprVal.toBool();
2933  }
2934 
2935  if ( !drawShadow )
2936  {
2937  return;
2938  }
2939 
2940  // data defined shadow transparency?
2941  int shadowTransp = shadowTransparency;
2942  if ( dataDefinedValEval( "transp", QgsPalLayerSettings::ShadowTransparency, exprVal ) )
2943  {
2944  shadowTransp = exprVal.toInt();
2945  }
2946 
2947  // data defined shadow offset distance?
2948  double shadowOffDist = shadowOffsetDist;
2949  if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShadowOffsetDist, exprVal ) )
2950  {
2951  shadowOffDist = exprVal.toDouble();
2952  }
2953 
2954  // data defined shadow offset distance?
2955  double shadowRad = shadowRadius;
2956  if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShadowRadius, exprVal ) )
2957  {
2958  shadowRad = exprVal.toDouble();
2959  }
2960 
2961  drawShadow = ( drawShadow && shadowTransp < 100 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
2962 
2963  if ( !drawShadow )
2964  {
2965  dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
2966  dataDefinedValues.remove( QgsPalLayerSettings::ShadowTransparency );
2967  dataDefinedValues.remove( QgsPalLayerSettings::ShadowOffsetDist );
2968  dataDefinedValues.remove( QgsPalLayerSettings::ShadowRadius );
2969  return; // don't bother evaluating values that won't be used
2970  }
2971 
2972  // data defined shadow under type?
2974  {
2975  QString str = exprVal.toString().trimmed();
2976  QgsDebugMsgLevel( QString( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
2977 
2978  if ( !str.isEmpty() )
2979  {
2980  // "Lowest"
2982 
2983  if ( str.compare( "Text", Qt::CaseInsensitive ) == 0 )
2984  {
2986  }
2987  else if ( str.compare( "Buffer", Qt::CaseInsensitive ) == 0 )
2988  {
2990  }
2991  else if ( str.compare( "Background", Qt::CaseInsensitive ) == 0 )
2992  {
2994  }
2995  dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant(( int )shdwtype ) );
2996  }
2997  }
2998 
2999  // data defined shadow offset angle?
3000  dataDefinedValEval( "rotation180", QgsPalLayerSettings::ShadowOffsetAngle, exprVal );
3001 
3002  // data defined shadow offset units?
3003  dataDefinedValEval( "units", QgsPalLayerSettings::ShadowOffsetUnits, exprVal );
3004 
3005  // data defined shadow radius?
3006  dataDefinedValEval( "double", QgsPalLayerSettings::ShadowRadius, exprVal );
3007 
3008  // data defined shadow radius units?
3009  dataDefinedValEval( "units", QgsPalLayerSettings::ShadowRadiusUnits, exprVal );
3010 
3011  // data defined shadow scale? ( gui bounds to 0-2000, no upper bound here )
3012  dataDefinedValEval( "intpos", QgsPalLayerSettings::ShadowScale, exprVal );
3013 
3014  // data defined shadow color?
3015  dataDefinedValEval( "color", QgsPalLayerSettings::ShadowColor, exprVal );
3016 
3017  // data defined shadow blend mode?
3018  dataDefinedValEval( "blendmode", QgsPalLayerSettings::ShadowBlendMode, exprVal );
3019 }
3020 
3021 int QgsPalLayerSettings::sizeToPixel( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor, const QgsMapUnitScale& mapUnitScale ) const
3022 {
3023  return ( int )( scaleToPixelContext( size, c, unit, rasterfactor, mapUnitScale ) + 0.5 );
3024 }
3025 
3026 double QgsPalLayerSettings::scaleToPixelContext( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor, const QgsMapUnitScale& mapUnitScale ) const
3027 {
3028  // if render context is that of device (i.e. not a scaled map), just return size
3029  double mapUnitsPerPixel = mapUnitScale.computeMapUnitsPerPixel( c );
3030 
3031  if ( unit == MapUnits && mapUnitsPerPixel > 0.0 )
3032  {
3033  size = size / mapUnitsPerPixel * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3034  }
3035  else // e.g. in points or mm
3036  {
3037  double ptsTomm = ( unit == Points ? 0.352778 : 1 );
3038  size *= ptsTomm * c.scaleFactor() * ( rasterfactor ? c.rasterScaleFactor() : 1 );
3039  }
3040  return size;
3041 }
3042 
3043 // -------------
3044 
3046  : mMapSettings( NULL ), mPal( NULL )
3047  , mResults( 0 )
3048 {
3049 
3050  // find out engine defaults
3051  Pal p;
3052  mCandPoint = p.getPointP();
3053  mCandLine = p.getLineP();
3054  mCandPolygon = p.getPolyP();
3055 
3056  switch ( p.getSearch() )
3057  {
3058  case CHAIN: mSearch = Chain; break;
3059  case POPMUSIC_TABU: mSearch = Popmusic_Tabu; break;
3060  case POPMUSIC_CHAIN: mSearch = Popmusic_Chain; break;
3062  case FALP: mSearch = Falp; break;
3063  }
3064 
3065  mShowingCandidates = false;
3066  mShowingShadowRects = false;
3067  mShowingAllLabels = false;
3069  mDrawOutlineLabels = true;
3070 }
3071 
3073 {
3074  // make sure we've freed everything
3075  exit();
3076 
3078 
3079  delete mResults;
3080  mResults = 0;
3081 }
3082 
3084 {
3085  return staticWillUseLayer( layer );
3086 }
3087 
3088 bool QgsPalLabeling::staticWillUseLayer( const QString& layerID )
3089 {
3090  QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( layerID ) );
3091  if ( !layer )
3092  return false;
3093  return staticWillUseLayer( layer );
3094 }
3095 
3096 
3098 {
3099  // don't do QgsPalLayerSettings::readFromLayer( layer ) if not needed
3100  bool enabled = false;
3101  if ( layer->customProperty( "labeling" ).toString() == QString( "pal" ) )
3102  enabled = layer->labelsEnabled() || layer->diagramsEnabled();
3103 
3104  return enabled;
3105 }
3106 
3107 
3109 {
3110  QHash<QString, QgsPalLayerSettings>::iterator lit;
3111  for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
3112  {
3113  clearActiveLayer( lit.key() );
3114  }
3115  mActiveLayers.clear();
3116 }
3117 
3118 void QgsPalLabeling::clearActiveLayer( const QString &layerID )
3119 {
3120  QgsPalLayerSettings& lyr = mActiveLayers[layerID];
3121 
3122  // delete all QgsDataDefined objects (which also deletes their QgsExpression object)
3123  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::iterator it = lyr.dataDefinedProperties.begin();
3124  for ( ; it != lyr.dataDefinedProperties.constEnd(); ++it )
3125  {
3126  delete( it.value() );
3127  it.value() = 0;
3128  }
3129  lyr.dataDefinedProperties.clear();
3130 }
3131 
3132 int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames, QgsRenderContext& ctx )
3133 {
3134  Q_ASSERT( mMapSettings != NULL );
3135 
3136  if ( !willUseLayer( layer ) || !layer->labelsEnabled() )
3137  {
3138  return 0;
3139  }
3140 
3141  QgsDebugMsgLevel( "PREPARE LAYER " + layer->id(), 4 );
3142 
3143  // start with a temporary settings class, find out labeling info
3144  QgsPalLayerSettings lyrTmp;
3145  lyrTmp.readFromLayer( layer );
3146 
3147  if ( lyrTmp.fieldName.isEmpty() )
3148  {
3149  return 0;
3150  }
3151 
3152  if ( lyrTmp.isExpression )
3153  {
3154  QgsExpression exp( lyrTmp.fieldName );
3155  if ( exp.hasEvalError() )
3156  {
3157  QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 );
3158  return 0;
3159  }
3160  }
3161  else
3162  {
3163  // If we aren't an expression, we check to see if we can find the column.
3164  if ( layer->fieldNameIndex( lyrTmp.fieldName ) == -1 )
3165  {
3166  return 0;
3167  }
3168  }
3169 
3170  // add layer settings to the pallabeling hashtable: <QgsVectorLayer*, QgsPalLayerSettings>
3171  mActiveLayers.insert( layer->id(), lyrTmp );
3172  // start using the reference to the layer in hashtable instead of local instance
3173  QgsPalLayerSettings& lyr = mActiveLayers[layer->id()];
3174 
3175  lyr.mCurFields = &( layer->pendingFields() );
3176 
3177  // add field indices for label's text, from expression or field
3178  if ( lyr.isExpression )
3179  {
3180  // prepare expression for use in QgsPalLayerSettings::registerFeature()
3181  QgsExpression* exp = lyr.getLabelExpression();
3182  exp->prepare( layer->pendingFields() );
3183  if ( exp->hasEvalError() )
3184  {
3185  QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 );
3186  }
3187  foreach ( QString name, exp->referencedColumns() )
3188  {
3189  QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 );
3190  attrNames.append( name );
3191  }
3192  }
3193  else
3194  {
3195  attrNames.append( lyr.fieldName );
3196  }
3197 
3198  // add field indices of data defined expression or field
3199  QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator dIt = lyr.dataDefinedProperties.constBegin();
3200  for ( ; dIt != lyr.dataDefinedProperties.constEnd(); ++dIt )
3201  {
3202  QgsDataDefined* dd = dIt.value();
3203  if ( !dd->isActive() )
3204  {
3205  continue;
3206  }
3207 
3208  // NOTE: the following also prepares any expressions for later use
3209 
3210  // store parameters for data defined expressions
3211  QMap<QString, QVariant> exprParams;
3212  exprParams.insert( "scale", ctx.rendererScale() );
3213 
3214  dd->setExpressionParams( exprParams );
3215 
3216  // this will return columns for expressions or field name, depending upon what is set to be used
3217  QStringList cols = dd->referencedColumns( layer ); // <-- prepares any expressions, too
3218 
3219  //QgsDebugMsgLevel( QString( "Data defined referenced columns:" ) + cols.join( "," ), 4 );
3220  foreach ( QString name, cols )
3221  {
3222  attrNames.append( name );
3223  }
3224  }
3225 
3226  // how to place the labels
3227  Arrangement arrangement;
3228  switch ( lyr.placement )
3229  {
3230  case QgsPalLayerSettings::AroundPoint: arrangement = P_POINT; break;
3231  case QgsPalLayerSettings::OverPoint: arrangement = P_POINT_OVER; break;
3232  case QgsPalLayerSettings::Line: arrangement = P_LINE; break;
3233  case QgsPalLayerSettings::Curved: arrangement = P_CURVED; break;
3234  case QgsPalLayerSettings::Horizontal: arrangement = P_HORIZ; break;
3235  case QgsPalLayerSettings::Free: arrangement = P_FREE; break;
3236  default: Q_ASSERT( "unsupported placement" && 0 ); return 0;
3237  }
3238 
3239  // create the pal layer
3240  double priority = 1 - lyr.priority / 10.0; // convert 0..10 --> 1..0
3241  double min_scale = -1, max_scale = -1;
3242 
3243  // handled in QgsPalLayerSettings::registerFeature now
3244  //if ( lyr.scaleVisibility && !lyr.dataDefinedIsActive( QgsPalLayerSettings::ScaleVisibility ) )
3245  //{
3246  // min_scale = lyr.scaleMin;
3247  // max_scale = lyr.scaleMax;
3248  //}
3249 
3250  Layer* l = mPal->addLayer( layer->id().toUtf8().data(),
3251  min_scale, max_scale, arrangement,
3252  METER, priority, lyr.obstacle, true, true,
3253  lyr.displayAll );
3254 
3255  if ( lyr.placementFlags )
3257 
3258  // set label mode (label per feature is the default)
3259  l->setLabelMode( lyr.labelPerPart ? Layer::LabelPerFeaturePart : Layer::LabelPerFeature );
3260 
3261  // set whether adjacent lines should be merged
3263 
3264 
3265  // set whether location of centroid must be inside of polygons
3267 
3268  // set how to show upside-down labels
3269  Layer::UpsideDownLabels upsdnlabels;
3270  switch ( lyr.upsidedownLabels )
3271  {
3272  case QgsPalLayerSettings::Upright: upsdnlabels = Layer::Upright; break;
3273  case QgsPalLayerSettings::ShowDefined: upsdnlabels = Layer::ShowDefined; break;
3274  case QgsPalLayerSettings::ShowAll: upsdnlabels = Layer::ShowAll; break;
3275  default: Q_ASSERT( "unsupported upside-down label setting" && 0 ); return 0;
3276  }
3277  l->setUpsidedownLabels( upsdnlabels );
3278 
3279 // // fix for font size in map units causing font to show pointsize at small map scales
3280 // int pixelFontSize = lyr.sizeToPixel( lyr.textFont.pointSizeF(), ctx,
3281 // lyr.fontSizeInMapUnits ? QgsPalLayerSettings::MapUnits : QgsPalLayerSettings::Points,
3282 // true );
3283 
3284 // if ( pixelFontSize < 1 )
3285 // {
3286 // lyr.textFont.setPointSize( 1 );
3287 // lyr.textFont.setPixelSize( 1 );
3288 // }
3289 // else
3290 // {
3291 // lyr.textFont.setPixelSize( pixelFontSize );
3292 // }
3293 
3294 // // scale spacing sizes if using map units
3295 // if ( lyr.fontSizeInMapUnits )
3296 // {
3297 // double spacingPixelSize;
3298 // if ( lyr.textFont.wordSpacing() != 0 )
3299 // {
3300 // spacingPixelSize = lyr.textFont.wordSpacing() / ctx.mapToPixel().mapUnitsPerPixel() * ctx.rasterScaleFactor();
3301 // lyr.textFont.setWordSpacing( spacingPixelSize );
3302 // }
3303 
3304 // if ( lyr.textFont.letterSpacing() != 0 )
3305 // {
3306 // spacingPixelSize = lyr.textFont.letterSpacing() / ctx.mapToPixel().mapUnitsPerPixel() * ctx.rasterScaleFactor();
3307 // lyr.textFont.setLetterSpacing( QFont::AbsoluteSpacing, spacingPixelSize );
3308 // }
3309 // }
3310 
3311  //raster and vector scale factors
3312  lyr.vectorScaleFactor = ctx.scaleFactor();
3314 
3315  // save the pal layer to our layer context (with some additional info)
3316  lyr.palLayer = l;
3317  lyr.fieldIndex = layer->fieldNameIndex( lyr.fieldName );
3318 
3319  lyr.xform = &mMapSettings->mapToPixel();
3320  lyr.ct = 0;
3322  lyr.ct = ctx.coordinateTransform()->clone();
3323  lyr.ptZero = lyr.xform->toMapCoordinates( 0, 0 );
3324  lyr.ptOne = lyr.xform->toMapCoordinates( 1, 0 );
3325 
3326  // rect for clipping
3327  lyr.extentGeom = QgsGeometry::fromRect( mMapSettings->visibleExtent() );
3328 
3329  lyr.mFeatsSendingToPal = 0;
3330 
3331  return 1; // init successful
3332 }
3333 
3335 {
3336  double priority = 1 - s->priority / 10.0; // convert 0..10 --> 1..0
3337  Layer* l = mPal->addLayer( layer->id().append( "d" ).toUtf8().data(), -1, -1, pal::Arrangement( s->placement ), METER, priority, s->obstacle, true, true );
3339 
3340  mActiveDiagramLayers.insert( layer->id(), *s );
3341  // initialize the local copy
3343 
3344  s2.palLayer = l;
3345  s2.ct = 0;
3347  s2.ct = new QgsCoordinateTransform( layer->crs(), mMapSettings->destinationCrs() );
3348 
3349  s2.xform = &mMapSettings->mapToPixel();
3350 
3351  s2.fields = layer->pendingFields();
3352 
3353  s2.renderer = layer->diagramRenderer()->clone();
3354 
3355  return 1;
3356 }
3357 
3358 void QgsPalLabeling::registerFeature( const QString& layerID, QgsFeature& f, const QgsRenderContext& context, QString dxfLayer )
3359 {
3360  QgsPalLayerSettings& lyr = mActiveLayers[layerID];
3361  lyr.registerFeature( f, context, dxfLayer );
3362 }
3363 
3364 bool QgsPalLabeling::geometryRequiresPreparation( const QgsGeometry* geometry, const QgsRenderContext& context, const QgsCoordinateTransform* ct, QgsGeometry* clipGeometry )
3365 {
3366  if ( !geometry )
3367  {
3368  return false;
3369  }
3370 
3371  //requires reprojection
3372  if ( ct )
3373  return true;
3374 
3375  //requires fixing
3376  if ( geometry->type() == QGis::Polygon && !geometry->isGeosValid() )
3377  return true;
3378 
3379  //requires rotation
3380  const QgsMapToPixel& m2p = context.mapToPixel();
3381  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
3382  return true;
3383 
3384  //requires clip
3385  if ( clipGeometry && !clipGeometry->contains( geometry ) )
3386  return true;
3387 
3388  return false;
3389 }
3390 
3391 QStringList QgsPalLabeling::splitToLines( const QString &text, const QString &wrapCharacter )
3392 {
3393  QStringList multiLineSplit;
3394  if ( !wrapCharacter.isEmpty() && wrapCharacter != QString( "\n" ) )
3395  {
3396  //wrap on both the wrapchr and new line characters
3397  foreach ( QString line, text.split( wrapCharacter ) )
3398  {
3399  multiLineSplit.append( line.split( QString( "\n" ) ) );
3400  }
3401  }
3402  else
3403  {
3404  multiLineSplit = text.split( "\n" );
3405  }
3406 
3407  return multiLineSplit;
3408 }
3409 
3410 QStringList QgsPalLabeling::splitToGraphemes( const QString &text )
3411 {
3412  QStringList graphemes;
3413  QTextBoundaryFinder boundaryFinder( QTextBoundaryFinder::Grapheme, text );
3414  int currentBoundary = -1;
3415  int previousBoundary = 0;
3416  while (( currentBoundary = boundaryFinder.toNextBoundary() ) > 0 )
3417  {
3418  graphemes << text.mid( previousBoundary, currentBoundary - previousBoundary );
3419  previousBoundary = currentBoundary;
3420  }
3421  return graphemes;
3422 }
3423 
3424 QgsGeometry* QgsPalLabeling::prepareGeometry( const QgsGeometry* geometry, const QgsRenderContext& context, const QgsCoordinateTransform* ct, double minSize, QgsGeometry* clipGeometry )
3425 {
3426  if ( !geometry )
3427  {
3428  return 0;
3429  }
3430 
3431  //don't modify the feature's geometry so that geometry based expressions keep working
3432  QgsGeometry* geom = new QgsGeometry( *geometry );
3433  QScopedPointer<QgsGeometry> clonedGeometry( geom );
3434 
3435  //reproject the geometry if necessary
3436  if ( ct )
3437  {
3438  try
3439  {
3440  geom->transform( *ct );
3441  }
3442  catch ( QgsCsException &cse )
3443  {
3444  Q_UNUSED( cse );
3445  QgsDebugMsgLevel( QString( "Ignoring feature due to transformation exception" ), 4 );
3446  return 0;
3447  }
3448  }
3449 
3450  if ( minSize > 0 && !checkMinimumSizeMM( context, geom, minSize ) )
3451  {
3452  return 0;
3453  }
3454 
3455  if ( !geom->asGeos() )
3456  return 0; // there is something really wrong with the geometry
3457 
3458  // fix invalid polygons
3459  if ( geom->type() == QGis::Polygon && !geom->isGeosValid() )
3460  {
3461  QgsGeometry* bufferGeom = geom->buffer( 0, 0 );
3462  if ( !bufferGeom )
3463  {
3464  return 0;
3465  }
3466  geom = bufferGeom;
3467  clonedGeometry.reset( geom );
3468  }
3469 
3470  // Rotate the geometry if needed, before clipping
3471  const QgsMapToPixel& m2p = context.mapToPixel();
3472  if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) )
3473  {
3474  if ( geom->rotate( m2p.mapRotation(), context.extent().center() ) )
3475  {
3476  QgsDebugMsg( QString( "Error rotating geometry" ).arg( geom->exportToWkt() ) );
3477  return 0;
3478  }
3479  }
3480 
3481  if ( clipGeometry && !clipGeometry->contains( geom ) )
3482  {
3483  QgsGeometry* clipGeom = geom->intersection( clipGeometry ); // creates new geometry
3484  if ( !clipGeom )
3485  {
3486  return 0;
3487  }
3488  geom = clipGeom;
3489  clonedGeometry.reset( geom );
3490  }
3491 
3492  return clonedGeometry.take();
3493 }
3494 
3495 bool QgsPalLabeling::checkMinimumSizeMM( const QgsRenderContext& context, QgsGeometry* geom, double minSize )
3496 {
3497  if ( minSize <= 0 )
3498  {
3499  return true;
3500  }
3501 
3502  if ( !geom )
3503  {
3504  return false;
3505  }
3506 
3507  QGis::GeometryType featureType = geom->type();
3508  if ( featureType == QGis::Point ) //minimum size does not apply to point features
3509  {
3510  return true;
3511  }
3512 
3513  double mapUnitsPerMM = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
3514  if ( featureType == QGis::Line )
3515  {
3516  double length = geom->length();
3517  if ( length >= 0.0 )
3518  {
3519  return ( length >= ( minSize * mapUnitsPerMM ) );
3520  }
3521  }
3522  else if ( featureType == QGis::Polygon )
3523  {
3524  double area = geom->area();
3525  if ( area >= 0.0 )
3526  {
3527  return ( sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
3528  }
3529  }
3530  return true; //should never be reached. Return true in this case to label such geometries anyway.
3531 }
3532 
3533 void QgsPalLabeling::registerDiagramFeature( const QString& layerID, QgsFeature& feat, const QgsRenderContext& context )
3534 {
3535  //get diagram layer settings, diagram renderer
3536  QHash<QString, QgsDiagramLayerSettings>::iterator layerIt = mActiveDiagramLayers.find( layerID );
3537  if ( layerIt == mActiveDiagramLayers.constEnd() )
3538  {
3539  return;
3540  }
3541 
3542  QgsDiagramRendererV2* dr = layerIt.value().renderer;
3543  if ( dr )
3544  {
3545  QList<QgsDiagramSettings> settingList = dr->diagramSettings();
3546  if ( settingList.size() > 0 )
3547  {
3548  double minScale = settingList.at( 0 ).minScaleDenominator;
3549  if ( minScale > 0 && context.rendererScale() < minScale )
3550  {
3551  return;
3552  }
3553 
3554  double maxScale = settingList.at( 0 ).maxScaleDenominator;
3555  if ( maxScale > 0 && context.rendererScale() > maxScale )
3556  {
3557  return;
3558  }
3559  }
3560  }
3561 
3562  //convert geom to geos
3563  const QgsGeometry* geom = feat.constGeometry();
3564  QScopedPointer<QgsGeometry> extentGeom( QgsGeometry::fromRect( mMapSettings->visibleExtent() ) );
3565 
3566  const GEOSGeometry* geos_geom = 0;
3567  QScopedPointer<QgsGeometry> preparedGeom;
3568  if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, layerIt.value().ct, extentGeom.data() ) )
3569  {
3570  preparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, layerIt.value().ct, 0, extentGeom.data() ) );
3571  if ( !preparedGeom.data() )
3572  return;
3573  geos_geom = preparedGeom.data()->asGeos();
3574  }
3575  else
3576  {
3577  geos_geom = geom->asGeos();
3578  }
3579 
3580  if ( geos_geom == 0 )
3581  {
3582  return; // invalid geometry
3583  }
3584 
3585  //create PALGeometry with diagram = true
3586  QgsPalGeometry* lbl = new QgsPalGeometry( feat.id(), "", GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom ) );
3587  lbl->setIsDiagram( true );
3588 
3589  // record the created geometry - it will be deleted at the end.
3590  layerIt.value().geometries.append( lbl );
3591 
3592  double diagramWidth = 0;
3593  double diagramHeight = 0;
3594  if ( dr )
3595  {
3596  QSizeF diagSize = dr->sizeMapUnits( feat, context );
3597  if ( diagSize.isValid() )
3598  {
3599  diagramWidth = diagSize.width();
3600  diagramHeight = diagSize.height();
3601  }
3602 
3603  //append the diagram attributes to lbl
3604  lbl->setDiagramAttributes( feat.attributes() );
3605  }
3606 
3607  // feature to the layer
3608  bool alwaysShow = layerIt.value().showAll;
3609  int ddColX = layerIt.value().xPosColumn;
3610  int ddColY = layerIt.value().yPosColumn;
3611  double ddPosX = 0.0;
3612  double ddPosY = 0.0;
3613  bool ddPos = ( ddColX >= 0 && ddColY >= 0 );
3614  if ( ddPos )
3615  {
3616  bool posXOk, posYOk;
3617  ddPosX = feat.attribute( ddColX ).toDouble( &posXOk );
3618  ddPosY = feat.attribute( ddColY ).toDouble( &posYOk );
3619  if ( !posXOk || !posYOk )
3620  {
3621  ddPos = false;
3622  }
3623  else
3624  {
3625  const QgsCoordinateTransform* ct = layerIt.value().ct;
3626  if ( ct )
3627  {
3628  double z = 0;
3629  ct->transformInPlace( ddPosX, ddPosY, z );
3630  }
3631  //data defined diagram position is always centered
3632  ddPosX -= diagramWidth / 2.0;
3633  ddPosY -= diagramHeight / 2.0;
3634  }
3635  }
3636 
3637  try
3638  {
3639  if ( !layerIt.value().palLayer->registerFeature( lbl->strId(), lbl, diagramWidth, diagramHeight, "", ddPosX, ddPosY, ddPos, 0.0, true, 0, 0, 0, 0, alwaysShow ) )
3640  {
3641  return;
3642  }
3643  }
3644  catch ( std::exception &e )
3645  {
3646  Q_UNUSED( e );
3647  QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( feat.id() ) + QString::fromLatin1( e.what() ), 4 );
3648  return;
3649  }
3650 
3651  pal::Feature* palFeat = layerIt.value().palLayer->getFeature( lbl->strId() );
3652  QgsPoint ptZero = layerIt.value().xform->toMapCoordinates( 0, 0 );
3653  QgsPoint ptOne = layerIt.value().xform->toMapCoordinates( 1, 0 );
3654  palFeat->setDistLabel( qAbs( ptOne.x() - ptZero.x() ) * layerIt.value().dist );
3655 }
3656 
3657 
3659 {
3660  init( mr->mapSettings() );
3661 }
3662 
3663 void QgsPalLabeling::init( const QgsMapSettings& mapSettings )
3664 {
3665  mMapSettings = &mapSettings;
3666 
3667  // delete if exists already
3668  if ( mPal )
3669  delete mPal;
3670 
3671  mPal = new Pal;
3672 
3673  SearchMethod s;
3674  switch ( mSearch )
3675  {
3676  default:
3677  case Chain: s = CHAIN; break;
3678  case Popmusic_Tabu: s = POPMUSIC_TABU; break;
3679  case Popmusic_Chain: s = POPMUSIC_CHAIN; break;
3680  case Popmusic_Tabu_Chain: s = POPMUSIC_TABU_CHAIN; break;
3681  case Falp: s = FALP; break;
3682  }
3683  mPal->setSearch( s );
3684 
3685  // set number of candidates generated per feature
3687  mPal->setLineP( mCandLine );
3689 
3691 
3692  clearActiveLayers(); // free any previous QgsDataDefined objects
3693  mActiveDiagramLayers.clear();
3694 }
3695 
3697 {
3698  delete mPal;
3699  mPal = NULL;
3700  mMapSettings = NULL;
3701 }
3702 
3703 QgsPalLayerSettings& QgsPalLabeling::layer( const QString& layerName )
3704 {
3705  QHash<QString, QgsPalLayerSettings>::iterator lit;
3706  for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
3707  {
3708  if ( lit.key() == layerName )
3709  {
3710  return lit.value();
3711  }
3712  }
3713  return mInvalidLayerSettings;
3714 }
3715 
3717  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3718 {
3719  //font color
3720  if ( ddValues.contains( QgsPalLayerSettings::Color ) )
3721  {
3722  QVariant ddColor = ddValues.value( QgsPalLayerSettings::Color );
3723  tmpLyr.textColor = ddColor.value<QColor>();
3724  }
3725 
3726  //font transparency
3727  if ( ddValues.contains( QgsPalLayerSettings::FontTransp ) )
3728  {
3729  tmpLyr.textTransp = ddValues.value( QgsPalLayerSettings::FontTransp ).toInt();
3730  }
3731 
3732  tmpLyr.textColor.setAlphaF(( 100.0 - ( double )( tmpLyr.textTransp ) ) / 100.0 );
3733 
3734  //font blend mode
3735  if ( ddValues.contains( QgsPalLayerSettings::FontBlendMode ) )
3736  {
3737  tmpLyr.blendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::FontBlendMode ).toInt();
3738  }
3739 }
3740 
3742  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3743 {
3744  if ( ddValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
3745  {
3746  tmpLyr.wrapChar = ddValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
3747  }
3748 
3749  if ( !tmpLyr.wrapChar.isEmpty() || tmpLyr.getLabelExpression()->expression().contains( "wordwrap" ) )
3750  {
3751 
3752  if ( ddValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
3753  {
3754  tmpLyr.multilineHeight = ddValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
3755  }
3756 
3757  if ( ddValues.contains( QgsPalLayerSettings::MultiLineAlignment ) )
3758  {
3760  }
3761 
3762  }
3763 
3764  if ( ddValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
3765  {
3766  tmpLyr.addDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
3767  }
3768 
3769  if ( tmpLyr.addDirectionSymbol )
3770  {
3771 
3772  if ( ddValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
3773  {
3774  tmpLyr.leftDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
3775  }
3776  if ( ddValues.contains( QgsPalLayerSettings::DirSymbRight ) )
3777  {
3778  tmpLyr.rightDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
3779  }
3780 
3781  if ( ddValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
3782  {
3784  }
3785 
3786  if ( ddValues.contains( QgsPalLayerSettings::DirSymbReverse ) )
3787  {
3788  tmpLyr.reverseDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbReverse ).toBool();
3789  }
3790 
3791  }
3792 }
3793 
3795  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3796 {
3797  //buffer draw
3798  if ( ddValues.contains( QgsPalLayerSettings::BufferDraw ) )
3799  {
3800  tmpLyr.bufferDraw = ddValues.value( QgsPalLayerSettings::BufferDraw ).toBool();
3801  }
3802 
3803  if ( !tmpLyr.bufferDraw )
3804  {
3805  // tmpLyr.bufferSize > 0.0 && tmpLyr.bufferTransp < 100 figured in during evaluation
3806  return; // don't continue looking for unused values
3807  }
3808 
3809  //buffer size
3810  if ( ddValues.contains( QgsPalLayerSettings::BufferSize ) )
3811  {
3812  tmpLyr.bufferSize = ddValues.value( QgsPalLayerSettings::BufferSize ).toDouble();
3813  }
3814 
3815  //buffer transparency
3816  if ( ddValues.contains( QgsPalLayerSettings::BufferTransp ) )
3817  {
3818  tmpLyr.bufferTransp = ddValues.value( QgsPalLayerSettings::BufferTransp ).toInt();
3819  }
3820 
3821  //buffer size units
3822  if ( ddValues.contains( QgsPalLayerSettings::BufferUnit ) )
3823  {
3825  tmpLyr.bufferSizeInMapUnits = ( bufunit == QgsPalLayerSettings::MapUnits );
3826  }
3827 
3828  //buffer color
3829  if ( ddValues.contains( QgsPalLayerSettings::BufferColor ) )
3830  {
3831  QVariant ddColor = ddValues.value( QgsPalLayerSettings::BufferColor );
3832  tmpLyr.bufferColor = ddColor.value<QColor>();
3833  }
3834 
3835  // apply any transparency
3836  tmpLyr.bufferColor.setAlphaF(( 100.0 - ( double )( tmpLyr.bufferTransp ) ) / 100.0 );
3837 
3838  //buffer pen join style
3839  if ( ddValues.contains( QgsPalLayerSettings::BufferJoinStyle ) )
3840  {
3841  tmpLyr.bufferJoinStyle = ( Qt::PenJoinStyle )ddValues.value( QgsPalLayerSettings::BufferJoinStyle ).toInt();
3842  }
3843 
3844  //buffer blend mode
3845  if ( ddValues.contains( QgsPalLayerSettings::BufferBlendMode ) )
3846  {
3847  tmpLyr.bufferBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::BufferBlendMode ).toInt();
3848  }
3849 }
3850 
3852  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3853 {
3854  //shape draw
3855  if ( ddValues.contains( QgsPalLayerSettings::ShapeDraw ) )
3856  {
3857  tmpLyr.shapeDraw = ddValues.value( QgsPalLayerSettings::ShapeDraw ).toBool();
3858  }
3859 
3860  if ( !tmpLyr.shapeDraw )
3861  {
3862  return; // don't continue looking for unused values
3863  }
3864 
3865  if ( ddValues.contains( QgsPalLayerSettings::ShapeKind ) )
3866  {
3867  tmpLyr.shapeType = ( QgsPalLayerSettings::ShapeType )ddValues.value( QgsPalLayerSettings::ShapeKind ).toInt();
3868  }
3869 
3870  if ( ddValues.contains( QgsPalLayerSettings::ShapeSVGFile ) )
3871  {
3872  tmpLyr.shapeSVGFile = ddValues.value( QgsPalLayerSettings::ShapeSVGFile ).toString();
3873  }
3874 
3875  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeType ) )
3876  {
3878  }
3879 
3880  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeX ) )
3881  {
3882  tmpLyr.shapeSize.setX( ddValues.value( QgsPalLayerSettings::ShapeSizeX ).toDouble() );
3883  }
3884  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeY ) )
3885  {
3886  tmpLyr.shapeSize.setY( ddValues.value( QgsPalLayerSettings::ShapeSizeY ).toDouble() );
3887  }
3888 
3889  if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeUnits ) )
3890  {
3892  }
3893 
3894  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotationType ) )
3895  {
3897  }
3898 
3899  if ( ddValues.contains( QgsPalLayerSettings::ShapeRotation ) )
3900  {
3901  tmpLyr.shapeRotation = ddValues.value( QgsPalLayerSettings::ShapeRotation ).toDouble();
3902  }
3903 
3904  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffset ) )
3905  {
3906  tmpLyr.shapeOffset = ddValues.value( QgsPalLayerSettings::ShapeOffset ).toPointF();
3907  }
3908 
3909  if ( ddValues.contains( QgsPalLayerSettings::ShapeOffsetUnits ) )
3910  {
3912  }
3913 
3914  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadii ) )
3915  {
3916  tmpLyr.shapeRadii = ddValues.value( QgsPalLayerSettings::ShapeRadii ).toPointF();
3917  }
3918 
3919  if ( ddValues.contains( QgsPalLayerSettings::ShapeRadiiUnits ) )
3920  {
3922  }
3923 
3924  if ( ddValues.contains( QgsPalLayerSettings::ShapeTransparency ) )
3925  {
3926  tmpLyr.shapeTransparency = ddValues.value( QgsPalLayerSettings::ShapeTransparency ).toInt();
3927  }
3928 
3929  if ( ddValues.contains( QgsPalLayerSettings::ShapeBlendMode ) )
3930  {
3931  tmpLyr.shapeBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::ShapeBlendMode ).toInt();
3932  }
3933 
3934  if ( ddValues.contains( QgsPalLayerSettings::ShapeFillColor ) )
3935  {
3936  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeFillColor );
3937  tmpLyr.shapeFillColor = ddColor.value<QColor>();
3938  }
3939 
3940  if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderColor ) )
3941  {
3942  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeBorderColor );
3943  tmpLyr.shapeBorderColor = ddColor.value<QColor>();
3944  }
3945 
3946  if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderWidth ) )
3947  {
3948  tmpLyr.shapeBorderWidth = ddValues.value( QgsPalLayerSettings::ShapeBorderWidth ).toDouble();
3949  }
3950 
3951  if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderWidthUnits ) )
3952  {
3954  }
3955 
3956  if ( ddValues.contains( QgsPalLayerSettings::ShapeJoinStyle ) )
3957  {
3958  tmpLyr.shapeJoinStyle = ( Qt::PenJoinStyle )ddValues.value( QgsPalLayerSettings::ShapeJoinStyle ).toInt();
3959  }
3960 }
3961 
3963  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
3964 {
3965  //shadow draw
3966  if ( ddValues.contains( QgsPalLayerSettings::ShadowDraw ) )
3967  {
3968  tmpLyr.shadowDraw = ddValues.value( QgsPalLayerSettings::ShadowDraw ).toBool();
3969  }
3970 
3971  if ( !tmpLyr.shadowDraw )
3972  {
3973  return; // don't continue looking for unused values
3974  }
3975 
3976  if ( ddValues.contains( QgsPalLayerSettings::ShadowUnder ) )
3977  {
3979  }
3980 
3981  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetAngle ) )
3982  {
3983  tmpLyr.shadowOffsetAngle = ddValues.value( QgsPalLayerSettings::ShadowOffsetAngle ).toInt();
3984  }
3985 
3986  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetDist ) )
3987  {
3988  tmpLyr.shadowOffsetDist = ddValues.value( QgsPalLayerSettings::ShadowOffsetDist ).toDouble();
3989  }
3990 
3991  if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetUnits ) )
3992  {
3994  }
3995 
3996  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadius ) )
3997  {
3998  tmpLyr.shadowRadius = ddValues.value( QgsPalLayerSettings::ShadowRadius ).toDouble();
3999  }
4000 
4001  if ( ddValues.contains( QgsPalLayerSettings::ShadowRadiusUnits ) )
4002  {
4004  }
4005 
4006  if ( ddValues.contains( QgsPalLayerSettings::ShadowTransparency ) )
4007  {
4008  tmpLyr.shadowTransparency = ddValues.value( QgsPalLayerSettings::ShadowTransparency ).toInt();
4009  }
4010 
4011  if ( ddValues.contains( QgsPalLayerSettings::ShadowScale ) )
4012  {
4013  tmpLyr.shadowScale = ddValues.value( QgsPalLayerSettings::ShadowScale ).toInt();
4014  }
4015 
4016  if ( ddValues.contains( QgsPalLayerSettings::ShadowColor ) )
4017  {
4018  QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShadowColor );
4019  tmpLyr.shadowColor = ddColor.value<QColor>();
4020  }
4021 
4022  if ( ddValues.contains( QgsPalLayerSettings::ShadowBlendMode ) )
4023  {
4024  tmpLyr.shadowBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::ShadowBlendMode ).toInt();
4025  }
4026 }
4027 
4028 
4029 // helper function for checking for job cancellation within PAL
4030 static bool _palIsCancelled( void* ctx )
4031 {
4032  return (( QgsRenderContext* ) ctx )->renderingStopped();
4033 }
4034 
4036 {
4037  Q_ASSERT( mMapSettings != NULL );
4038  QPainter* painter = context.painter();
4039  QgsRectangle extent = context.extent();
4040 
4042 
4043  delete mResults;
4045 
4046  QTime t;
4047  t.start();
4048 
4049  // do the labeling itself
4050  double scale = mMapSettings->scale(); // scale denominator
4051  QgsRectangle r = extent;
4052  double bbox[] = { r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum() };
4053 
4054  std::list<LabelPosition*>* labels;
4055  pal::Problem* problem;
4056  try
4057  {
4058  problem = mPal->extractProblem( scale, bbox );
4059  }
4060  catch ( std::exception& e )
4061  {
4062  Q_UNUSED( e );
4063  QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
4064  //mActiveLayers.clear(); // clean up
4065  return;
4066  }
4067 
4068  if ( context.renderingStopped() )
4069  return; // it has been cancelled
4070 
4071 #if 1 // XXX strk
4072  // features are pre-rotated but not scaled/translated,
4073  // so we only disable rotation here. Ideally, they'd be
4074  // also pre-scaled/translated, as suggested here:
4075  // http://hub.qgis.org/issues/11856
4077  xform.setMapRotation( 0, 0, 0 );
4078 #else
4079  const QgsMapToPixel& xform = mMapSettings->mapToPixel();
4080 #endif
4081 
4082  // draw rectangles with all candidates
4083  // this is done before actual solution of the problem
4084  // before number of candidates gets reduced
4085  mCandidates.clear();
4086  if ( mShowingCandidates && problem )
4087  {
4088  painter->setPen( QColor( 0, 0, 0, 64 ) );
4089  painter->setBrush( Qt::NoBrush );
4090  for ( int i = 0; i < problem->getNumFeatures(); i++ )
4091  {
4092  for ( int j = 0; j < problem->getFeatureCandidateCount( i ); j++ )
4093  {
4094  pal::LabelPosition* lp = problem->getFeatureCandidate( i, j );
4095 
4096  drawLabelCandidateRect( lp, painter, &xform );
4097  }
4098  }
4099  }
4100 
4101  // find the solution
4102  labels = mPal->solveProblem( problem, mShowingAllLabels );
4103 
4104  QgsDebugMsgLevel( QString( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( labels->size() ), 4 );
4105  t.restart();
4106 
4107  if ( context.renderingStopped() )
4108  {
4109  delete problem;
4110  delete labels;
4112  return;
4113  }
4114 
4115  painter->setRenderHint( QPainter::Antialiasing );
4116 
4117  // draw the labels
4118  std::list<LabelPosition*>::iterator it = labels->begin();
4119  for ( ; it != labels->end(); ++it )
4120  {
4121  if ( context.renderingStopped() )
4122  break;
4123 
4124  QgsPalGeometry* palGeometry = dynamic_cast< QgsPalGeometry* >(( *it )->getFeaturePart()->getUserGeometry() );
4125  if ( !palGeometry )
4126  {
4127  continue;
4128  }
4129 
4130  //layer names
4131  QString layerName = QString::fromUtf8(( *it )->getLayerName() );
4132  if ( palGeometry->isDiagram() )
4133  {
4134  QgsFeature feature;
4135  //render diagram
4136  QHash<QString, QgsDiagramLayerSettings>::iterator dit = mActiveDiagramLayers.begin();
4137  for ( dit = mActiveDiagramLayers.begin(); dit != mActiveDiagramLayers.end(); ++dit )
4138  {
4139  if ( QString( dit.key() + "d" ) == layerName )
4140  {
4141  feature.setFields( dit.value().fields );
4142  palGeometry->feature( feature );
4143 
4144  //calculate top-left point for diagram
4145  //first, calculate the centroid of the label (accounts for PAL creating
4146  //rotated labels when we do not want to draw the diagrams rotated)
4147  double centerX = 0;
4148  double centerY = 0;
4149  for ( int i = 0; i < 4; ++i )
4150  {
4151  centerX += ( *it )->getX( i );
4152  centerY += ( *it )->getY( i );
4153  }
4154  QgsPoint outPt( centerX / 4.0, centerY / 4.0 );
4155  //then, calculate the top left point for the diagram with this center position
4156  QgsPoint centerPt = xform.transform( outPt.x() - ( *it )->getWidth() / 2,
4157  outPt.y() - ( *it )->getHeight() / 2 );
4158 
4159  dit.value().renderer->renderDiagram( feature, context, centerPt.toQPointF() );
4160  }
4161  }
4162 
4163  //insert into label search tree to manipulate position interactively
4164  if ( mResults->mLabelSearchTree )
4165  {
4166  //for diagrams, remove the additional 'd' at the end of the layer id
4167  QString layerId = layerName;
4168  layerId.chop( 1 );
4169  mResults->mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), layerId, QString( "" ), QFont(), true, false );
4170  }
4171  continue;
4172  }
4173 
4174  const QgsPalLayerSettings& lyr = layer( layerName );
4175 
4176  // Copy to temp, editable layer settings
4177  // these settings will be changed by any data defined values, then used for rendering label components
4178  // settings may be adjusted during rendering of components
4179  QgsPalLayerSettings tmpLyr( lyr );
4180 
4181  // apply any previously applied data defined settings for the label
4182  const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues = palGeometry->dataDefinedValues();
4183 
4184  //font
4185  QFont dFont = palGeometry->definedFont();
4186  // following debug is >= Qt 4.8 only ( because of QFont::styleName() )
4187 #if QT_VERSION >= 0x040800
4188  QgsDebugMsgLevel( QString( "PAL font tmpLyr: %1, Style: %2" ).arg( tmpLyr.textFont.toString() ).arg( QFontInfo( tmpLyr.textFont ).styleName() ), 4 );
4189  QgsDebugMsgLevel( QString( "PAL font definedFont: %1, Style: %2" ).arg( dFont.toString() ).arg( dFont.styleName() ), 4 );
4190 #endif
4191  tmpLyr.textFont = dFont;
4192 
4194  {
4195  //calculate font alignment based on label quadrant
4196  switch (( *it )->getQuadrant() )
4197  {
4198  case LabelPosition::QuadrantAboveLeft:
4199  case LabelPosition::QuadrantLeft:
4200  case LabelPosition::QuadrantBelowLeft:
4202  break;
4203  case LabelPosition::QuadrantAbove:
4204  case LabelPosition::QuadrantOver:
4205  case LabelPosition::QuadrantBelow:
4207  break;
4208  case LabelPosition::QuadrantAboveRight:
4209  case LabelPosition::QuadrantRight:
4210  case LabelPosition::QuadrantBelowRight:
4212  break;
4213  }
4214  }
4215 
4216  // update tmpLyr with any data defined text style values
4217  dataDefinedTextStyle( tmpLyr, ddValues );
4218 
4219  // update tmpLyr with any data defined text buffer values
4220  dataDefinedTextBuffer( tmpLyr, ddValues );
4221 
4222  // update tmpLyr with any data defined text formatting values
4223  dataDefinedTextFormatting( tmpLyr, ddValues );
4224 
4225  // update tmpLyr with any data defined shape background values
4226  dataDefinedShapeBackground( tmpLyr, ddValues );
4227 
4228  // update tmpLyr with any data defined drop shadow values
4229  dataDefinedDropShadow( tmpLyr, ddValues );
4230 
4231 
4233 
4234  // Render the components of a label in reverse order
4235  // (backgrounds -> text)
4236 
4237  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowLowest )
4238  {
4239  if ( tmpLyr.shapeDraw )
4240  {
4242  }
4243  else if ( tmpLyr.bufferDraw )
4244  {
4246  }
4247  else
4248  {
4250  }
4251  }
4252 
4253  if ( tmpLyr.shapeDraw )
4254  {
4255  drawLabel( *it, context, tmpLyr, LabelShape );
4256  }
4257 
4258  if ( tmpLyr.bufferDraw )
4259  {
4260  drawLabel( *it, context, tmpLyr, LabelBuffer );
4261  }
4262 
4263  drawLabel( *it, context, tmpLyr, LabelText );
4264 
4265  if ( mResults->mLabelSearchTree )
4266  {
4267  QString labeltext = (( QgsPalGeometry* )( *it )->getFeaturePart()->getUserGeometry() )->text();
4268  mResults->mLabelSearchTree->insertLabel( *it, QString( palGeometry->strId() ).toInt(), layerName, labeltext, dFont, false, palGeometry->isPinned() );
4269  }
4270  }
4271 
4272  // Reset composition mode for further drawing operations
4273  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
4274 
4275  QgsDebugMsgLevel( QString( "LABELING draw: %1 ms" ).arg( t.elapsed() ), 4 );
4276 
4277  delete problem;
4278  delete labels;
4280 }
4281 
4283 {
4284  // delete all allocated geometries for features
4285  QHash<QString, QgsPalLayerSettings>::iterator lit;
4286  for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
4287  {
4288  QgsPalLayerSettings& lyr = lit.value();
4289  for ( QList<QgsPalGeometry*>::iterator git = lyr.geometries.begin(); git != lyr.geometries.end(); ++git )
4290  delete *git;
4291  if ( lyr.limitNumLabels )
4292  {
4293  QgsDebugMsgLevel( QString( "mFeaturesToLabel: %1" ).arg( lyr.mFeaturesToLabel ), 4 );
4294  QgsDebugMsgLevel( QString( "maxNumLabels: %1" ).arg( lyr.maxNumLabels ), 4 );
4295  QgsDebugMsgLevel( QString( "mFeatsSendingToPal: %1" ).arg( lyr.mFeatsSendingToPal ), 4 );
4296  QgsDebugMsgLevel( QString( "mFeatsRegPal: %1" ).arg( lyr.geometries.count() ), 4 );
4297  }
4298  lyr.geometries.clear();
4299  }
4300 
4301  //delete all allocated geometries for diagrams
4302  QHash<QString, QgsDiagramLayerSettings>::iterator dIt = mActiveDiagramLayers.begin();
4303  for ( ; dIt != mActiveDiagramLayers.end(); ++dIt )
4304  {
4305  QgsDiagramLayerSettings& dls = dIt.value();
4306  for ( QList<QgsPalGeometry*>::iterator git = dls.geometries.begin(); git != dls.geometries.end(); ++git )
4307  {
4308  delete *git;
4309  }
4310  dls.geometries.clear();
4311  }
4312 }
4313 
4314 QList<QgsLabelPosition> QgsPalLabeling::labelsAtPosition( const QgsPoint& p )
4315 {
4316  return mResults ? mResults->labelsAtPosition( p ) : QList<QgsLabelPosition>();
4317 }
4318 
4319 QList<QgsLabelPosition> QgsPalLabeling::labelsWithinRect( const QgsRectangle& r )
4320 {
4321  return mResults ? mResults->labelsWithinRect( r ) : QList<QgsLabelPosition>();
4322 }
4323 
4325 {
4326  if ( mResults )
4327  {
4329  mResults = 0;
4330  return tmp; // ownership passed to the caller
4331  }
4332  else
4333  return 0;
4334 }
4335 
4336 void QgsPalLabeling::numCandidatePositions( int& candPoint, int& candLine, int& candPolygon )
4337 {
4338  candPoint = mCandPoint;
4339  candLine = mCandLine;
4340  candPolygon = mCandPolygon;
4341 }
4342 
4343 void QgsPalLabeling::setNumCandidatePositions( int candPoint, int candLine, int candPolygon )
4344 {
4345  mCandPoint = candPoint;
4346  mCandLine = candLine;
4347  mCandPolygon = candPolygon;
4348 }
4349 
4351 {
4352  mSearch = s;
4353 }
4354 
4356 {
4357  return mSearch;
4358 }
4359 
4361 {
4362  QgsPoint outPt = xform->transform( lp->getX(), lp->getY() );
4363 
4364  painter->save();
4365 
4366 #if 0 // TODO: generalize some of this
4367  double w = lp->getWidth();
4368  double h = lp->getHeight();
4369  double cx = lp->getX() + w / 2.0;
4370  double cy = lp->getY() + h / 2.0;
4371  double scale = 1.0 / xform->mapUnitsPerPixel();
4372  double rotation = xform->mapRotation();
4373  double sw = w * scale;
4374  double sh = h * scale;
4375  QRectF rect( -sw / 2, -sh / 2, sw, sh );
4376 
4377  painter->translate( xform->transform( QPointF( cx, cy ) ).toQPointF() );
4378  if ( rotation )
4379  {
4380  // Only if not horizontal
4381  if ( lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT &&
4384  {
4385  painter->rotate( rotation );
4386  }
4387  }
4388  painter->translate( rect.bottomLeft() );
4389  painter->rotate( -lp->getAlpha() * 180 / M_PI );
4390  painter->translate( -rect.bottomLeft() );
4391 #else
4392  QgsPoint outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
4393  QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
4394  painter->translate( QPointF( outPt.x(), outPt.y() ) );
4395  painter->rotate( -lp->getAlpha() * 180 / M_PI );
4396 #endif
4397 
4398  painter->drawRect( rect );
4399  painter->restore();
4400 
4401  // save the rect
4402  rect.moveTo( outPt.x(), outPt.y() );
4403  mCandidates.append( QgsLabelCandidate( rect, lp->getCost() * 1000 ) );
4404 
4405  // show all parts of the multipart label
4406  if ( lp->getNextPart() )
4407  drawLabelCandidateRect( lp->getNextPart(), painter, xform );
4408 }
4409 
4410 void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QgsRenderContext& context, QgsPalLayerSettings& tmpLyr, DrawLabelType drawType, double dpiRatio )
4411 {
4412  // NOTE: this is repeatedly called for multi-part labels
4413  QPainter* painter = context.painter();
4414 #if 1 // XXX strk
4415  // features are pre-rotated but not scaled/translated,
4416  // so we only disable rotation here. Ideally, they'd be
4417  // also pre-scaled/translated, as suggested here:
4418  // http://hub.qgis.org/issues/11856
4419  QgsMapToPixel xform = context.mapToPixel();
4420  xform.setMapRotation( 0, 0, 0 );
4421 #else
4422  const QgsMapToPixel& xform = context.mapToPixel();
4423 #endif
4424 
4425  QgsLabelComponent component;
4426  component.setDpiRatio( dpiRatio );
4427 
4428  QgsPoint outPt = xform.transform( label->getX(), label->getY() );
4429 // QgsPoint outPt2 = xform->transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() );
4430 // QRectF labelRect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
4431 
4432  component.setOrigin( outPt );
4433  component.setRotation( label->getAlpha() );
4434 
4435  if ( drawType == QgsPalLabeling::LabelShape )
4436  {
4437  // get rotated label's center point
4438  QgsPoint centerPt( outPt );
4439  QgsPoint outPt2 = xform.transform( label->getX() + label->getWidth() / 2,
4440  label->getY() + label->getHeight() / 2 );
4441 
4442  double xc = outPt2.x() - outPt.x();
4443  double yc = outPt2.y() - outPt.y();
4444 
4445  double angle = -label->getAlpha();
4446  double xd = xc * cos( angle ) - yc * sin( angle );
4447  double yd = xc * sin( angle ) + yc * cos( angle );
4448 
4449  centerPt.setX( centerPt.x() + xd );
4450  centerPt.setY( centerPt.y() + yd );
4451 
4452  component.setCenter( centerPt );
4453  component.setSize( QgsPoint( label->getWidth(), label->getHeight() ) );
4454 
4455  drawLabelBackground( context, component, tmpLyr );
4456  }
4457 
4458  else if ( drawType == QgsPalLabeling::LabelBuffer
4459  || drawType == QgsPalLabeling::LabelText )
4460  {
4461 
4462  // TODO: optimize access :)
4463  QString txt = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->text( label->getPartId() );
4464  QFontMetricsF* labelfm = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->getLabelFontMetrics();
4465 
4466  //add the direction symbol if needed
4467  if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line &&
4468  tmpLyr.addDirectionSymbol )
4469  {
4470  bool prependSymb = false;
4471  QString symb = tmpLyr.rightDirectionSymbol;
4472 
4473  if ( label->getReversed() )
4474  {
4475  prependSymb = true;
4476  symb = tmpLyr.leftDirectionSymbol;
4477  }
4478 
4479  if ( tmpLyr.reverseDirectionSymbol )
4480  {
4481  if ( symb == tmpLyr.rightDirectionSymbol )
4482  {
4483  prependSymb = true;
4484  symb = tmpLyr.leftDirectionSymbol;
4485  }
4486  else
4487  {
4488  prependSymb = false;
4489  symb = tmpLyr.rightDirectionSymbol;
4490  }
4491  }
4492 
4494  {
4495  prependSymb = true;
4496  symb = symb + QString( "\n" );
4497  }
4499  {
4500  prependSymb = false;
4501  symb = QString( "\n" ) + symb;
4502  }
4503 
4504  if ( prependSymb )
4505  {
4506  txt.prepend( symb );
4507  }
4508  else
4509  {
4510  txt.append( symb );
4511  }
4512  }
4513 
4514  //QgsDebugMsgLevel( "drawLabel " + txt, 4 );
4515  QStringList multiLineList = QgsPalLabeling::splitToLines( txt, tmpLyr.wrapChar );
4516  int lines = multiLineList.size();
4517 
4518  double labelWidest = 0.0;
4519  for ( int i = 0; i < lines; ++i )
4520  {
4521  double labelWidth = labelfm->width( multiLineList.at( i ) );
4522  if ( labelWidth > labelWidest )
4523  {
4524  labelWidest = labelWidth;
4525  }
4526  }
4527 
4528  double labelHeight = labelfm->ascent() + labelfm->descent(); // ignore +1 for baseline
4529  // double labelHighest = labelfm->height() + ( double )(( lines - 1 ) * labelHeight * tmpLyr.multilineHeight );
4530 
4531  // needed to move bottom of text's descender to within bottom edge of label
4532  double ascentOffset = 0.25 * labelfm->ascent(); // labelfm->descent() is not enough
4533 
4534  for ( int i = 0; i < lines; ++i )
4535  {
4536  painter->save();
4537 #if 0 // TODO: generalize some of this
4538  LabelPosition* lp = label;
4539  double w = lp->getWidth();
4540  double h = lp->getHeight();
4541  double cx = lp->getX() + w / 2.0;
4542  double cy = lp->getY() + h / 2.0;
4543  double scale = 1.0 / xform->mapUnitsPerPixel();
4544  double rotation = xform->mapRotation();
4545  double sw = w * scale;
4546  double sh = h * scale;
4547  QRectF rect( -sw / 2, -sh / 2, sw, sh );
4548  painter->translate( xform->transform( QPointF( cx, cy ) ).toQPointF() );
4549  if ( rotation )
4550  {
4551  // Only if not horizontal
4552  if ( lp->getFeaturePart()->getLayer()->getArrangement() != P_POINT &&
4555  {
4556  painter->rotate( rotation );
4557  }
4558  }
4559  painter->translate( rect.bottomLeft() );
4560  painter->rotate( -lp->getAlpha() * 180 / M_PI );
4561 #else
4562  painter->translate( QPointF( outPt.x(), outPt.y() ) );
4563  painter->rotate( -label->getAlpha() * 180 / M_PI );
4564 #endif
4565 
4566  // scale down painter: the font size has been multiplied by raster scale factor
4567  // to workaround a Qt font scaling bug with small font sizes
4568  painter->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
4569 
4570  // figure x offset for horizontal alignment of multiple lines
4571  double xMultiLineOffset = 0.0;
4572  double labelWidth = labelfm->width( multiLineList.at( i ) );
4573  if ( lines > 1 && tmpLyr.multilineAlign != QgsPalLayerSettings::MultiLeft )
4574  {
4575  double labelWidthDiff = labelWidest - labelWidth;
4577  {
4578  labelWidthDiff /= 2;
4579  }
4580  xMultiLineOffset = labelWidthDiff;
4581  //QgsDebugMsgLevel( QString( "xMultiLineOffset: %1" ).arg( xMultiLineOffset ), 4 );
4582  }
4583 
4584  double yMultiLineOffset = ( lines - 1 - i ) * labelHeight * tmpLyr.multilineHeight;
4585  painter->translate( QPointF( xMultiLineOffset, - ascentOffset - yMultiLineOffset ) );
4586 
4587  component.setText( multiLineList.at( i ) );
4588  component.setSize( QgsPoint( labelWidth, labelHeight ) );
4589  component.setOffset( QgsPoint( 0.0, -ascentOffset ) );
4590  component.setRotation( -component.rotation() * 180 / M_PI );
4591  component.setRotationOffset( 0.0 );
4592 
4593  if ( drawType == QgsPalLabeling::LabelBuffer )
4594  {
4595  // draw label's buffer
4596  drawLabelBuffer( context, component, tmpLyr );
4597  }
4598  else
4599  {
4600  // draw label's text, QPainterPath method
4601  QPainterPath path;
4602  path.addText( 0, 0, tmpLyr.textFont, component.text() );
4603 
4604  // store text's drawing in QPicture for drop shadow call
4605  QPicture textPict;
4606  QPainter textp;
4607  textp.begin( &textPict );
4608  textp.setPen( Qt::NoPen );
4609  textp.setBrush( tmpLyr.textColor );
4610  textp.drawPath( path );
4611  // TODO: why are some font settings lost on drawPicture() when using drawText() inside QPicture?
4612  // e.g. some capitalization options, but not others
4613  //textp.setFont( tmpLyr.textFont );
4614  //textp.setPen( tmpLyr.textColor );
4615  //textp.drawText( 0, 0, component.text() );
4616  textp.end();
4617 
4618  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowText )
4619  {
4620  component.setPicture( &textPict );
4621  component.setPictureBuffer( 0.0 ); // no pen width to deal with
4622  component.setOrigin( QgsPoint( 0.0, 0.0 ) );
4623 
4624  drawLabelShadow( context, component, tmpLyr );
4625  }
4626 
4627  // paint the text
4628  if ( context.useAdvancedEffects() )
4629  {
4630  painter->setCompositionMode( tmpLyr.blendMode );
4631  }
4632 
4633  // scale for any print output or image saving @ specific dpi
4634  painter->scale( component.dpiRatio(), component.dpiRatio() );
4635 
4636  if ( mDrawOutlineLabels )
4637  {
4638  // draw outlined text
4639  _fixQPictureDPI( painter );
4640  painter->drawPicture( 0, 0, textPict );
4641  }
4642  else
4643  {
4644  // draw text as text (for SVG and PDF exports)
4645  painter->setFont( tmpLyr.textFont );
4646  painter->setPen( tmpLyr.textColor );
4647  painter->setRenderHint( QPainter::TextAntialiasing );
4648  painter->drawText( 0, 0, component.text() );
4649  }
4650  }
4651  painter->restore();
4652  }
4653  }
4654 
4655  // NOTE: this used to be within above multi-line loop block, at end. (a mistake since 2010? [LS])
4656  if ( label->getNextPart() )
4657  drawLabel( label->getNextPart(), context, tmpLyr, drawType, dpiRatio );
4658 }
4659 
4661  const QgsLabelComponent& component,
4662  const QgsPalLayerSettings& tmpLyr )
4663 {
4664  QPainter* p = context.painter();
4665 
4666  double penSize = tmpLyr.scaleToPixelContext( tmpLyr.bufferSize, context,
4668 
4669  QPainterPath path;
4670  path.addText( 0, 0, tmpLyr.textFont, component.text() );
4671  QPen pen( tmpLyr.bufferColor );
4672  pen.setWidthF( penSize );
4673  pen.setJoinStyle( tmpLyr.bufferJoinStyle );
4674  QColor tmpColor( tmpLyr.bufferColor );
4675  // honor pref for whether to fill buffer interior
4676  if ( tmpLyr.bufferNoFill )
4677  {
4678  tmpColor.setAlpha( 0 );
4679  }
4680 
4681  // store buffer's drawing in QPicture for drop shadow call
4682  QPicture buffPict;
4683  QPainter buffp;
4684  buffp.begin( &buffPict );
4685  buffp.setPen( pen );
4686  buffp.setBrush( tmpColor );
4687  buffp.drawPath( path );
4688  buffp.end();
4689 
4690  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowBuffer )
4691  {
4692  QgsLabelComponent bufferComponent = component;
4693  bufferComponent.setOrigin( QgsPoint( 0.0, 0.0 ) );
4694  bufferComponent.setPicture( &buffPict );
4695  bufferComponent.setPictureBuffer( penSize / 2.0 );
4696  drawLabelShadow( context, bufferComponent, tmpLyr );
4697  }
4698 
4699  p->save();
4700  if ( context.useAdvancedEffects() )
4701  {
4702  p->setCompositionMode( tmpLyr.bufferBlendMode );
4703  }
4704 // p->setPen( pen );
4705 // p->setBrush( tmpColor );
4706 // p->drawPath( path );
4707 
4708  // scale for any print output or image saving @ specific dpi
4709  p->scale( component.dpiRatio(), component.dpiRatio() );
4710  _fixQPictureDPI( p );
4711  p->drawPicture( 0, 0, buffPict );
4712  p->restore();
4713 }
4714 
4716  QgsLabelComponent component,
4717  const QgsPalLayerSettings& tmpLyr )
4718 {
4719  QPainter* p = context.painter();
4720  double labelWidth = component.size().x(), labelHeight = component.size().y();
4721  //QgsDebugMsgLevel( QString( "Background label rotation: %1" ).arg( component.rotation() ), 4 );
4722 
4723  // shared calculations between shapes and SVG
4724 
4725  // configure angles, set component rotation and rotationOffset
4727  {
4728  component.setRotation( -( component.rotation() * 180 / M_PI ) ); // RotationSync
4729  component.setRotationOffset(
4731  }
4732  else // RotationFixed
4733  {
4734  component.setRotation( 0.0 ); // don't use label's rotation
4735  component.setRotationOffset( tmpLyr.shapeRotation );
4736  }
4737 
4738  // mm to map units conversion factor
4739  double mmToMapUnits = tmpLyr.shapeSizeMapUnitScale.computeMapUnitsPerPixel( context ) * context.scaleFactor();
4740 
4741  // TODO: the following label-buffered generated shapes and SVG symbols should be moved into marker symbology classes
4742 
4743  if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeSVG )
4744  {
4745  // all calculations done in shapeSizeUnits, which are then passed to symbology class for painting
4746 
4747  if ( tmpLyr.shapeSVGFile.isEmpty() )
4748  return;
4749 
4750  double sizeOut = 0.0;
4751  // only one size used for SVG sizing/scaling (no use of shapeSize.y() or Y field in gui)
4753  {
4754  sizeOut = tmpLyr.shapeSize.x();
4755  }
4756  else if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeBuffer )
4757  {
4758  // add buffer to greatest dimension of label
4759  if ( labelWidth >= labelHeight )
4760  sizeOut = labelWidth;
4761  else if ( labelHeight > labelWidth )
4762  sizeOut = labelHeight;
4763 
4764  // label size in map units, convert to shapeSizeUnits, if different
4765  if ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM )
4766  {
4767  sizeOut /= mmToMapUnits;
4768  }
4769 
4770  // add buffer
4771  sizeOut += tmpLyr.shapeSize.x() * 2;
4772  }
4773 
4774  // don't bother rendering symbols smaller than 1x1 pixels in size
4775  // TODO: add option to not show any svgs under/over a certian size
4776  if ( tmpLyr.scaleToPixelContext( sizeOut, context, tmpLyr.shapeSizeUnits, false, tmpLyr.shapeSizeMapUnitScale ) < 1.0 )
4777  return;
4778 
4779  QgsStringMap map; // for SVG symbology marker
4780  map["name"] = QgsSymbolLayerV2Utils::symbolNameToPath( tmpLyr.shapeSVGFile.trimmed() );
4781  map["size"] = QString::number( sizeOut );
4782  map["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
4784  map["angle"] = QString::number( 0.0 ); // angle is handled by this local painter
4785 
4786  // offset is handled by this local painter
4787  // TODO: see why the marker renderer doesn't seem to translate offset *after* applying rotation
4788  //map["offset"] = QgsSymbolLayerV2Utils::encodePoint( tmpLyr.shapeOffset );
4789  //map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
4790  // tmpLyr.shapeOffsetUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
4791 
4792  map["fill"] = tmpLyr.shapeFillColor.name();
4793  map["outline"] = tmpLyr.shapeBorderColor.name();
4794  map["outline-width"] = QString::number( tmpLyr.shapeBorderWidth );
4795 
4796  // TODO: fix overriding SVG symbol's border width/units in QgsSvgCache
4797  // currently broken, fall back to symbol's
4798  //map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
4799  // tmpLyr.shapeBorderWidthUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
4800 
4801  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowShape )
4802  {
4803  // configure SVG shadow specs
4804  QgsStringMap shdwmap( map );
4805  shdwmap["fill"] = tmpLyr.shadowColor.name();
4806  shdwmap["outline"] = tmpLyr.shadowColor.name();
4807  shdwmap["size"] = QString::number( sizeOut * tmpLyr.rasterCompressFactor );
4808 
4809  // store SVG's drawing in QPicture for drop shadow call
4810  QPicture svgPict;
4811  QPainter svgp;
4812  svgp.begin( &svgPict );
4813 
4814  // draw shadow symbol
4815 
4816  // clone current render context map unit/mm conversion factors, but not
4817  // other map canvas parameters, then substitute this painter for use in symbology painting
4818  // NOTE: this is because the shadow needs to be scaled correctly for output to map canvas,
4819  // but will be created relative to the SVG's computed size, not the current map canvas
4820  QgsRenderContext shdwContext;
4821  shdwContext.setMapToPixel( context.mapToPixel() );
4822  shdwContext.setScaleFactor( context.scaleFactor() );
4823  shdwContext.setPainter( &svgp );
4824 
4825  QgsSymbolLayerV2* symShdwL = QgsSvgMarkerSymbolLayerV2::create( shdwmap );
4826  QgsSvgMarkerSymbolLayerV2* svgShdwM = static_cast<QgsSvgMarkerSymbolLayerV2*>( symShdwL );
4827  QgsSymbolV2RenderContext svgShdwContext( shdwContext, QgsSymbolV2::Mixed,
4828  ( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
4829 
4830  double svgSize = tmpLyr.scaleToPixelContext( sizeOut, context, tmpLyr.shapeSizeUnits, true, tmpLyr.shapeSizeMapUnitScale );
4831  svgShdwM->renderPoint( QPointF( svgSize / 2, -svgSize / 2 ), svgShdwContext );
4832  svgp.end();
4833 
4834  component.setPicture( &svgPict );
4835  // TODO: when SVG symbol's border width/units is fixed in QgsSvgCache, adjust for it here
4836  component.setPictureBuffer( 0.0 );
4837 
4838  component.setSize( QgsPoint( svgSize, svgSize ) );
4839  component.setOffset( QgsPoint( 0.0, 0.0 ) );
4840 
4841  // rotate about origin center of SVG
4842  p->save();
4843  p->translate( component.center().x(), component.center().y() );
4844  p->rotate( component.rotation() );
4845  p->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
4846  double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits, true, tmpLyr.shapeOffsetMapUnitScale );
4847  double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits, true, tmpLyr.shapeOffsetMapUnitScale );
4848  p->translate( QPointF( xoff, yoff ) );
4849  p->rotate( component.rotationOffset() );
4850  p->translate( -svgSize / 2, svgSize / 2 );
4851 
4852  drawLabelShadow( context, component, tmpLyr );
4853  p->restore();
4854 
4855  delete svgShdwM;
4856  svgShdwM = 0;
4857  }
4858 
4859  // draw the actual symbol
4861  QgsSvgMarkerSymbolLayerV2* svgM = static_cast<QgsSvgMarkerSymbolLayerV2*>( symL );
4862  QgsSymbolV2RenderContext svgContext( context, QgsSymbolV2::Mixed,
4863  ( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
4864 
4865  p->save();
4866  if ( context.useAdvancedEffects() )
4867  {
4868  p->setCompositionMode( tmpLyr.shapeBlendMode );
4869  }
4870  p->translate( component.center().x(), component.center().y() );
4871  p->rotate( component.rotation() );
4872  double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits, false, tmpLyr.shapeOffsetMapUnitScale );
4873  double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits, false, tmpLyr.shapeOffsetMapUnitScale );
4874  p->translate( QPointF( xoff, yoff ) );
4875  p->rotate( component.rotationOffset() );
4876  svgM->renderPoint( QPointF( 0, 0 ), svgContext );
4877  p->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
4878  p->restore();
4879 
4880  delete svgM;
4881  svgM = 0;
4882 
4883  }
4884  else // Generated Shapes
4885  {
4886  // all calculations done in shapeSizeUnits
4887 
4888  double w = labelWidth / ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM ? mmToMapUnits : 1 );
4889  double h = labelHeight / ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM ? mmToMapUnits : 1 );
4890 
4891  double xsize = tmpLyr.shapeSize.x();
4892  double ysize = tmpLyr.shapeSize.y();
4893 
4895  {
4896  w = xsize;
4897  h = ysize;
4898  }
4899  else if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeBuffer )
4900  {
4902  {
4903  if ( w > h )
4904  h = w;
4905  else if ( h > w )
4906  w = h;
4907  }
4908  else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeCircle )
4909  {
4910  // start with label bound by circle
4911  h = sqrt( pow( w, 2 ) + pow( h, 2 ) );
4912  w = h;
4913  }
4914  else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeEllipse )
4915  {
4916  // start with label bound by ellipse
4917  h = h / sqrt( 2.0 ) * 2;
4918  w = w / sqrt( 2.0 ) * 2;
4919  }
4920 
4921  w += xsize * 2;
4922  h += ysize * 2;
4923  }
4924 
4925  // convert everything over to map pixels from here on
4926  w = tmpLyr.scaleToPixelContext( w, context, tmpLyr.shapeSizeUnits, true, tmpLyr.shapeSizeMapUnitScale );
4927  h = tmpLyr.scaleToPixelContext( h, context, tmpLyr.shapeSizeUnits, true, tmpLyr.shapeSizeMapUnitScale );
4928 
4929  // offsets match those of symbology: -x = left, -y = up
4930  QRectF rect( -w / 2.0, - h / 2.0, w, h );
4931 
4932  if ( rect.isNull() )
4933  return;
4934 
4935  p->save();
4936  p->translate( QPointF( component.center().x(), component.center().y() ) );
4937  p->rotate( component.rotation() );
4938  double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits, false, tmpLyr.shapeOffsetMapUnitScale );
4939  double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits, false, tmpLyr.shapeOffsetMapUnitScale );
4940  p->translate( QPointF( xoff, yoff ) );
4941  p->rotate( component.rotationOffset() );
4942 
4943  double penSize = tmpLyr.scaleToPixelContext( tmpLyr.shapeBorderWidth, context, tmpLyr.shapeBorderWidthUnits, true, tmpLyr.shapeBorderWidthMapUnitScale );
4944 
4945  QPen pen;
4946  if ( tmpLyr.shapeBorderWidth > 0 )
4947  {
4948  pen.setColor( tmpLyr.shapeBorderColor );
4949  pen.setWidthF( penSize );
4951  pen.setJoinStyle( tmpLyr.shapeJoinStyle );
4952  }
4953  else
4954  {
4955  pen = Qt::NoPen;
4956  }
4957 
4958  // store painting in QPicture for shadow drawing
4959  QPicture shapePict;
4960  QPainter shapep;
4961  shapep.begin( &shapePict );
4962  shapep.setPen( pen );
4963  shapep.setBrush( tmpLyr.shapeFillColor );
4964 
4967  {
4969  {
4970  shapep.drawRoundedRect( rect, tmpLyr.shapeRadii.x(), tmpLyr.shapeRadii.y(), Qt::RelativeSize );
4971  }
4972  else
4973  {
4974  double xRadius = tmpLyr.scaleToPixelContext( tmpLyr.shapeRadii.x(), context, tmpLyr.shapeRadiiUnits, true, tmpLyr.shapeRadiiMapUnitScale );
4975  double yRadius = tmpLyr.scaleToPixelContext( tmpLyr.shapeRadii.y(), context, tmpLyr.shapeRadiiUnits, true, tmpLyr.shapeRadiiMapUnitScale );
4976  shapep.drawRoundedRect( rect, xRadius, yRadius );
4977  }
4978  }
4979  else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeEllipse
4981  {
4982  shapep.drawEllipse( rect );
4983  }
4984  shapep.end();
4985 
4986  p->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
4987 
4988  if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowShape )
4989  {
4990  component.setPicture( &shapePict );
4991  component.setPictureBuffer( penSize / 2.0 );
4992 
4993  component.setSize( QgsPoint( rect.width(), rect.height() ) );
4994  component.setOffset( QgsPoint( rect.width() / 2, -rect.height() / 2 ) );
4995  drawLabelShadow( context, component, tmpLyr );
4996  }
4997 
4998  p->setOpacity(( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
4999  if ( context.useAdvancedEffects() )
5000  {
5001  p->setCompositionMode( tmpLyr.shapeBlendMode );
5002  }
5003 
5004  // scale for any print output or image saving @ specific dpi
5005  p->scale( component.dpiRatio(), component.dpiRatio() );
5006  _fixQPictureDPI( p );
5007  p->drawPicture( 0, 0, shapePict );
5008  p->restore();
5009  }
5010 }
5011 
5013  const QgsLabelComponent& component,
5014  const QgsPalLayerSettings& tmpLyr )
5015 {
5016  // incoming component sizes should be multiplied by rasterCompressFactor, as
5017  // this allows shadows to be created at paint device dpi (e.g. high resolution),
5018  // then scale device painter by 1.0 / rasterCompressFactor for output
5019 
5020  QPainter* p = context.painter();
5021  double componentWidth = component.size().x(), componentHeight = component.size().y();
5022  double xOffset = component.offset().x(), yOffset = component.offset().y();
5023  double pictbuffer = component.pictureBuffer();
5024 
5025  // generate pixmap representation of label component drawing
5026  bool mapUnits = ( tmpLyr.shadowRadiusUnits == QgsPalLayerSettings::MapUnits );
5027  double radius = tmpLyr.scaleToPixelContext( tmpLyr.shadowRadius, context, tmpLyr.shadowRadiusUnits, !mapUnits, tmpLyr.shadowRadiusMapUnitScale );
5028  radius /= ( mapUnits ? tmpLyr.vectorScaleFactor / component.dpiRatio() : 1 );
5029  radius = ( int )( radius + 0.5 );
5030 
5031  // TODO: add labeling gui option to adjust blurBufferClippingScale to minimize pixels, or
5032  // to ensure shadow isn't clipped too tight. (Or, find a better method of buffering)
5033  double blurBufferClippingScale = 3.75;
5034  int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
5035 
5036  QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
5037  componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
5038  QImage::Format_ARGB32_Premultiplied );
5039 
5040  // TODO: add labeling gui option to not show any shadows under/over a certian size
5041  // keep very small QImages from causing paint device issues, i.e. must be at least > 1
5042  int minBlurImgSize = 1;
5043  // max limitation on QgsSvgCache is 10,000 for screen, which will probably be reasonable for future caching here, too
5044  // 4 x QgsSvgCache limit for output to print/image at higher dpi
5045  // TODO: should it be higher, scale with dpi, or have no limit? Needs testing with very large labels rendered at high dpi output
5046  int maxBlurImgSize = 40000;
5047  if ( blurImg.isNull()
5048  || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
5049  || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
5050  return;
5051 
5052  blurImg.fill( QColor( Qt::transparent ).rgba() );
5053  QPainter pictp;
5054  if ( !pictp.begin( &blurImg ) )
5055  return;
5056  pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
5057  QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
5058  blurbuffer + pictbuffer + componentHeight + yOffset );
5059 
5060  pictp.drawPicture( imgOffset,
5061  *component.picture() );
5062 
5063  // overlay shadow color
5064  pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
5065  pictp.fillRect( blurImg.rect(), tmpLyr.shadowColor );
5066  pictp.end();
5067 
5068  // blur the QImage in-place
5069  if ( tmpLyr.shadowRadius > 0.0 && radius > 0 )
5070  {
5071  QgsSymbolLayerV2Utils::blurImageInPlace( blurImg, blurImg.rect(), radius, tmpLyr.shadowRadiusAlphaOnly );
5072  }
5073 
5074  if ( tmpLyr.showingShadowRects ) // engine setting, not per layer
5075  {
5076  // debug rect for QImage shadow registration and clipping visualization
5077  QPainter picti;
5078  picti.begin( &blurImg );
5079  picti.setBrush( Qt::Dense7Pattern );
5080  QPen imgPen( QColor( 0, 0, 255, 255 ) );
5081  imgPen.setWidth( 1 );
5082  picti.setPen( imgPen );
5083  picti.setOpacity( 0.1 );
5084  picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
5085  picti.end();
5086  }
5087 
5088  double offsetDist = tmpLyr.scaleToPixelContext( tmpLyr.shadowOffsetDist, context, tmpLyr.shadowOffsetUnits, true, tmpLyr.shadowOffsetMapUnitScale );
5089  double angleRad = tmpLyr.shadowOffsetAngle * M_PI / 180; // to radians
5090  if ( tmpLyr.shadowOffsetGlobal )
5091  {
5092  // TODO: check for differences in rotation origin and cw/ccw direction,
5093  // when this shadow function is used for something other than labels
5094 
5095  // it's 0-->cw-->360 for labels
5096  //QgsDebugMsgLevel( QString( "Shadow aggregated label rotation (degrees): %1" ).arg( component.rotation() + component.rotationOffset() ), 4 );
5097  angleRad -= ( component.rotation() * M_PI / 180 + component.rotationOffset() * M_PI / 180 );
5098  }
5099 
5100  QPointF transPt( -offsetDist * cos( angleRad + M_PI / 2 ),
5101  -offsetDist * sin( angleRad + M_PI / 2 ) );
5102 
5103  p->save();
5104  p->setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
5105  if ( context.useAdvancedEffects() )
5106  {
5107  p->setCompositionMode( tmpLyr.shadowBlendMode );
5108  }
5109  p->setOpacity(( 100.0 - ( double )( tmpLyr.shadowTransparency ) ) / 100.0 );
5110 
5111  double scale = ( double )tmpLyr.shadowScale / 100.0;
5112  // TODO: scale from center/center, left/center or left/top, instead of default left/bottom?
5113  p->scale( scale, scale );
5114  if ( component.useOrigin() )
5115  {
5116  p->translate( component.origin().x(), component.origin().y() );
5117  }
5118  p->translate( transPt );
5119  p->translate( -imgOffset.x(),
5120  -imgOffset.y() );
5121  p->drawImage( 0, 0, blurImg );
5122  p->restore();
5123 
5124  // debug rects
5125  if ( tmpLyr.showingShadowRects ) // engine setting, not per layer
5126  {
5127  // draw debug rect for QImage painting registration
5128  p->save();
5129  p->setBrush( Qt::NoBrush );
5130  QPen imgPen( QColor( 255, 0, 0, 10 ) );
5131  imgPen.setWidth( 2 );
5132  imgPen.setStyle( Qt::DashLine );
5133  p->setPen( imgPen );
5134  p->scale( scale, scale );
5135  if ( component.useOrigin() )
5136  {
5137  p->translate( component.origin().x(), component.origin().y() );
5138  }
5139  p->translate( transPt );
5140  p->translate( -imgOffset.x(),
5141  -imgOffset.y() );
5142  p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
5143  p->restore();
5144 
5145  // draw debug rect for passed in component dimensions
5146  p->save();
5147  p->setBrush( Qt::NoBrush );
5148  QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
5149  componentRectPen.setWidth( 1 );
5150  if ( component.useOrigin() )
5151  {
5152  p->translate( component.origin().x(), component.origin().y() );
5153  }
5154  p->setPen( componentRectPen );
5155  p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
5156  p->restore();
5157  }
5158 }
5159 
5161 {
5162  // start with engine defaults for new project, or project that has no saved settings
5163  Pal p;
5164  bool saved = false;
5166  "PAL", "/SearchMethod", ( int )p.getSearch(), &saved ) );
5168  "PAL", "/CandidatesPoint", p.getPointP(), &saved );
5170  "PAL", "/CandidatesLine", p.getLineP(), &saved );
5172  "PAL", "/CandidatesPolygon", p.getPolyP(), &saved );
5174  "PAL", "/ShowingCandidates", false, &saved );
5176  "PAL", "/ShowingShadowRects", false, &saved );
5178  "PAL", "/ShowingAllLabels", false, &saved );
5180  "PAL", "/ShowingPartialsLabels", p.getShowPartial(), &saved );
5182  "PAL", "/DrawOutlineLabels", true, &saved );
5183 }
5184 
5186 {
5187  QgsProject::instance()->writeEntry( "PAL", "/SearchMethod", ( int )mSearch );
5188  QgsProject::instance()->writeEntry( "PAL", "/CandidatesPoint", mCandPoint );
5189  QgsProject::instance()->writeEntry( "PAL", "/CandidatesLine", mCandLine );
5190  QgsProject::instance()->writeEntry( "PAL", "/CandidatesPolygon", mCandPolygon );
5191  QgsProject::instance()->writeEntry( "PAL", "/ShowingCandidates", mShowingCandidates );
5192  QgsProject::instance()->writeEntry( "PAL", "/ShowingShadowRects", mShowingShadowRects );
5193  QgsProject::instance()->writeEntry( "PAL", "/ShowingAllLabels", mShowingAllLabels );
5194  QgsProject::instance()->writeEntry( "PAL", "/ShowingPartialsLabels", mShowingPartialsLabels );
5195  QgsProject::instance()->writeEntry( "PAL", "/DrawOutlineLabels", mDrawOutlineLabels );
5196 }
5197 
5199 {
5200  QgsProject::instance()->removeEntry( "PAL", "/SearchMethod" );
5201  QgsProject::instance()->removeEntry( "PAL", "/CandidatesPoint" );
5202  QgsProject::instance()->removeEntry( "PAL", "/CandidatesLine" );
5203  QgsProject::instance()->removeEntry( "PAL", "/CandidatesPolygon" );
5204  QgsProject::instance()->removeEntry( "PAL", "/ShowingCandidates" );
5205  QgsProject::instance()->removeEntry( "PAL", "/ShowingShadowRects" );
5206  QgsProject::instance()->removeEntry( "PAL", "/ShowingAllLabels" );
5207  QgsProject::instance()->removeEntry( "PAL", "/ShowingPartialsLabels" );
5208  QgsProject::instance()->removeEntry( "PAL", "/DrawOutlineLabels" );
5209 }
5210 
5212 {
5213  QgsPalLabeling* lbl = new QgsPalLabeling();
5219  return lbl;
5220 }
5221 
5222 
5224 {
5225  mLabelSearchTree = new QgsLabelSearchTree();
5226 }
5227 
5229 {
5230  delete mLabelSearchTree;
5231  mLabelSearchTree = NULL;
5232 }
5233 
5234 QList<QgsLabelPosition> QgsLabelingResults::labelsAtPosition( const QgsPoint& p ) const
5235 {
5236  QList<QgsLabelPosition> positions;
5237 
5238  QList<QgsLabelPosition*> positionPointers;
5239  if ( mLabelSearchTree )
5240  {
5241  mLabelSearchTree->label( p, positionPointers );
5242  QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
5243  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
5244  {
5245  positions.push_back( QgsLabelPosition( **pointerIt ) );
5246  }
5247  }
5248 
5249  return positions;
5250 }
5251 
5252 QList<QgsLabelPosition> QgsLabelingResults::labelsWithinRect( const QgsRectangle& r ) const
5253 {
5254  QList<QgsLabelPosition> positions;
5255 
5256  QList<QgsLabelPosition*> positionPointers;
5257  if ( mLabelSearchTree )
5258  {
5259  mLabelSearchTree->labelsInRect( r, positionPointers );
5260  QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
5261  for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
5262  {
5263  positions.push_back( QgsLabelPosition( **pointerIt ) );
5264  }
5265  }
5266 
5267  return positions;
5268 }
const QgsMapSettings & mapSettings()
bridge to QgsMapSettings
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:51
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:69
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)
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)