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