QGIS API Documentation  master-59fd5e0
src/core/qgspallabeling.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002   qgspallabeling.cpp
00003   Smart labeling for vector layers
00004   -------------------
00005          begin                : June 2009
00006          copyright            : (C) Martin Dobias
00007          email                : wonder dot sk at gmail dot com
00008 
00009  ***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "qgspallabeling.h"
00019 
00020 #include <list>
00021 
00022 #include <pal/pal.h>
00023 #include <pal/feature.h>
00024 #include <pal/layer.h>
00025 #include <pal/palgeometry.h>
00026 #include <pal/palexception.h>
00027 #include <pal/problem.h>
00028 #include <pal/labelposition.h>
00029 
00030 #include <geos_c.h>
00031 
00032 #include <cmath>
00033 
00034 #include <QApplication>
00035 #include <QByteArray>
00036 #include <QString>
00037 #include <QFontMetrics>
00038 #include <QTime>
00039 #include <QPainter>
00040 
00041 #include "diagram/qgsdiagram.h"
00042 #include "qgsdiagramrendererv2.h"
00043 #include "qgsfontutils.h"
00044 #include "qgslabelsearchtree.h"
00045 #include "qgsexpression.h"
00046 #include "qgsdatadefined.h"
00047 
00048 #include <qgslogger.h>
00049 #include <qgsvectorlayer.h>
00050 #include <qgsmaplayerregistry.h>
00051 #include <qgsvectordataprovider.h>
00052 #include <qgsgeometry.h>
00053 #include <qgsmaprenderer.h>
00054 #include <qgsmarkersymbollayerv2.h>
00055 #include <qgsproject.h>
00056 #include "qgssymbolv2.h"
00057 #include "qgssymbollayerv2utils.h"
00058 #include <QMessageBox>
00059 
00060 using namespace pal;
00061 
00062 
00063 class QgsPalGeometry : public PalGeometry
00064 {
00065   public:
00066     QgsPalGeometry( QgsFeatureId id, QString text, GEOSGeometry* g,
00067                     qreal ltrSpacing = 0.0, qreal wordSpacing = 0.0, bool curvedLabeling = false )
00068         : mG( g )
00069         , mText( text )
00070         , mId( id )
00071         , mInfo( NULL )
00072         , mIsDiagram( false )
00073         , mIsPinned( false )
00074         , mFontMetrics( NULL )
00075         , mLetterSpacing( ltrSpacing )
00076         , mWordSpacing( wordSpacing )
00077         , mCurvedLabeling( curvedLabeling )
00078     {
00079       mStrId = FID_TO_STRING( id ).toAscii();
00080       mDefinedFont = QFont();
00081     }
00082 
00083     ~QgsPalGeometry()
00084     {
00085       if ( mG )
00086         GEOSGeom_destroy( mG );
00087       delete mInfo;
00088       delete mFontMetrics;
00089     }
00090 
00091     // getGeosGeometry + releaseGeosGeometry is called twice: once when adding, second time when labeling
00092 
00093     GEOSGeometry* getGeosGeometry()
00094     {
00095       return mG;
00096     }
00097     void releaseGeosGeometry( GEOSGeometry* /*geom*/ )
00098     {
00099       // nothing here - we'll delete the geometry in destructor
00100     }
00101 
00102     const char* strId() { return mStrId.data(); }
00103     QString text() { return mText; }
00104 
00105     pal::LabelInfo* info( QFontMetricsF* fm, const QgsMapToPixel* xform, double fontScale, double maxinangle, double maxoutangle )
00106     {
00107       if ( mInfo )
00108         return mInfo;
00109 
00110       mFontMetrics = new QFontMetricsF( *fm ); // duplicate metrics for when drawing label
00111 
00112       // max angle between curved label characters (20.0/-20.0 was default in QGIS <= 1.8)
00113       if ( maxinangle < 20.0 )
00114         maxinangle = 20.0;
00115       if ( 60.0 < maxinangle )
00116         maxinangle = 60.0;
00117       if ( maxoutangle > -20.0 )
00118         maxoutangle = -20.0;
00119       if ( -95.0 > maxoutangle )
00120         maxoutangle = -95.0;
00121 
00122       // create label info!
00123       QgsPoint ptZero = xform->toMapCoordinates( 0, 0 );
00124       QgsPoint ptSize = xform->toMapCoordinatesF( 0.0, -fm->height() / fontScale );
00125 
00126       // mLetterSpacing/mWordSpacing = 0.0 is default for non-curved labels
00127       // (non-curved spacings handled by Qt in QgsPalLayerSettings/QgsPalLabeling)
00128       qreal charWidth;
00129       qreal wordSpaceFix;
00130       mInfo = new pal::LabelInfo( mText.count(), ptSize.y() - ptZero.y(), maxinangle, maxoutangle );
00131       for ( int i = 0; i < mText.count(); i++ )
00132       {
00133         mInfo->char_info[i].chr = mText[i].unicode();
00134 
00135         // reconstruct how Qt creates word spacing, then adjust per individual stored character
00136         // this will allow PAL to create each candidate width = character width + correct spacing
00137         charWidth = fm->width( mText[i] );
00138         if ( mCurvedLabeling )
00139         {
00140           wordSpaceFix = qreal( 0.0 );
00141           if ( mText[i] == QString( " " )[0] )
00142           {
00143             // word spacing only gets added once at end of consecutive run of spaces, see QTextEngine::shapeText()
00144             int nxt = i + 1;
00145             wordSpaceFix = ( nxt < mText.count() && mText[nxt] != QString( " " )[0] ) ? mWordSpacing : qreal( 0.0 );
00146           }
00147           if ( fm->width( QString( mText[i] ) ) - fm->width( mText[i] ) - mLetterSpacing != qreal( 0.0 ) )
00148           {
00149             // word spacing applied when it shouldn't be
00150             wordSpaceFix -= mWordSpacing;
00151           }
00152           charWidth = fm->width( QString( mText[i] ) ) + wordSpaceFix;
00153         }
00154 
00155         ptSize = xform->toMapCoordinatesF((( double ) charWidth ) / fontScale , 0.0 );
00156         mInfo->char_info[i].width = ptSize.x() - ptZero.x();
00157       }
00158       return mInfo;
00159     }
00160 
00161     const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& dataDefinedValues() const { return mDataDefinedValues; }
00162     void addDataDefinedValue( QgsPalLayerSettings::DataDefinedProperties p, QVariant v ) { mDataDefinedValues.insert( p, v ); }
00163 
00164     void setIsDiagram( bool d ) { mIsDiagram = d; }
00165     bool isDiagram() const { return mIsDiagram; }
00166 
00167     void setIsPinned( bool f ) { mIsPinned = f; }
00168     bool isPinned() const { return mIsPinned; }
00169 
00170     void setDefinedFont( QFont f ) { mDefinedFont = QFont( f ); }
00171     QFont definedFont() { return mDefinedFont; }
00172 
00173     QFontMetricsF* getLabelFontMetrics() { return mFontMetrics; }
00174 
00175     void setDiagramAttributes( const QgsAttributes& attrs ) { mDiagramAttributes = attrs; }
00176     const QgsAttributes& diagramAttributes() { return mDiagramAttributes; }
00177 
00178   protected:
00179     GEOSGeometry* mG;
00180     QString mText;
00181     QByteArray mStrId;
00182     QgsFeatureId mId;
00183     LabelInfo* mInfo;
00184     bool mIsDiagram;
00185     bool mIsPinned;
00186     QFont mDefinedFont;
00187     QFontMetricsF* mFontMetrics;
00188     qreal mLetterSpacing; // for use with curved labels
00189     qreal mWordSpacing; // for use with curved labels
00190     bool mCurvedLabeling; // whether the geometry is to be used for curved labeling placement
00192     QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant > mDataDefinedValues;
00193 
00195     QgsAttributes mDiagramAttributes;
00196 };
00197 
00198 // -------------
00199 
00200 QgsPalLayerSettings::QgsPalLayerSettings()
00201     : palLayer( NULL )
00202     , mCurFeat( 0 )
00203     , mCurFields( 0 )
00204     , ct( NULL )
00205     , extentGeom( NULL )
00206     , mFeaturesToLabel( 0 )
00207     , mFeatsSendingToPal( 0 )
00208     , mFeatsRegPal( 0 )
00209     , expression( NULL )
00210 {
00211   enabled = false;
00212 
00213   // text style
00214   textFont = QApplication::font();
00215   textNamedStyle = QString( "" );
00216   fontSizeInMapUnits = false;
00217   textColor = Qt::black;
00218   textTransp = 0;
00219   blendMode = QPainter::CompositionMode_SourceOver;
00220   previewBkgrdColor = Qt::white;
00221   // font processing info
00222   mTextFontFound = true;
00223   mTextFontFamily = QApplication::font().family();
00224 
00225   // text formatting
00226   wrapChar = "";
00227   multilineHeight = 1.0;
00228   multilineAlign = MultiLeft;
00229   addDirectionSymbol = false;
00230   leftDirectionSymbol = QString( "<" );
00231   rightDirectionSymbol = QString( ">" );
00232   reverseDirectionSymbol = false;
00233   placeDirectionSymbol = SymbolLeftRight;
00234   formatNumbers = false;
00235   decimals = 3;
00236   plusSign = false;
00237 
00238   // text buffer
00239   bufferDraw = false;
00240   bufferSize = 1.0;
00241   bufferSizeInMapUnits = false;
00242   bufferColor = Qt::white;
00243   bufferTransp = 0;
00244   bufferNoFill = false;
00245   bufferJoinStyle = Qt::BevelJoin;
00246   bufferBlendMode = QPainter::CompositionMode_SourceOver;
00247 
00248   // shape background
00249   shapeDraw = false;
00250   shapeType = ShapeRectangle;
00251   shapeSVGFile = QString();
00252   shapeSizeType = SizeBuffer;
00253   shapeSize = QPointF( 0.0, 0.0 );
00254   shapeSizeUnits = MM;
00255   shapeRotationType = RotationSync;
00256   shapeRotation = 0.0;
00257   shapeOffset = QPointF( 0.0, 0.0 );
00258   shapeOffsetUnits = MM;
00259   shapeRadii = QPointF( 0.0, 0.0 );
00260   shapeRadiiUnits = MM;
00261   shapeFillColor = Qt::white;
00262   shapeBorderColor = Qt::darkGray;
00263   shapeBorderWidth = 0.0;
00264   shapeBorderWidthUnits = MM;
00265   shapeJoinStyle = Qt::BevelJoin;
00266   shapeTransparency = 0;
00267   shapeBlendMode = QPainter::CompositionMode_SourceOver;
00268 
00269   // drop shadow
00270   shadowDraw = false;
00271   shadowUnder = ShadowLowest;
00272   shadowOffsetAngle = 135;
00273   shadowOffsetDist = 1.0;
00274   shadowOffsetUnits = MM;
00275   shadowOffsetGlobal = true;
00276   shadowRadius = 1.5;
00277   shadowRadiusUnits = MM;
00278   shadowRadiusAlphaOnly = false;
00279   shadowTransparency = 30;
00280   shadowScale = 100;
00281   shadowColor = Qt::black;
00282   shadowBlendMode = QPainter::CompositionMode_Multiply;
00283 
00284   // placement
00285   placement = AroundPoint;
00286   placementFlags = 0;
00287   centroidWhole = false;
00288   quadOffset = QuadrantOver;
00289   xOffset = 0;
00290   yOffset = 0;
00291   labelOffsetInMapUnits = true;
00292   dist = 0;
00293   distInMapUnits = false;
00294   angleOffset = 0;
00295   preserveRotation = true;
00296   maxCurvedCharAngleIn = 20.0;
00297   maxCurvedCharAngleOut = -20.0;
00298   priority = 5;
00299 
00300   // rendering
00301   scaleVisibility = false;
00302   scaleMin = 1;
00303   scaleMax = 10000000;
00304   fontLimitPixelSize = false;
00305   fontMinPixelSize = 0; //trigger to turn it on by default for map unit labels
00306   fontMaxPixelSize = 10000;
00307   displayAll = false;
00308   upsidedownLabels = Upright;
00309 
00310   labelPerPart = false;
00311   mergeLines = false;
00312   minFeatureSize = 0.0;
00313   limitNumLabels = false;
00314   maxNumLabels = 2000;
00315   obstacle = true;
00316 
00317   // scale factors
00318   vectorScaleFactor = 1.0;
00319   rasterCompressFactor = 1.0;
00320 
00321   // data defined string and old-style index values
00322   // NOTE: in QPair use -1 for second value (other values are for old-style layer properties migration)
00323 
00324   // text style
00325   mDataDefinedNames.insert( Size, QPair<QString, int>( "Size", 0 ) );
00326   mDataDefinedNames.insert( Bold, QPair<QString, int>( "Bold", 1 ) );
00327   mDataDefinedNames.insert( Italic, QPair<QString, int>( "Italic", 2 ) );
00328   mDataDefinedNames.insert( Underline, QPair<QString, int>( "Underline", 3 ) );
00329   mDataDefinedNames.insert( Color, QPair<QString, int>( "Color", 4 ) );
00330   mDataDefinedNames.insert( Strikeout, QPair<QString, int>( "Strikeout", 5 ) );
00331   mDataDefinedNames.insert( Family, QPair<QString, int>( "Family", 6 ) );
00332   mDataDefinedNames.insert( FontStyle, QPair<QString, int>( "FontStyle", -1 ) );
00333   mDataDefinedNames.insert( FontSizeUnit, QPair<QString, int>( "FontSizeUnit", -1 ) );
00334   mDataDefinedNames.insert( FontTransp, QPair<QString, int>( "FontTransp", 18 ) );
00335   mDataDefinedNames.insert( FontCase, QPair<QString, int>( "FontCase", -1 ) );
00336   mDataDefinedNames.insert( FontLetterSpacing, QPair<QString, int>( "FontLetterSpacing", -1 ) );
00337   mDataDefinedNames.insert( FontWordSpacing, QPair<QString, int>( "FontWordSpacing", -1 ) );
00338   mDataDefinedNames.insert( FontBlendMode, QPair<QString, int>( "FontBlendMode", -1 ) );
00339 
00340   // text formatting
00341   mDataDefinedNames.insert( MultiLineWrapChar, QPair<QString, int>( "MultiLineWrapChar", -1 ) );
00342   mDataDefinedNames.insert( MultiLineHeight, QPair<QString, int>( "MultiLineHeight", -1 ) );
00343   mDataDefinedNames.insert( MultiLineAlignment, QPair<QString, int>( "MultiLineAlignment", -1 ) );
00344   mDataDefinedNames.insert( DirSymbDraw, QPair<QString, int>( "DirSymbDraw", -1 ) );
00345   mDataDefinedNames.insert( DirSymbLeft, QPair<QString, int>( "DirSymbLeft", -1 ) );
00346   mDataDefinedNames.insert( DirSymbRight, QPair<QString, int>( "DirSymbRight", -1 ) );
00347   mDataDefinedNames.insert( DirSymbPlacement, QPair<QString, int>( "DirSymbPlacement", -1 ) );
00348   mDataDefinedNames.insert( DirSymbReverse, QPair<QString, int>( "DirSymbReverse", -1 ) );
00349   mDataDefinedNames.insert( NumFormat, QPair<QString, int>( "NumFormat", -1 ) );
00350   mDataDefinedNames.insert( NumDecimals, QPair<QString, int>( "NumDecimals", -1 ) );
00351   mDataDefinedNames.insert( NumPlusSign, QPair<QString, int>( "NumPlusSign", -1 ) );
00352 
00353   // text buffer
00354   mDataDefinedNames.insert( BufferDraw, QPair<QString, int>( "BufferDraw", -1 ) );
00355   mDataDefinedNames.insert( BufferSize, QPair<QString, int>( "BufferSize", 7 ) );
00356   mDataDefinedNames.insert( BufferUnit, QPair<QString, int>( "BufferUnit", -1 ) );
00357   mDataDefinedNames.insert( BufferColor, QPair<QString, int>( "BufferColor", 8 ) );
00358   mDataDefinedNames.insert( BufferTransp, QPair<QString, int>( "BufferTransp", 19 ) );
00359   mDataDefinedNames.insert( BufferJoinStyle, QPair<QString, int>( "BufferJoinStyle", -1 ) );
00360   mDataDefinedNames.insert( BufferBlendMode, QPair<QString, int>( "BufferBlendMode", -1 ) );
00361 
00362   // background
00363   mDataDefinedNames.insert( ShapeDraw, QPair<QString, int>( "ShapeDraw", -1 ) );
00364   mDataDefinedNames.insert( ShapeKind, QPair<QString, int>( "ShapeKind", -1 ) );
00365   mDataDefinedNames.insert( ShapeSVGFile, QPair<QString, int>( "ShapeSVGFile", -1 ) );
00366   mDataDefinedNames.insert( ShapeSizeType, QPair<QString, int>( "ShapeSizeType", -1 ) );
00367   mDataDefinedNames.insert( ShapeSizeX, QPair<QString, int>( "ShapeSizeX", -1 ) );
00368   mDataDefinedNames.insert( ShapeSizeY, QPair<QString, int>( "ShapeSizeY", -1 ) );
00369   mDataDefinedNames.insert( ShapeSizeUnits, QPair<QString, int>( "ShapeSizeUnits", -1 ) );
00370   mDataDefinedNames.insert( ShapeRotationType, QPair<QString, int>( "ShapeRotationType", -1 ) );
00371   mDataDefinedNames.insert( ShapeRotation, QPair<QString, int>( "ShapeRotation", -1 ) );
00372   mDataDefinedNames.insert( ShapeOffset, QPair<QString, int>( "ShapeOffset", -1 ) );
00373   mDataDefinedNames.insert( ShapeOffsetUnits, QPair<QString, int>( "ShapeOffsetUnits", -1 ) );
00374   mDataDefinedNames.insert( ShapeRadii, QPair<QString, int>( "ShapeRadii", -1 ) );
00375   mDataDefinedNames.insert( ShapeRadiiUnits, QPair<QString, int>( "ShapeRadiiUnits", -1 ) );
00376   mDataDefinedNames.insert( ShapeTransparency, QPair<QString, int>( "ShapeTransparency", -1 ) );
00377   mDataDefinedNames.insert( ShapeBlendMode, QPair<QString, int>( "ShapeBlendMode", -1 ) );
00378   mDataDefinedNames.insert( ShapeFillColor, QPair<QString, int>( "ShapeFillColor", -1 ) );
00379   mDataDefinedNames.insert( ShapeBorderColor, QPair<QString, int>( "ShapeBorderColor", -1 ) );
00380   mDataDefinedNames.insert( ShapeBorderWidth, QPair<QString, int>( "ShapeBorderWidth", -1 ) );
00381   mDataDefinedNames.insert( ShapeBorderWidthUnits, QPair<QString, int>( "ShapeBorderWidthUnits", -1 ) );
00382   mDataDefinedNames.insert( ShapeJoinStyle, QPair<QString, int>( "ShapeJoinStyle", -1 ) );
00383 
00384   // drop shadow
00385   mDataDefinedNames.insert( ShadowDraw, QPair<QString, int>( "ShadowDraw", -1 ) );
00386   mDataDefinedNames.insert( ShadowUnder, QPair<QString, int>( "ShadowUnder", -1 ) );
00387   mDataDefinedNames.insert( ShadowOffsetAngle, QPair<QString, int>( "ShadowOffsetAngle", -1 ) );
00388   mDataDefinedNames.insert( ShadowOffsetDist, QPair<QString, int>( "ShadowOffsetDist", -1 ) );
00389   mDataDefinedNames.insert( ShadowOffsetUnits, QPair<QString, int>( "ShadowOffsetUnits", -1 ) );
00390   mDataDefinedNames.insert( ShadowRadius, QPair<QString, int>( "ShadowRadius", -1 ) );
00391   mDataDefinedNames.insert( ShadowRadiusUnits, QPair<QString, int>( "ShadowRadiusUnits", -1 ) );
00392   mDataDefinedNames.insert( ShadowTransparency, QPair<QString, int>( "ShadowTransparency", -1 ) );
00393   mDataDefinedNames.insert( ShadowScale, QPair<QString, int>( "ShadowScale", -1 ) );
00394   mDataDefinedNames.insert( ShadowColor, QPair<QString, int>( "ShadowColor", -1 ) );
00395   mDataDefinedNames.insert( ShadowBlendMode, QPair<QString, int>( "ShadowBlendMode", -1 ) );
00396 
00397   // placement
00398   mDataDefinedNames.insert( CentroidWhole, QPair<QString, int>( "CentroidWhole", -1 ) );
00399   mDataDefinedNames.insert( OffsetQuad, QPair<QString, int>( "OffsetQuad", -1 ) );
00400   mDataDefinedNames.insert( OffsetXY, QPair<QString, int>( "OffsetXY", -1 ) );
00401   mDataDefinedNames.insert( OffsetUnits, QPair<QString, int>( "OffsetUnits", -1 ) );
00402   mDataDefinedNames.insert( LabelDistance, QPair<QString, int>( "LabelDistance", 13 ) );
00403   mDataDefinedNames.insert( DistanceUnits, QPair<QString, int>( "DistanceUnits", -1 ) );
00404   mDataDefinedNames.insert( OffsetRotation, QPair<QString, int>( "OffsetRotation", -1 ) );
00405   mDataDefinedNames.insert( CurvedCharAngleInOut, QPair<QString, int>( "CurvedCharAngleInOut", -1 ) );
00406   // (data defined only)
00407   mDataDefinedNames.insert( PositionX, QPair<QString, int>( "PositionX", 9 ) );
00408   mDataDefinedNames.insert( PositionY, QPair<QString, int>( "PositionY", 10 ) );
00409   mDataDefinedNames.insert( Hali, QPair<QString, int>( "Hali", 11 ) );
00410   mDataDefinedNames.insert( Vali, QPair<QString, int>( "Vali", 12 ) );
00411   mDataDefinedNames.insert( Rotation, QPair<QString, int>( "Rotation", 14 ) );
00412 
00413   //rendering
00414   mDataDefinedNames.insert( ScaleVisibility, QPair<QString, int>( "ScaleVisibility", -1 ) );
00415   mDataDefinedNames.insert( MinScale, QPair<QString, int>( "MinScale", 16 ) );
00416   mDataDefinedNames.insert( MaxScale, QPair<QString, int>( "MaxScale", 17 ) );
00417   mDataDefinedNames.insert( FontLimitPixel, QPair<QString, int>( "FontLimitPixel", -1 ) );
00418   mDataDefinedNames.insert( FontMinPixel, QPair<QString, int>( "FontMinPixel", -1 ) );
00419   mDataDefinedNames.insert( FontMaxPixel, QPair<QString, int>( "FontMaxPixel", -1 ) );
00420   // (data defined only)
00421   mDataDefinedNames.insert( Show, QPair<QString, int>( "Show", 15 ) );
00422   mDataDefinedNames.insert( AlwaysShow, QPair<QString, int>( "AlwaysShow", 20 ) );
00423 
00424   // temp stuff for when drawing label components (don't copy)
00425   showingShadowRects = false;
00426 }
00427 
00428 QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
00429 {
00430   // copy only permanent stuff
00431 
00432   enabled = s.enabled;
00433 
00434   // text style
00435   fieldName = s.fieldName;
00436   isExpression = s.isExpression;
00437   textFont = s.textFont;
00438   textNamedStyle = s.textNamedStyle;
00439   fontSizeInMapUnits = s.fontSizeInMapUnits;
00440   textColor = s.textColor;
00441   textTransp = s.textTransp;
00442   blendMode = s.blendMode;
00443   previewBkgrdColor = s.previewBkgrdColor;
00444   // font processing info
00445   mTextFontFound = s.mTextFontFound;
00446   mTextFontFamily = s.mTextFontFamily;
00447 
00448   // text formatting
00449   wrapChar = s.wrapChar;
00450   multilineHeight = s.multilineHeight;
00451   multilineAlign = s.multilineAlign;
00452   addDirectionSymbol = s.addDirectionSymbol;
00453   leftDirectionSymbol = s.leftDirectionSymbol;
00454   rightDirectionSymbol = s.rightDirectionSymbol;
00455   reverseDirectionSymbol = s.reverseDirectionSymbol;
00456   placeDirectionSymbol = s.placeDirectionSymbol;
00457   formatNumbers = s.formatNumbers;
00458   decimals = s.decimals;
00459   plusSign = s.plusSign;
00460 
00461   // text buffer
00462   bufferDraw = s.bufferDraw;
00463   bufferSize = s.bufferSize;
00464   bufferSizeInMapUnits = s.bufferSizeInMapUnits;
00465   bufferColor = s.bufferColor;
00466   bufferTransp = s.bufferTransp;
00467   bufferNoFill = s.bufferNoFill;
00468   bufferJoinStyle = s.bufferJoinStyle;
00469   bufferBlendMode = s.bufferBlendMode;
00470 
00471   // placement
00472   placement = s.placement;
00473   placementFlags = s.placementFlags;
00474   centroidWhole = s.centroidWhole;
00475   quadOffset = s.quadOffset;
00476   xOffset = s.xOffset;
00477   yOffset = s.yOffset;
00478   labelOffsetInMapUnits = s.labelOffsetInMapUnits;
00479   dist = s.dist;
00480   distInMapUnits = s.distInMapUnits;
00481   angleOffset = s.angleOffset;
00482   preserveRotation = s.preserveRotation;
00483   maxCurvedCharAngleIn = s.maxCurvedCharAngleIn;
00484   maxCurvedCharAngleOut = s.maxCurvedCharAngleOut;
00485   priority = s.priority;
00486 
00487   // rendering
00488   scaleVisibility = s.scaleVisibility;
00489   scaleMin = s.scaleMin;
00490   scaleMax = s.scaleMax;
00491   fontLimitPixelSize = s.fontLimitPixelSize;
00492   fontMinPixelSize = s.fontMinPixelSize;
00493   fontMaxPixelSize = s.fontMaxPixelSize;
00494   displayAll = s.displayAll;
00495   upsidedownLabels = s.upsidedownLabels;
00496 
00497   labelPerPart = s.labelPerPart;
00498   mergeLines = s.mergeLines;
00499   minFeatureSize = s.minFeatureSize;
00500   limitNumLabels = s.limitNumLabels;
00501   maxNumLabels = s.maxNumLabels;
00502   obstacle = s.obstacle;
00503 
00504   // shape background
00505   shapeDraw = s.shapeDraw;
00506   shapeType = s.shapeType;
00507   shapeSVGFile = s.shapeSVGFile;
00508   shapeSizeType = s.shapeSizeType;
00509   shapeSize = s.shapeSize;
00510   shapeSizeUnits = s.shapeSizeUnits;
00511   shapeRotationType = s.shapeRotationType;
00512   shapeRotation = s.shapeRotation;
00513   shapeOffset = s.shapeOffset;
00514   shapeOffsetUnits = s.shapeOffsetUnits;
00515   shapeRadii = s.shapeRadii;
00516   shapeRadiiUnits = s.shapeRadiiUnits;
00517   shapeFillColor = s.shapeFillColor;
00518   shapeBorderColor = s.shapeBorderColor;
00519   shapeBorderWidth = s.shapeBorderWidth;
00520   shapeBorderWidthUnits = s.shapeBorderWidthUnits;
00521   shapeJoinStyle = s.shapeJoinStyle;
00522   shapeTransparency = s.shapeTransparency;
00523   shapeBlendMode = s.shapeBlendMode;
00524 
00525   // drop shadow
00526   shadowDraw = s.shadowDraw;
00527   shadowUnder = s.shadowUnder;
00528   shadowOffsetAngle = s.shadowOffsetAngle;
00529   shadowOffsetDist = s.shadowOffsetDist;
00530   shadowOffsetUnits = s.shadowOffsetUnits;
00531   shadowOffsetGlobal = s.shadowOffsetGlobal;
00532   shadowRadius = s.shadowRadius;
00533   shadowRadiusUnits = s.shadowRadiusUnits;
00534   shadowRadiusAlphaOnly = s.shadowRadiusAlphaOnly;
00535   shadowTransparency = s.shadowTransparency;
00536   shadowScale = s.shadowScale;
00537   shadowColor = s.shadowColor;
00538   shadowBlendMode = s.shadowBlendMode;
00539 
00540   // data defined
00541   dataDefinedProperties = s.dataDefinedProperties;
00542   mDataDefinedNames = s.mDataDefinedNames;
00543 
00544   // scale factors
00545   vectorScaleFactor = s.vectorScaleFactor;
00546   rasterCompressFactor = s.rasterCompressFactor;
00547 
00548   ct = NULL;
00549   extentGeom = NULL;
00550   expression = NULL;
00551 }
00552 
00553 
00554 QgsPalLayerSettings::~QgsPalLayerSettings()
00555 {
00556   // pal layer is deleted internally in PAL
00557 
00558   delete ct;
00559   delete expression;
00560   delete extentGeom;
00561 
00562   // clear pointers to QgsDataDefined objects
00563   dataDefinedProperties.clear();
00564 }
00565 
00566 QgsExpression* QgsPalLayerSettings::getLabelExpression()
00567 {
00568   if ( expression == NULL )
00569   {
00570     expression = new QgsExpression( fieldName );
00571   }
00572   return expression;
00573 }
00574 
00575 static QColor _readColor( QgsVectorLayer* layer, QString property, QColor defaultColor = Qt::black, bool withAlpha = true )
00576 {
00577   int r = layer->customProperty( property + "R", QVariant( defaultColor.red() ) ).toInt();
00578   int g = layer->customProperty( property + "G", QVariant( defaultColor.green() ) ).toInt();
00579   int b = layer->customProperty( property + "B", QVariant( defaultColor.blue() ) ).toInt();
00580   int a = withAlpha ? layer->customProperty( property + "A", QVariant( defaultColor.alpha() ) ).toInt() : 255;
00581   return QColor( r, g, b, a );
00582 }
00583 
00584 static void _writeColor( QgsVectorLayer* layer, QString property, QColor color, bool withAlpha = true )
00585 {
00586   layer->setCustomProperty( property + "R", color.red() );
00587   layer->setCustomProperty( property + "G", color.green() );
00588   layer->setCustomProperty( property + "B", color.blue() );
00589   if ( withAlpha )
00590     layer->setCustomProperty( property + "A", color.alpha() );
00591 }
00592 
00593 static QgsPalLayerSettings::SizeUnit _decodeUnits( const QString& str )
00594 {
00595   if ( str.compare( "Point", Qt::CaseInsensitive ) == 0
00596        || str.compare( "Points", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Points;
00597   if ( str.compare( "MapUnit", Qt::CaseInsensitive ) == 0
00598        || str.compare( "MapUnits", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::MapUnits;
00599   if ( str.compare( "Percent", Qt::CaseInsensitive ) == 0 ) return QgsPalLayerSettings::Percent;
00600   return QgsPalLayerSettings::MM; // "MM"
00601 }
00602 
00603 static QPainter::CompositionMode _decodeBlendMode( const QString& str )
00604 {
00605   if ( str.compare( "Lighten", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Lighten;
00606   if ( str.compare( "Screen", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Screen;
00607   if ( str.compare( "Dodge", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_ColorDodge;
00608   if ( str.compare( "Addition", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Plus;
00609   if ( str.compare( "Darken", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Darken;
00610   if ( str.compare( "Multiply", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Multiply;
00611   if ( str.compare( "Burn", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_ColorBurn;
00612   if ( str.compare( "Overlay", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Overlay;
00613   if ( str.compare( "SoftLight", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_SoftLight;
00614   if ( str.compare( "HardLight", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_HardLight;
00615   if ( str.compare( "Difference", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Difference;
00616   if ( str.compare( "Subtract", Qt::CaseInsensitive ) == 0 ) return QPainter::CompositionMode_Exclusion;
00617   return QPainter::CompositionMode_SourceOver; // "Normal"
00618 }
00619 
00620 static Qt::PenJoinStyle _decodePenJoinStyle( const QString& str )
00621 {
00622   if ( str.compare( "Miter", Qt::CaseInsensitive ) == 0 ) return Qt::MiterJoin;
00623   if ( str.compare( "Round", Qt::CaseInsensitive ) == 0 ) return Qt::RoundJoin;
00624   return Qt::BevelJoin; // "Bevel"
00625 }
00626 
00627 void QgsPalLayerSettings::readDataDefinedPropertyMap( QgsVectorLayer* layer,
00628     QMap < QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* > & propertyMap )
00629 {
00630   if ( !layer )
00631   {
00632     return;
00633   }
00634 
00635   QMapIterator<QgsPalLayerSettings::DataDefinedProperties, QPair<QString, int> > i( mDataDefinedNames );
00636   while ( i.hasNext() )
00637   {
00638     i.next();
00639     readDataDefinedProperty( layer, i.key(), propertyMap );
00640   }
00641 }
00642 
00643 void QgsPalLayerSettings::writeDataDefinedPropertyMap( QgsVectorLayer* layer,
00644     const QMap < QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* > & propertyMap )
00645 {
00646   if ( !layer )
00647   {
00648     return;
00649   }
00650 
00651   QMapIterator<QgsPalLayerSettings::DataDefinedProperties, QPair<QString, int> > i( mDataDefinedNames );
00652   while ( i.hasNext() )
00653   {
00654     i.next();
00655     QString newPropertyName = "labeling/dataDefined/" + i.value().first;
00656     QVariant propertyValue = QVariant();
00657 
00658     QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator it = propertyMap.find( i.key() );
00659     if ( it != propertyMap.constEnd() )
00660     {
00661       QgsDataDefined* dd = it.value();
00662       if ( dd )
00663       {
00664         bool active = dd->isActive();
00665         bool useExpr = dd->useExpression();
00666         QString expr = dd->expressionString();
00667         QString field = dd->field();
00668 
00669         bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
00670 
00671         if ( !defaultVals )
00672         {
00673           // TODO: update this when project settings for labeling are migrated to better XML layout
00674           QStringList values;
00675           values << ( active ? "1" : "0" );
00676           values << ( useExpr ? "1" : "0" );
00677           values << expr;
00678           values << field;
00679           if ( !values.isEmpty() )
00680           {
00681             propertyValue = QVariant( values.join( "~~" ) );
00682           }
00683         }
00684       }
00685     }
00686 
00687     if ( propertyValue.isValid() )
00688     {
00689       layer->setCustomProperty( newPropertyName, propertyValue );
00690     }
00691     else
00692     {
00693       // remove unused properties
00694       layer->removeCustomProperty( newPropertyName );
00695     }
00696 
00697     if ( layer->customProperty( newPropertyName, QVariant() ).isValid() && i.value().second > -1 )
00698     {
00699       // remove old-style field index-based property, if still present
00700       layer->removeCustomProperty( QString( "labeling/dataDefinedProperty" ) + QString::number( i.value().second ) );
00701     }
00702   }
00703 }
00704 
00705 void QgsPalLayerSettings::readDataDefinedProperty( QgsVectorLayer* layer,
00706     QgsPalLayerSettings::DataDefinedProperties p,
00707     QMap < QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* > & propertyMap )
00708 {
00709   QString newPropertyName = "labeling/dataDefined/" + mDataDefinedNames.value( p ).first;
00710   QVariant newPropertyField = layer->customProperty( newPropertyName, QVariant() );
00711 
00712   QString ddString = QString();
00713   if ( newPropertyField.isValid() )
00714   {
00715     ddString = newPropertyField.toString();
00716   }
00717   else // maybe working with old-style field index-based property (< QGIS 2.0)
00718   {
00719     int oldIndx = mDataDefinedNames.value( p ).second;
00720 
00721     if ( oldIndx < 0 ) // something went wrong and we are working with new-style
00722     {
00723       return;
00724     }
00725 
00726     QString oldPropertyName = "labeling/dataDefinedProperty" + QString::number( oldIndx );
00727     QVariant oldPropertyField = layer->customProperty( oldPropertyName, QVariant() );
00728 
00729     if ( !oldPropertyField.isValid() )
00730     {
00731       return;
00732     }
00733 
00734     // switch from old-style field index- to name-based properties
00735     bool conversionOk;
00736     int indx = oldPropertyField.toInt( &conversionOk );
00737 
00738     if ( conversionOk )
00739     {
00740       // Fix to migrate from old-style vector api, where returned QMap keys possibly
00741       //   had 'holes' in sequence of field indices, e.g. 0,2,3
00742       // QgsAttrPalIndexNameHash provides a means of access field name in sequences from
00743       //   providers that procuded holes (e.g. PostGIS skipped geom column), otherwise it is empty
00744       QgsAttrPalIndexNameHash oldIndicesToNames = layer->dataProvider()->palAttributeIndexNames();
00745 
00746       if ( !oldIndicesToNames.isEmpty() )
00747       {
00748         ddString = oldIndicesToNames.value( indx );
00749       }
00750       else
00751       {
00752         QgsFields fields = layer->dataProvider()->fields();
00753         if ( indx < fields.size() ) // in case field count has changed
00754         {
00755           ddString = fields.at( indx ).name();
00756         }
00757       }
00758     }
00759 
00760     if ( !ddString.isEmpty() )
00761     {
00762       //upgrade any existing property to field name-based
00763       layer->setCustomProperty( newPropertyName, QVariant( updateDataDefinedString( ddString ) ) );
00764 
00765       // fix for buffer drawing triggered off of just its data defined size in the past (<2.0)
00766       if ( oldIndx == 7 ) // old bufferSize enum
00767       {
00768         bufferDraw = true;
00769         layer->setCustomProperty( "labeling/bufferDraw", true );
00770       }
00771 
00772       // fix for scale visibility limits triggered off of just its data defined values in the past (<2.0)
00773       if ( oldIndx == 16 || oldIndx == 17 ) // old minScale and maxScale enums
00774       {
00775         scaleVisibility = true;
00776         layer->setCustomProperty( "labeling/scaleVisibility", true );
00777       }
00778     }
00779 
00780     // remove old-style field index-based property
00781     layer->removeCustomProperty( oldPropertyName );
00782   }
00783 
00784   if ( !ddString.isEmpty() && ddString != QString( "0~~0~~~~" ) )
00785   {
00786     // TODO: update this when project settings for labeling are migrated to better XML layout
00787     QString newStyleString = updateDataDefinedString( ddString );
00788     QStringList ddv = newStyleString.split( "~~" );
00789 
00790     QgsDataDefined* dd = new QgsDataDefined( ddv.at( 0 ).toInt(), ddv.at( 1 ).toInt(), ddv.at( 2 ), ddv.at( 3 ) );
00791     propertyMap.insert( p, dd );
00792   }
00793   else
00794   {
00795     // remove unused properties
00796     layer->removeCustomProperty( newPropertyName );
00797   }
00798 }
00799 
00800 void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
00801 {
00802   if ( layer->customProperty( "labeling" ).toString() != QString( "pal" ) )
00803     return; // there's no information available
00804 
00805   // NOTE: set defaults for newly added properties, for backwards compatibility
00806 
00807   enabled = layer->customProperty( "labeling/enabled" ).toBool();
00808 
00809   // text style
00810   fieldName = layer->customProperty( "labeling/fieldName" ).toString();
00811   isExpression = layer->customProperty( "labeling/isExpression" ).toBool();
00812   QFont appFont = QApplication::font();
00813   mTextFontFamily = layer->customProperty( "labeling/fontFamily", QVariant( appFont.family() ) ).toString();
00814   QString fontFamily = mTextFontFamily;
00815   if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
00816   {
00817     // trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
00818     mTextFontFound = false;
00819 
00820     // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
00821     // currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
00822 
00823     // for now, do not use matching algorithm for substitution if family not found, substitute default instead
00824     fontFamily = appFont.family();
00825   }
00826 
00827   double fontSize = layer->customProperty( "labeling/fontSize" ).toDouble();
00828   fontSizeInMapUnits = layer->customProperty( "labeling/fontSizeInMapUnits" ).toBool();
00829   int fontWeight = layer->customProperty( "labeling/fontWeight" ).toInt();
00830   bool fontItalic = layer->customProperty( "labeling/fontItalic" ).toBool();
00831   textFont = QFont( fontFamily, fontSize, fontWeight, fontItalic );
00832   textFont.setPointSizeF( fontSize ); //double precision needed because of map units
00833   textNamedStyle = layer->customProperty( "labeling/namedStyle", QVariant( "" ) ).toString();
00834   QgsFontUtils::updateFontViaStyle( textFont, textNamedStyle ); // must come after textFont.setPointSizeF()
00835   textFont.setCapitalization(( QFont::Capitalization )layer->customProperty( "labeling/fontCapitals", QVariant( 0 ) ).toUInt() );
00836   textFont.setUnderline( layer->customProperty( "labeling/fontUnderline" ).toBool() );
00837   textFont.setStrikeOut( layer->customProperty( "labeling/fontStrikeout" ).toBool() );
00838   textFont.setLetterSpacing( QFont::AbsoluteSpacing, layer->customProperty( "labeling/fontLetterSpacing", QVariant( 0.0 ) ).toDouble() );
00839   textFont.setWordSpacing( layer->customProperty( "labeling/fontWordSpacing", QVariant( 0.0 ) ).toDouble() );
00840   textColor = _readColor( layer, "labeling/textColor", Qt::black, false );
00841   textTransp = layer->customProperty( "labeling/textTransp" ).toInt();
00842   blendMode = QgsMapRenderer::getCompositionMode(
00843                 ( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/blendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() );
00844   previewBkgrdColor = QColor( layer->customProperty( "labeling/previewBkgrdColor", QVariant( "#ffffff" ) ).toString() );
00845 
00846 
00847   // text formatting
00848   wrapChar = layer->customProperty( "labeling/wrapChar" ).toString();
00849   multilineHeight = layer->customProperty( "labeling/multilineHeight", QVariant( 1.0 ) ).toDouble();
00850   multilineAlign = ( MultiLineAlign )layer->customProperty( "labeling/multilineAlign", QVariant( MultiLeft ) ).toUInt();
00851   addDirectionSymbol = layer->customProperty( "labeling/addDirectionSymbol" ).toBool();
00852   leftDirectionSymbol = layer->customProperty( "labeling/leftDirectionSymbol", QVariant( "<" ) ).toString();
00853   rightDirectionSymbol = layer->customProperty( "labeling/rightDirectionSymbol", QVariant( ">" ) ).toString();
00854   reverseDirectionSymbol = layer->customProperty( "labeling/reverseDirectionSymbol" ).toBool();
00855   placeDirectionSymbol = ( DirectionSymbols )layer->customProperty( "labeling/placeDirectionSymbol", QVariant( SymbolLeftRight ) ).toUInt();
00856   formatNumbers = layer->customProperty( "labeling/formatNumbers" ).toBool();
00857   decimals = layer->customProperty( "labeling/decimals" ).toInt();
00858   plusSign = layer->customProperty( "labeling/plussign" ).toInt();
00859 
00860   // text buffer
00861   double bufSize = layer->customProperty( "labeling/bufferSize", QVariant( 0.0 ) ).toDouble();
00862 
00863   // fix for buffer being keyed off of just its size in the past (<2.0)
00864   QVariant drawBuffer = layer->customProperty( "labeling/bufferDraw", QVariant() );
00865   if ( drawBuffer.isValid() )
00866   {
00867     bufferDraw = drawBuffer.toBool();
00868     bufferSize = bufSize;
00869   }
00870   else if ( bufSize != 0.0 )
00871   {
00872     bufferDraw = true;
00873     bufferSize = bufSize;
00874   }
00875   else
00876   {
00877     // keep bufferSize at new 1.0 default
00878     bufferDraw = false;
00879   }
00880 
00881   bufferSizeInMapUnits = layer->customProperty( "labeling/bufferSizeInMapUnits" ).toBool();
00882   bufferColor = _readColor( layer, "labeling/bufferColor", Qt::white, false );
00883   bufferTransp = layer->customProperty( "labeling/bufferTransp" ).toInt();
00884   bufferBlendMode = QgsMapRenderer::getCompositionMode(
00885                       ( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/bufferBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() );
00886   bufferJoinStyle = ( Qt::PenJoinStyle )layer->customProperty( "labeling/bufferJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt();
00887   bufferNoFill = layer->customProperty( "labeling/bufferNoFill", QVariant( false ) ).toBool();
00888 
00889   // background
00890   shapeDraw = layer->customProperty( "labeling/shapeDraw", QVariant( false ) ).toBool();
00891   shapeType = ( ShapeType )layer->customProperty( "labeling/shapeType", QVariant( ShapeRectangle ) ).toUInt();
00892   shapeSVGFile = layer->customProperty( "labeling/shapeSVGFile", QVariant( "" ) ).toString();
00893   shapeSizeType = ( SizeType )layer->customProperty( "labeling/shapeSizeType", QVariant( SizeBuffer ) ).toUInt();
00894   shapeSize = QPointF( layer->customProperty( "labeling/shapeSizeX", QVariant( 0.0 ) ).toDouble(),
00895                        layer->customProperty( "labeling/shapeSizeY", QVariant( 0.0 ) ).toDouble() );
00896   shapeSizeUnits = ( SizeUnit )layer->customProperty( "labeling/shapeSizeUnits", QVariant( MM ) ).toUInt();
00897   shapeRotationType = ( RotationType )layer->customProperty( "labeling/shapeRotationType", QVariant( RotationSync ) ).toUInt();
00898   shapeRotation = layer->customProperty( "labeling/shapeRotation", QVariant( 0.0 ) ).toDouble();
00899   shapeOffset = QPointF( layer->customProperty( "labeling/shapeOffsetX", QVariant( 0.0 ) ).toDouble(),
00900                          layer->customProperty( "labeling/shapeOffsetY", QVariant( 0.0 ) ).toDouble() );
00901   shapeOffsetUnits = ( SizeUnit )layer->customProperty( "labeling/shapeOffsetUnits", QVariant( MM ) ).toUInt();
00902   shapeRadii = QPointF( layer->customProperty( "labeling/shapeRadiiX", QVariant( 0.0 ) ).toDouble(),
00903                         layer->customProperty( "labeling/shapeRadiiY", QVariant( 0.0 ) ).toDouble() );
00904   shapeRadiiUnits = ( SizeUnit )layer->customProperty( "labeling/shapeRadiiUnits", QVariant( MM ) ).toUInt();
00905   shapeFillColor = _readColor( layer, "labeling/shapeFillColor", Qt::white, true );
00906   shapeBorderColor = _readColor( layer, "labeling/shapeBorderColor", Qt::darkGray, true );
00907   shapeBorderWidth = layer->customProperty( "labeling/shapeBorderWidth", QVariant( .0 ) ).toDouble();
00908   shapeBorderWidthUnits = ( SizeUnit )layer->customProperty( "labeling/shapeBorderWidthUnits", QVariant( MM ) ).toUInt();
00909   shapeJoinStyle = ( Qt::PenJoinStyle )layer->customProperty( "labeling/shapeJoinStyle", QVariant( Qt::BevelJoin ) ).toUInt();
00910   shapeTransparency = layer->customProperty( "labeling/shapeTransparency", QVariant( 0 ) ).toInt();
00911   shapeBlendMode = QgsMapRenderer::getCompositionMode(
00912                      ( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/shapeBlendMode", QVariant( QgsMapRenderer::BlendNormal ) ).toUInt() );
00913 
00914   // drop shadow
00915   shadowDraw = layer->customProperty( "labeling/shadowDraw", QVariant( false ) ).toBool();
00916   shadowUnder = ( ShadowType )layer->customProperty( "labeling/shadowUnder", QVariant( ShadowLowest ) ).toUInt();//ShadowLowest;
00917   shadowOffsetAngle = layer->customProperty( "labeling/shadowOffsetAngle", QVariant( 135 ) ).toInt();
00918   shadowOffsetDist = layer->customProperty( "labeling/shadowOffsetDist", QVariant( 1.0 ) ).toDouble();
00919   shadowOffsetUnits = ( SizeUnit )layer->customProperty( "labeling/shadowOffsetUnits", QVariant( MM ) ).toUInt();
00920   shadowOffsetGlobal = layer->customProperty( "labeling/shadowOffsetGlobal", QVariant( true ) ).toBool();
00921   shadowRadius = layer->customProperty( "labeling/shadowRadius", QVariant( 1.5 ) ).toDouble();
00922   shadowRadiusUnits = ( SizeUnit )layer->customProperty( "labeling/shadowRadiusUnits", QVariant( MM ) ).toUInt();
00923   shadowRadiusAlphaOnly = layer->customProperty( "labeling/shadowRadiusAlphaOnly", QVariant( false ) ).toBool();
00924   shadowTransparency = layer->customProperty( "labeling/shadowTransparency", QVariant( 30 ) ).toInt();
00925   shadowScale = layer->customProperty( "labeling/shadowScale", QVariant( 100 ) ).toInt();
00926   shadowColor = _readColor( layer, "labeling/shadowColor", Qt::black, false );
00927   shadowBlendMode = QgsMapRenderer::getCompositionMode(
00928                       ( QgsMapRenderer::BlendMode )layer->customProperty( "labeling/shadowBlendMode", QVariant( QgsMapRenderer::BlendMultiply ) ).toUInt() );
00929 
00930   // placement
00931   placement = ( Placement )layer->customProperty( "labeling/placement" ).toInt();
00932   placementFlags = layer->customProperty( "labeling/placementFlags" ).toUInt();
00933   centroidWhole = layer->customProperty( "labeling/centroidWhole", QVariant( false ) ).toBool();
00934   dist = layer->customProperty( "labeling/dist" ).toDouble();
00935   distInMapUnits = layer->customProperty( "labeling/distInMapUnits" ).toBool();
00936   quadOffset = ( QuadrantPosition )layer->customProperty( "labeling/quadOffset", QVariant( QuadrantOver ) ).toUInt();
00937   xOffset = layer->customProperty( "labeling/xOffset", QVariant( 0.0 ) ).toDouble();
00938   yOffset = layer->customProperty( "labeling/yOffset", QVariant( 0.0 ) ).toDouble();
00939   labelOffsetInMapUnits = layer->customProperty( "labeling/labelOffsetInMapUnits", QVariant( true ) ).toBool();
00940   angleOffset = layer->customProperty( "labeling/angleOffset", QVariant( 0.0 ) ).toDouble();
00941   preserveRotation = layer->customProperty( "labeling/preserveRotation", QVariant( true ) ).toBool();
00942   maxCurvedCharAngleIn = layer->customProperty( "labeling/maxCurvedCharAngleIn", QVariant( 20.0 ) ).toDouble();
00943   maxCurvedCharAngleOut = layer->customProperty( "labeling/maxCurvedCharAngleOut", QVariant( -20.0 ) ).toDouble();
00944   priority = layer->customProperty( "labeling/priority" ).toInt();
00945 
00946   // rendering
00947   int scalemn = layer->customProperty( "labeling/scaleMin", QVariant( 0 ) ).toInt();
00948   int scalemx = layer->customProperty( "labeling/scaleMax", QVariant( 0 ) ).toInt();
00949 
00950   // fix for scale visibility limits being keyed off of just its values in the past (<2.0)
00951   QVariant scalevis = layer->customProperty( "labeling/scaleVisibility", QVariant() );
00952   if ( scalevis.isValid() )
00953   {
00954     scaleVisibility = scalevis.toBool();
00955     scaleMin = scalemn;
00956     scaleMax = scalemx;
00957   }
00958   else if ( scalemn > 0 || scalemx > 0 )
00959   {
00960     scaleVisibility = true;
00961     scaleMin = scalemn;
00962     scaleMax = scalemx;
00963   }
00964   else
00965   {
00966     // keep scaleMin and scaleMax at new 1.0 defaults (1 and 10000000, were 0 and 0)
00967     scaleVisibility = false;
00968   }
00969 
00970 
00971   fontLimitPixelSize = layer->customProperty( "labeling/fontLimitPixelSize", QVariant( false ) ).toBool();
00972   fontMinPixelSize = layer->customProperty( "labeling/fontMinPixelSize", QVariant( 0 ) ).toInt();
00973   fontMaxPixelSize = layer->customProperty( "labeling/fontMaxPixelSize", QVariant( 10000 ) ).toInt();
00974   displayAll = layer->customProperty( "labeling/displayAll", QVariant( false ) ).toBool();
00975   upsidedownLabels = ( UpsideDownLabels )layer->customProperty( "labeling/upsidedownLabels", QVariant( Upright ) ).toUInt();
00976 
00977   labelPerPart = layer->customProperty( "labeling/labelPerPart" ).toBool();
00978   mergeLines = layer->customProperty( "labeling/mergeLines" ).toBool();
00979   minFeatureSize = layer->customProperty( "labeling/minFeatureSize" ).toDouble();
00980   limitNumLabels = layer->customProperty( "labeling/limitNumLabels", QVariant( false ) ).toBool();
00981   maxNumLabels = layer->customProperty( "labeling/maxNumLabels", QVariant( 2000 ) ).toInt();
00982   obstacle = layer->customProperty( "labeling/obstacle", QVariant( true ) ).toBool();
00983 
00984   readDataDefinedPropertyMap( layer, dataDefinedProperties );
00985 }
00986 
00987 void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
00988 {
00989   // this is a mark that labeling information is present
00990   layer->setCustomProperty( "labeling", "pal" );
00991 
00992   layer->setCustomProperty( "labeling/enabled", enabled );
00993 
00994   // text style
00995   layer->setCustomProperty( "labeling/fieldName", fieldName );
00996   layer->setCustomProperty( "labeling/isExpression", isExpression );
00997   layer->setCustomProperty( "labeling/fontFamily", textFont.family() );
00998   layer->setCustomProperty( "labeling/namedStyle", textNamedStyle );
00999   layer->setCustomProperty( "labeling/fontSize", textFont.pointSizeF() );
01000   layer->setCustomProperty( "labeling/fontSizeInMapUnits", fontSizeInMapUnits );
01001   layer->setCustomProperty( "labeling/fontWeight", textFont.weight() );
01002   layer->setCustomProperty( "labeling/fontItalic", textFont.italic() );
01003   layer->setCustomProperty( "labeling/fontBold", textFont.bold() );
01004   layer->setCustomProperty( "labeling/fontStrikeout", textFont.strikeOut() );
01005   layer->setCustomProperty( "labeling/fontUnderline", textFont.underline() );
01006   _writeColor( layer, "labeling/textColor", textColor );
01007   layer->setCustomProperty( "labeling/fontCapitals", ( unsigned int )textFont.capitalization() );
01008   layer->setCustomProperty( "labeling/fontLetterSpacing", textFont.letterSpacing() );
01009   layer->setCustomProperty( "labeling/fontWordSpacing", textFont.wordSpacing() );
01010   layer->setCustomProperty( "labeling/textTransp", textTransp );
01011   layer->setCustomProperty( "labeling/blendMode", QgsMapRenderer::getBlendModeEnum( blendMode ) );
01012   layer->setCustomProperty( "labeling/previewBkgrdColor", previewBkgrdColor.name() );
01013 
01014   // text formatting
01015   layer->setCustomProperty( "labeling/wrapChar", wrapChar );
01016   layer->setCustomProperty( "labeling/multilineHeight", multilineHeight );
01017   layer->setCustomProperty( "labeling/multilineAlign", ( unsigned int )multilineAlign );
01018   layer->setCustomProperty( "labeling/addDirectionSymbol", addDirectionSymbol );
01019   layer->setCustomProperty( "labeling/leftDirectionSymbol", leftDirectionSymbol );
01020   layer->setCustomProperty( "labeling/rightDirectionSymbol", rightDirectionSymbol );
01021   layer->setCustomProperty( "labeling/reverseDirectionSymbol", reverseDirectionSymbol );
01022   layer->setCustomProperty( "labeling/placeDirectionSymbol", ( unsigned int )placeDirectionSymbol );
01023   layer->setCustomProperty( "labeling/formatNumbers", formatNumbers );
01024   layer->setCustomProperty( "labeling/decimals", decimals );
01025   layer->setCustomProperty( "labeling/plussign", plusSign );
01026 
01027   // text buffer
01028   layer->setCustomProperty( "labeling/bufferDraw", bufferDraw );
01029   layer->setCustomProperty( "labeling/bufferSize", bufferSize );
01030   layer->setCustomProperty( "labeling/bufferSizeInMapUnits", bufferSizeInMapUnits );
01031   _writeColor( layer, "labeling/bufferColor", bufferColor );
01032   layer->setCustomProperty( "labeling/bufferNoFill", bufferNoFill );
01033   layer->setCustomProperty( "labeling/bufferTransp", bufferTransp );
01034   layer->setCustomProperty( "labeling/bufferJoinStyle", ( unsigned int )bufferJoinStyle );
01035   layer->setCustomProperty( "labeling/bufferBlendMode", QgsMapRenderer::getBlendModeEnum( bufferBlendMode ) );
01036 
01037   // background
01038   layer->setCustomProperty( "labeling/shapeDraw", shapeDraw );
01039   layer->setCustomProperty( "labeling/shapeType", ( unsigned int )shapeType );
01040   layer->setCustomProperty( "labeling/shapeSVGFile", shapeSVGFile );
01041   layer->setCustomProperty( "labeling/shapeSizeType", ( unsigned int )shapeSizeType );
01042   layer->setCustomProperty( "labeling/shapeSizeX", shapeSize.x() );
01043   layer->setCustomProperty( "labeling/shapeSizeY", shapeSize.y() );
01044   layer->setCustomProperty( "labeling/shapeSizeUnits", ( unsigned int )shapeSizeUnits );
01045   layer->setCustomProperty( "labeling/shapeRotationType", ( unsigned int )shapeRotationType );
01046   layer->setCustomProperty( "labeling/shapeRotation", shapeRotation );
01047   layer->setCustomProperty( "labeling/shapeOffsetX", shapeOffset.x() );
01048   layer->setCustomProperty( "labeling/shapeOffsetY", shapeOffset.y() );
01049   layer->setCustomProperty( "labeling/shapeOffsetUnits", ( unsigned int )shapeOffsetUnits );
01050   layer->setCustomProperty( "labeling/shapeRadiiX", shapeRadii.x() );
01051   layer->setCustomProperty( "labeling/shapeRadiiY", shapeRadii.y() );
01052   layer->setCustomProperty( "labeling/shapeRadiiUnits", ( unsigned int )shapeRadiiUnits );
01053   _writeColor( layer, "labeling/shapeFillColor", shapeFillColor, true );
01054   _writeColor( layer, "labeling/shapeBorderColor", shapeBorderColor, true );
01055   layer->setCustomProperty( "labeling/shapeBorderWidth", shapeBorderWidth );
01056   layer->setCustomProperty( "labeling/shapeBorderWidthUnits", ( unsigned int )shapeBorderWidthUnits );
01057   layer->setCustomProperty( "labeling/shapeJoinStyle", ( unsigned int )shapeJoinStyle );
01058   layer->setCustomProperty( "labeling/shapeTransparency", shapeTransparency );
01059   layer->setCustomProperty( "labeling/shapeBlendMode", QgsMapRenderer::getBlendModeEnum( shapeBlendMode ) );
01060 
01061   // drop shadow
01062   layer->setCustomProperty( "labeling/shadowDraw", shadowDraw );
01063   layer->setCustomProperty( "labeling/shadowUnder", ( unsigned int )shadowUnder );
01064   layer->setCustomProperty( "labeling/shadowOffsetAngle", shadowOffsetAngle );
01065   layer->setCustomProperty( "labeling/shadowOffsetDist", shadowOffsetDist );
01066   layer->setCustomProperty( "labeling/shadowOffsetUnits", ( unsigned int )shadowOffsetUnits );
01067   layer->setCustomProperty( "labeling/shadowOffsetGlobal", shadowOffsetGlobal );
01068   layer->setCustomProperty( "labeling/shadowRadius", shadowRadius );
01069   layer->setCustomProperty( "labeling/shadowRadiusUnits", ( unsigned int )shadowRadiusUnits );
01070   layer->setCustomProperty( "labeling/shadowRadiusAlphaOnly", shadowRadiusAlphaOnly );
01071   layer->setCustomProperty( "labeling/shadowTransparency", shadowTransparency );
01072   layer->setCustomProperty( "labeling/shadowScale", shadowScale );
01073   _writeColor( layer, "labeling/shadowColor", shadowColor, false );
01074   layer->setCustomProperty( "labeling/shadowBlendMode", QgsMapRenderer::getBlendModeEnum( shadowBlendMode ) );
01075 
01076   // placement
01077   layer->setCustomProperty( "labeling/placement", placement );
01078   layer->setCustomProperty( "labeling/placementFlags", ( unsigned int )placementFlags );
01079   layer->setCustomProperty( "labeling/centroidWhole", centroidWhole );
01080   layer->setCustomProperty( "labeling/dist", dist );
01081   layer->setCustomProperty( "labeling/distInMapUnits", distInMapUnits );
01082   layer->setCustomProperty( "labeling/quadOffset", ( unsigned int )quadOffset );
01083   layer->setCustomProperty( "labeling/xOffset", xOffset );
01084   layer->setCustomProperty( "labeling/yOffset", yOffset );
01085   layer->setCustomProperty( "labeling/labelOffsetInMapUnits", labelOffsetInMapUnits );
01086   layer->setCustomProperty( "labeling/angleOffset", angleOffset );
01087   layer->setCustomProperty( "labeling/preserveRotation", preserveRotation );
01088   layer->setCustomProperty( "labeling/maxCurvedCharAngleIn", maxCurvedCharAngleIn );
01089   layer->setCustomProperty( "labeling/maxCurvedCharAngleOut", maxCurvedCharAngleOut );
01090   layer->setCustomProperty( "labeling/priority", priority );
01091 
01092   // rendering
01093   layer->setCustomProperty( "labeling/scaleVisibility", scaleVisibility );
01094   layer->setCustomProperty( "labeling/scaleMin", scaleMin );
01095   layer->setCustomProperty( "labeling/scaleMax", scaleMax );
01096   layer->setCustomProperty( "labeling/fontLimitPixelSize", fontLimitPixelSize );
01097   layer->setCustomProperty( "labeling/fontMinPixelSize", fontMinPixelSize );
01098   layer->setCustomProperty( "labeling/fontMaxPixelSize", fontMaxPixelSize );
01099   layer->setCustomProperty( "labeling/displayAll", displayAll );
01100   layer->setCustomProperty( "labeling/upsidedownLabels", ( unsigned int )upsidedownLabels );
01101 
01102   layer->setCustomProperty( "labeling/labelPerPart", labelPerPart );
01103   layer->setCustomProperty( "labeling/mergeLines", mergeLines );
01104   layer->setCustomProperty( "labeling/minFeatureSize", minFeatureSize );
01105   layer->setCustomProperty( "labeling/limitNumLabels", limitNumLabels );
01106   layer->setCustomProperty( "labeling/maxNumLabels", maxNumLabels );
01107   layer->setCustomProperty( "labeling/obstacle", obstacle );
01108 
01109   writeDataDefinedPropertyMap( layer, dataDefinedProperties );
01110 }
01111 
01112 void QgsPalLayerSettings::setDataDefinedProperty( QgsPalLayerSettings::DataDefinedProperties p,
01113     bool active, bool useExpr, const QString& expr, const QString& field )
01114 {
01115   bool defaultVals = ( !active && !useExpr && expr.isEmpty() && field.isEmpty() );
01116 
01117   if ( dataDefinedProperties.contains( p ) )
01118   {
01119     QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
01120     if ( it != dataDefinedProperties.constEnd() )
01121     {
01122       QgsDataDefined* dd = it.value();
01123       dd->setActive( active );
01124       dd->setUseExpression( useExpr );
01125       dd->setExpressionString( expr );
01126       dd->setField( field );
01127     }
01128   }
01129   else if ( !defaultVals )
01130   {
01131     QgsDataDefined* dd = new QgsDataDefined( active, useExpr, expr, field );
01132     dataDefinedProperties.insert( p, dd );
01133   }
01134 }
01135 
01136 void QgsPalLayerSettings::removeDataDefinedProperty( DataDefinedProperties p )
01137 {
01138   QMap< DataDefinedProperties, QgsDataDefined* >::iterator it = dataDefinedProperties.find( p );
01139   if ( it != dataDefinedProperties.end() )
01140   {
01141     delete( it.value() );
01142     dataDefinedProperties.erase( it );
01143   }
01144 }
01145 
01146 QString QgsPalLayerSettings::updateDataDefinedString( const QString& value )
01147 {
01148   // TODO: update or remove this when project settings for labeling are migrated to better XML layout
01149   QString newValue = value;
01150   if ( !value.isEmpty() && !value.contains( "~~" ) )
01151   {
01152     QStringList values;
01153     values << "1"; // all old-style values are active if not empty
01154     values << "0";
01155     values << "";
01156     values << value; // all old-style values are only field names
01157     newValue = values.join( "~~" );
01158   }
01159 
01160   return newValue;
01161 }
01162 
01163 QgsDataDefined* QgsPalLayerSettings::dataDefinedProperty( DataDefinedProperties p )
01164 {
01165   QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
01166   if ( it != dataDefinedProperties.constEnd() )
01167   {
01168     return it.value();
01169   }
01170   return 0;
01171 }
01172 
01173 QList<QgsPalLayerSettings::DataDefinedProperties> QgsPalLayerSettings::dataDefinedPropertyList()
01174 {
01175   return dataDefinedProperties.keys();
01176 }
01177 
01178 QMap<QString, QString> QgsPalLayerSettings::dataDefinedMap( DataDefinedProperties p ) const
01179 {
01180   QMap<QString, QString> map;
01181   QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
01182   if ( it != dataDefinedProperties.constEnd() )
01183   {
01184     return it.value()->toMap();
01185   }
01186   return map;
01187 }
01188 
01189 QVariant QgsPalLayerSettings::dataDefinedValue( DataDefinedProperties p, QgsFeature& f, const QgsFields& fields ) const
01190 {
01191   if ( !dataDefinedProperties.contains( p ) )
01192   {
01193     return QVariant();
01194   }
01195 
01196   QgsDataDefined* dd = 0;
01197   QMap< DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
01198   if ( it != dataDefinedProperties.constEnd() )
01199   {
01200     dd = it.value();
01201   }
01202 
01203   if ( !dd )
01204   {
01205     return QVariant();
01206   }
01207 
01208   if ( !dd->isActive() )
01209   {
01210     return QVariant();
01211   }
01212 
01213   QVariant result = QVariant();
01214   bool useExpression = dd->useExpression();
01215   QString field = dd->field();
01216 
01217   //QgsDebugMsgLevel( QString( "isActive:" ) + isActive ? "1" : "0", 4 );
01218   //QgsDebugMsgLevel( QString( "useExpression:" ) + useExpression ? "1" : "0", 4 );
01219   //QgsDebugMsgLevel( QString( "expression:" ) + dd->expressionString(), 4 );
01220   //QgsDebugMsgLevel( QString( "field:" ) + field, 4 );
01221 
01222   if ( useExpression && dd->expressionIsPrepared() )
01223   {
01224     QgsExpression* expr = dd->expression();
01225     //QgsDebugMsgLevel( QString( "expr columns:" ) + expr->referencedColumns().join( "," ), 4 );
01226 
01227     result = expr->evaluate( &f );
01228     if ( expr->hasEvalError() )
01229     {
01230       QgsDebugMsgLevel( QString( "Evaluate error:" ) + expr->evalErrorString(), 4 );
01231       return QVariant();
01232     }
01233   }
01234   else if ( !useExpression && !field.isEmpty() )
01235   {
01236     // use direct attribute access instead of evaluating "field" expression (much faster)
01237     int indx = fields.indexFromName( field );
01238     if ( indx != -1 )
01239     {
01240       result = f.attribute( indx );
01241     }
01242   }
01243   return result;
01244 }
01245 
01246 bool QgsPalLayerSettings::dataDefinedEvaluate( DataDefinedProperties p, QVariant& exprVal ) const
01247 {
01248   // null passed-around QVariant
01249   exprVal.clear();
01250 
01251   QVariant result = dataDefinedValue( p, *mCurFeat, *mCurFields );
01252 
01253   if ( result.isValid() ) // filter NULL values? i.e. && !result.isNull()
01254   {
01255     //QgsDebugMsgLevel( QString( "result type:" ) + QString( result.typeName() ), 4 );
01256     //QgsDebugMsgLevel( QString( "result string:" ) + result.toString(), 4 );
01257     exprVal = result;
01258     return true;
01259   }
01260 
01261   return false;
01262 }
01263 
01264 bool QgsPalLayerSettings::dataDefinedIsActive( DataDefinedProperties p ) const
01265 {
01266   bool isActive = false;
01267   QMap< DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
01268   if ( it != dataDefinedProperties.constEnd() )
01269   {
01270     isActive = it.value()->isActive();
01271   }
01272 
01273   return isActive;
01274 }
01275 
01276 bool QgsPalLayerSettings::dataDefinedUseExpression( DataDefinedProperties p ) const
01277 {
01278   bool useExpression = false;
01279   QMap< DataDefinedProperties, QgsDataDefined* >::const_iterator it = dataDefinedProperties.find( p );
01280   if ( it != dataDefinedProperties.constEnd() )
01281   {
01282     useExpression = it.value()->useExpression();
01283   }
01284 
01285   return useExpression;
01286 }
01287 
01288 bool QgsPalLayerSettings::checkMinimumSizeMM( const QgsRenderContext& ct, QgsGeometry* geom, double minSize ) const
01289 {
01290   if ( minSize <= 0 )
01291   {
01292     return true;
01293   }
01294 
01295   if ( !geom )
01296   {
01297     return false;
01298   }
01299 
01300   QGis::GeometryType featureType = geom->type();
01301   if ( featureType == QGis::Point ) //minimum size does not apply to point features
01302   {
01303     return true;
01304   }
01305 
01306   double mapUnitsPerMM = ct.mapToPixel().mapUnitsPerPixel() * ct.scaleFactor();
01307   if ( featureType == QGis::Line )
01308   {
01309     double length = geom->length();
01310     if ( length >= 0.0 )
01311     {
01312       return ( length >= ( minSize * mapUnitsPerMM ) );
01313     }
01314   }
01315   else if ( featureType == QGis::Polygon )
01316   {
01317     double area = geom->area();
01318     if ( area >= 0.0 )
01319     {
01320       return ( sqrt( area ) >= ( minSize * mapUnitsPerMM ) );
01321     }
01322   }
01323   return true; //should never be reached. Return true in this case to label such geometries anyway.
01324 }
01325 
01326 void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString text, double& labelX, double& labelY, QgsFeature* f )
01327 {
01328   if ( !fm || !f )
01329   {
01330     return;
01331   }
01332 
01333   QString wrapchr = wrapChar;
01334   double multilineH = multilineHeight;
01335 
01336   bool addDirSymb = addDirectionSymbol;
01337   QString leftDirSymb = leftDirectionSymbol;
01338   QString rightDirSymb = rightDirectionSymbol;
01339   QgsPalLayerSettings::DirectionSymbols placeDirSymb = placeDirectionSymbol;
01340 
01341   if ( f == mCurFeat ) // called internally, use any stored data defined values
01342   {
01343     if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
01344     {
01345       wrapchr = dataDefinedValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
01346     }
01347 
01348     if ( dataDefinedValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
01349     {
01350       multilineH = dataDefinedValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
01351     }
01352 
01353     if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
01354     {
01355       addDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
01356     }
01357 
01358     if ( addDirSymb )
01359     {
01360 
01361       if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
01362       {
01363         leftDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
01364       }
01365       if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbRight ) )
01366       {
01367         rightDirSymb = dataDefinedValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
01368       }
01369 
01370       if ( dataDefinedValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
01371       {
01372         placeDirSymb = ( QgsPalLayerSettings::DirectionSymbols )dataDefinedValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt();
01373       }
01374 
01375     }
01376 
01377   }
01378   else // called externally with passed-in feature, evaluate data defined
01379   {
01380     QVariant exprVal = QVariant();
01381 
01382     exprVal = dataDefinedValue( QgsPalLayerSettings::MultiLineWrapChar, *f, *mCurFields );
01383     if ( exprVal.isValid() )
01384     {
01385       wrapchr = exprVal.toString();
01386     }
01387     exprVal.clear();
01388     exprVal = dataDefinedValue( QgsPalLayerSettings::MultiLineHeight, *f, *mCurFields );
01389     if ( exprVal.isValid() )
01390     {
01391       bool ok;
01392       double size = exprVal.toDouble( &ok );
01393       if ( ok )
01394       {
01395         multilineH = size;
01396       }
01397     }
01398 
01399     exprVal.clear();
01400     exprVal = dataDefinedValue( QgsPalLayerSettings::DirSymbDraw, *f, *mCurFields );
01401     if ( exprVal.isValid() )
01402     {
01403       addDirSymb = exprVal.toBool();
01404     }
01405 
01406     if ( addDirSymb ) // don't do extra evaluations if not adding a direction symbol
01407     {
01408       exprVal.clear();
01409       exprVal = dataDefinedValue( QgsPalLayerSettings::DirSymbLeft, *f, *mCurFields );
01410       if ( exprVal.isValid() )
01411       {
01412         leftDirSymb = exprVal.toString();
01413       }
01414       exprVal.clear();
01415       exprVal = dataDefinedValue( QgsPalLayerSettings::DirSymbRight, *f, *mCurFields );
01416       if ( exprVal.isValid() )
01417       {
01418         rightDirSymb = exprVal.toString();
01419       }
01420       exprVal.clear();
01421       exprVal = dataDefinedValue( QgsPalLayerSettings::DirSymbPlacement, *f, *mCurFields );
01422       if ( exprVal.isValid() )
01423       {
01424         bool ok;
01425         int enmint = exprVal.toInt( &ok );
01426         if ( ok )
01427         {
01428           placeDirSymb = ( QgsPalLayerSettings::DirectionSymbols )enmint;
01429         }
01430       }
01431     }
01432 
01433   }
01434 
01435   if ( wrapchr.isEmpty() )
01436   {
01437     wrapchr = QString( "\n" ); // default to new line delimiter
01438   }
01439 
01440   //consider the space needed for the direction symbol
01441   if ( addDirSymb && placement == QgsPalLayerSettings::Line
01442        && ( !leftDirSymb.isEmpty() || !rightDirSymb.isEmpty() ) )
01443   {
01444     QString dirSym = leftDirSymb;
01445 
01446     if ( fm->width( rightDirSymb ) > fm->width( dirSym ) )
01447       dirSym = rightDirSymb;
01448 
01449     if ( placeDirSymb == QgsPalLayerSettings::SymbolLeftRight )
01450     {
01451       text.append( dirSym );
01452     }
01453     else
01454     {
01455       text.prepend( dirSym + wrapchr ); // SymbolAbove or SymbolBelow
01456     }
01457   }
01458 
01459   double w = 0.0, h = 0.0;
01460   QStringList multiLineSplit = text.split( wrapchr );
01461   int lines = multiLineSplit.size();
01462 
01463   double labelHeight = fm->ascent() + fm->descent(); // ignore +1 for baseline
01464 
01465   h += fm->height() + ( double )(( lines - 1 ) * labelHeight * multilineH );
01466   h /= rasterCompressFactor;
01467 
01468   for ( int i = 0; i < lines; ++i )
01469   {
01470     double width = fm->width( multiLineSplit.at( i ) );
01471     if ( width > w )
01472     {
01473       w = width;
01474     }
01475   }
01476   w /= rasterCompressFactor;
01477   QgsPoint ptSize = xform->toMapCoordinatesF( w, h );
01478 
01479   labelX = qAbs( ptSize.x() - ptZero.x() );
01480   labelY = qAbs( ptSize.y() - ptZero.y() );
01481 }
01482 
01483 void QgsPalLayerSettings::registerFeature( QgsVectorLayer* layer,  QgsFeature& f, const QgsRenderContext& context )
01484 {
01485   Q_UNUSED( layer );
01486 
01487   QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
01488   mCurFeat = &f;
01489 //  mCurFields = &layer->pendingFields();
01490 
01491   // store data defined-derived values for later adding to QgsPalGeometry for use during rendering
01492   dataDefinedValues.clear();
01493 
01494 
01495   // data defined show label? defaults to show label if not 0
01496   if ( dataDefinedEvaluate( QgsPalLayerSettings::Show, exprVal ) )
01497   {
01498     QgsDebugMsgLevel( QString( "exprVal Show:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
01499     if ( !exprVal.toBool() )
01500     {
01501       return;
01502     }
01503   }
01504 
01505   // data defined scale visibility?
01506   bool useScaleVisibility = scaleVisibility;
01507   if ( dataDefinedEvaluate( QgsPalLayerSettings::ScaleVisibility, exprVal ) )
01508   {
01509     QgsDebugMsgLevel( QString( "exprVal ScaleVisibility:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
01510     useScaleVisibility = exprVal.toBool();
01511   }
01512 
01513   if ( useScaleVisibility )
01514   {
01515     // data defined min scale?
01516     double minScale = scaleMin;
01517     if ( dataDefinedEvaluate( QgsPalLayerSettings::MinScale, exprVal ) )
01518     {
01519       QgsDebugMsgLevel( QString( "exprVal MinScale:%1" ).arg( exprVal.toDouble() ), 4 );
01520       bool conversionOk;
01521       double mins = exprVal.toDouble( &conversionOk );
01522       if ( conversionOk )
01523       {
01524         minScale = mins;
01525       }
01526     }
01527 
01528     // scales closer than 1:1
01529     if ( minScale < 0 )
01530     {
01531       minScale = 1 / qAbs( minScale );
01532     }
01533 
01534     if ( minScale != 0 && context.rendererScale() < minScale )
01535     {
01536       return;
01537     }
01538 
01539     // data defined max scale?
01540     double maxScale = scaleMax;
01541     if ( dataDefinedEvaluate( QgsPalLayerSettings::MaxScale, exprVal ) )
01542     {
01543       QgsDebugMsgLevel( QString( "exprVal MaxScale:%1" ).arg( exprVal.toDouble() ), 4 );
01544       bool conversionOk;
01545       double maxs = exprVal.toDouble( &conversionOk );
01546       if ( conversionOk )
01547       {
01548         maxScale = maxs;
01549       }
01550     }
01551 
01552     // scales closer than 1:1
01553     if ( maxScale < 0 )
01554     {
01555       maxScale = 1 / qAbs( maxScale );
01556     }
01557 
01558     if ( maxScale != 0 && context.rendererScale() > maxScale )
01559     {
01560       return;
01561     }
01562   }
01563 
01564   QFont labelFont = textFont;
01565   // labelFont will be added to label's QgsPalGeometry for use during label painting
01566 
01567   // data defined font units?
01568   SizeUnit fontunits = fontSizeInMapUnits ? QgsPalLayerSettings::MapUnits : QgsPalLayerSettings::Points;
01569   if ( dataDefinedEvaluate( QgsPalLayerSettings::FontSizeUnit, exprVal ) )
01570   {
01571     QString units = exprVal.toString().trimmed();
01572     QgsDebugMsgLevel( QString( "exprVal Font units:%1" ).arg( units ), 4 );
01573     if ( !units.isEmpty() )
01574     {
01575       fontunits = _decodeUnits( units );
01576     }
01577   }
01578 
01579   //data defined label size?
01580   double fontSize = labelFont.pointSizeF(); // font size doesn't have its own class data member
01581   if ( dataDefinedEvaluate( QgsPalLayerSettings::Size, exprVal ) )
01582   {
01583     QgsDebugMsgLevel( QString( "exprVal Size:%1" ).arg( exprVal.toDouble() ), 4 );
01584     bool ok;
01585     double size = exprVal.toDouble( &ok );
01586     if ( ok )
01587     {
01588       fontSize = size;
01589     }
01590   }
01591   if ( fontSize <= 0.0 )
01592   {
01593     return;
01594   }
01595 
01596   int fontPixelSize = sizeToPixel( fontSize, context, fontunits, true );
01597   // don't try to show font sizes less than 1 pixel (Qt complains)
01598   if ( fontPixelSize < 1 )
01599   {
01600     return;
01601   }
01602   labelFont.setPixelSize( fontPixelSize );
01603 
01604   // NOTE: labelFont now always has pixelSize set, so pointSize or pointSizeF might return -1
01605 
01606   // defined 'minimum/maximum pixel font size'?
01607   if ( fontunits == QgsPalLayerSettings::MapUnits )
01608   {
01609     bool useFontLimitPixelSize = fontLimitPixelSize;
01610     if ( dataDefinedEvaluate( QgsPalLayerSettings::FontLimitPixel, exprVal ) )
01611     {
01612       QgsDebugMsgLevel( QString( "exprVal FontLimitPixel:%1" ).arg( exprVal.toBool() ? "true" : "false" ), 4 );
01613       useFontLimitPixelSize = exprVal.toBool();
01614     }
01615 
01616     if ( useFontLimitPixelSize )
01617     {
01618       int fontMinPixel = fontMinPixelSize;
01619       if ( dataDefinedEvaluate( QgsPalLayerSettings::FontMinPixel, exprVal ) )
01620       {
01621         bool ok;
01622         int sizeInt = exprVal.toInt( &ok );
01623         QgsDebugMsgLevel( QString( "exprVal FontMinPixel:%1" ).arg( sizeInt ), 4 );
01624         if ( ok )
01625         {
01626           fontMinPixel = sizeInt;
01627         }
01628       }
01629 
01630       int fontMaxPixel = fontMaxPixelSize;
01631       if ( dataDefinedEvaluate( QgsPalLayerSettings::FontMaxPixel, exprVal ) )
01632       {
01633         bool ok;
01634         int sizeInt = exprVal.toInt( &ok );
01635         QgsDebugMsgLevel( QString( "exprVal FontMaxPixel:%1" ).arg( sizeInt ), 4 );
01636         if ( ok )
01637         {
01638           fontMaxPixel = sizeInt;
01639         }
01640       }
01641 
01642       if ( fontMinPixel > labelFont.pixelSize() || labelFont.pixelSize() > fontMaxPixel )
01643       {
01644         return;
01645       }
01646     }
01647   }
01648 
01649   // NOTE: the following parsing functions calculate and store any data defined values for later use in QgsPalLabeling::drawLabeling
01650   // this is done to provide clarity, and because such parsing is not directly related to PAL feature registration calculations
01651 
01652   // calculate rest of font attributes and store any data defined values
01653   // this is done here for later use in making label backgrounds part of collision management (when implemented)
01654   parseTextStyle( labelFont, fontunits, context );
01655   parseTextFormatting();
01656   parseTextBuffer();
01657   parseShapeBackground();
01658   parseDropShadow();
01659 
01660   QString labelText;
01661 
01662   // Check to see if we are a expression string.
01663   if ( isExpression )
01664   {
01665     QgsExpression* exp = getLabelExpression();
01666     if ( exp->hasParserError() )
01667     {
01668       QgsDebugMsgLevel( QString( "Expression parser error:%1" ).arg( exp->parserErrorString() ), 4 );
01669       return;
01670     }
01671     exp->setScale( context.rendererScale() );
01672 //    QVariant result = exp->evaluate( &f, layer->pendingFields() );
01673     QVariant result = exp->evaluate( &f ); // expression prepared in QgsPalLabeling::prepareLayer()
01674     if ( exp->hasEvalError() )
01675     {
01676       QgsDebugMsgLevel( QString( "Expression parser eval error:%1" ).arg( exp->evalErrorString() ), 4 );
01677       return;
01678     }
01679     labelText = result.toString();
01680   }
01681   else
01682   {
01683     labelText = f.attribute( fieldIndex ).toString();
01684   }
01685 
01686   // data defined format numbers?
01687   bool formatnum = formatNumbers;
01688   if ( dataDefinedEvaluate( QgsPalLayerSettings::NumFormat, exprVal ) )
01689   {
01690     formatnum = exprVal.toBool();
01691     QgsDebugMsgLevel( QString( "exprVal NumFormat:%1" ).arg( formatnum ? "true" : "false" ), 4 );
01692   }
01693 
01694   // format number if label text is coercible to a number
01695   if ( formatnum )
01696   {
01697     // data defined decimal places?
01698     int decimalPlaces = decimals;
01699     if ( dataDefinedEvaluate( QgsPalLayerSettings::NumDecimals, exprVal ) )
01700     {
01701       bool ok;
01702       int dInt = exprVal.toInt( &ok );
01703       QgsDebugMsgLevel( QString( "exprVal NumDecimals:%1" ).arg( dInt ), 4 );
01704       if ( ok && dInt > 0 ) // needs to be positive
01705       {
01706         decimalPlaces = dInt;
01707       }
01708     }
01709 
01710     // data defined plus sign?
01711     bool signPlus = plusSign;
01712     if ( dataDefinedEvaluate( QgsPalLayerSettings::NumPlusSign, exprVal ) )
01713     {
01714       signPlus = exprVal.toBool();
01715       QgsDebugMsgLevel( QString( "exprVal NumPlusSign:%1" ).arg( signPlus ? "true" : "false" ), 4 );
01716     }
01717 
01718     QVariant textV = QVariant( labelText );
01719     bool ok;
01720     double d = textV.toDouble( &ok );
01721     if ( ok )
01722     {
01723       QString numberFormat;
01724       if ( d > 0 && signPlus )
01725       {
01726         numberFormat.append( "+" );
01727       }
01728       numberFormat.append( "%1" );
01729       labelText = numberFormat.arg( d, 0, 'f', decimalPlaces );
01730     }
01731   }
01732 
01733 
01734   // NOTE: this should come AFTER any option that affects font metrics
01735   QFontMetricsF* labelFontMetrics = new QFontMetricsF( labelFont );
01736   double labelX, labelY; // will receive label size
01737   calculateLabelSize( labelFontMetrics, labelText, labelX, labelY, mCurFeat );
01738 
01739 
01740   // maximum angle between curved label characters (hardcoded defaults used in QGIS <2.0)
01741   //
01742   double maxcharanglein = 20.0; // range 20.0-60.0
01743   double maxcharangleout = -20.0; // range 20.0-95.0
01744 
01745   if ( placement == QgsPalLayerSettings::Curved )
01746   {
01747     maxcharanglein = maxCurvedCharAngleIn;
01748     maxcharangleout = maxCurvedCharAngleOut;
01749 
01750     //data defined maximum angle between curved label characters?
01751     if ( dataDefinedEvaluate( QgsPalLayerSettings::CurvedCharAngleInOut, exprVal ) )
01752     {
01753       QString ptstr = exprVal.toString().trimmed();
01754       QgsDebugMsgLevel( QString( "exprVal CurvedCharAngleInOut:%1" ).arg( ptstr ), 4 );
01755 
01756       if ( !ptstr.isEmpty() )
01757       {
01758         QPointF maxcharanglePt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
01759         maxcharanglein = qBound( 20.0, ( double )maxcharanglePt.x(), 60.0 );
01760         maxcharangleout = qBound( 20.0, ( double )maxcharanglePt.y(), 95.0 );
01761       }
01762     }
01763     // make sure maxcharangleout is always negative
01764     maxcharangleout = -( qAbs( maxcharangleout ) );
01765   }
01766 
01767   QgsGeometry* geom = f.geometry();
01768   if ( !geom )
01769   {
01770     return;
01771   }
01772 
01773   if ( ct ) // reproject the geometry if necessary
01774     geom->transform( *ct );
01775 
01776   if ( !checkMinimumSizeMM( context, geom, minFeatureSize ) )
01777   {
01778     return;
01779   }
01780 
01781   // whether we're going to create a centroid for polygon
01782   bool centroidPoly = (( placement == QgsPalLayerSettings::AroundPoint
01783                          || placement == QgsPalLayerSettings::OverPoint )
01784                        && geom->type() == QGis::Polygon );
01785 
01786   // data defined centroid whole or clipped?
01787   bool wholeCentroid = centroidWhole;
01788   if ( dataDefinedEvaluate( QgsPalLayerSettings::CentroidWhole, exprVal ) )
01789   {
01790     QString str = exprVal.toString().trimmed();
01791     QgsDebugMsgLevel( QString( "exprVal CentroidWhole:%1" ).arg( str ), 4 );
01792 
01793     if ( !str.isEmpty() )
01794     {
01795       if ( str.compare( "Visible", Qt::CaseInsensitive ) == 0 )
01796       {
01797         wholeCentroid = false;
01798       }
01799       else if ( str.compare( "Whole", Qt::CaseInsensitive ) == 0 )
01800       {
01801         wholeCentroid = true;
01802       }
01803     }
01804   }
01805 
01806   // CLIP the geometry if it is bigger than the extent
01807   // don't clip if centroid is requested for whole feature
01808   bool do_clip = false;
01809   if ( !centroidPoly || ( centroidPoly && !wholeCentroid ) )
01810   {
01811     do_clip = !extentGeom->contains( geom );
01812     if ( do_clip )
01813     {
01814       geom = geom->intersection( extentGeom ); // creates new geometry
01815       if ( !geom )
01816       {
01817         return;
01818       }
01819     }
01820   }
01821 
01822   GEOSGeometry* geos_geom = geom->asGeos();
01823 
01824   if ( geos_geom == NULL )
01825     return; // invalid geometry
01826 
01827   // likelihood exists label will be registered with PAL and may be drawn
01828   // check if max number of features to label (already registered with PAL) has been reached
01829   // Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
01830   if ( limitNumLabels )
01831   {
01832     if ( !maxNumLabels )
01833     {
01834       return;
01835     }
01836     mFeatsRegPal = palLayer->getNbFeatures();
01837     if ( mFeatsRegPal >= maxNumLabels )
01838     {
01839       return;
01840     }
01841 
01842     int divNum = ( int )(( mFeaturesToLabel / maxNumLabels ) + 0.5 );
01843     if ( divNum && ( mFeatsRegPal == ( int )( mFeatsSendingToPal / divNum ) ) )
01844     {
01845       mFeatsSendingToPal += 1;
01846       if ( divNum &&  mFeatsSendingToPal % divNum )
01847       {
01848         return;
01849       }
01850     }
01851   }
01852 
01853   GEOSGeometry* geos_geom_clone = GEOSGeom_clone( geos_geom );
01854 
01855   //data defined position / alignment / rotation?
01856   bool dataDefinedPosition = false;
01857   bool labelIsPinned = false;
01858   bool layerDefinedRotation = false;
01859   bool dataDefinedRotation = false;
01860   double xPos = 0.0, yPos = 0.0, angle = 0.0;
01861   bool ddXPos = false, ddYPos = false;
01862   double quadOffsetX = 0.0, quadOffsetY = 0.0;
01863   double offsetX = 0.0, offsetY = 0.0;
01864 
01865   //data defined quadrant offset?
01866   QuadrantPosition quadOff = quadOffset;
01867   if ( dataDefinedEvaluate( QgsPalLayerSettings::OffsetQuad, exprVal ) )
01868   {
01869     bool ok;
01870     int quadInt = exprVal.toInt( &ok );
01871     QgsDebugMsgLevel( QString( "exprVal OffsetQuad:%1" ).arg( quadInt ), 4 );
01872     if ( ok && 0 <= quadInt && quadInt <= 8 )
01873     {
01874       quadOff = ( QuadrantPosition )quadInt;
01875     }
01876   }
01877 
01878   // adjust quadrant offset of labels
01879   switch ( quadOff )
01880   {
01881     case QuadrantAboveLeft:
01882       quadOffsetX = -1.0;
01883       quadOffsetY = 1.0;
01884       break;
01885     case QuadrantAbove:
01886       quadOffsetX = 0.0;
01887       quadOffsetY = 1.0;
01888       break;
01889     case QuadrantAboveRight:
01890       quadOffsetX = 1.0;
01891       quadOffsetY = 1.0;
01892       break;
01893     case QuadrantLeft:
01894       quadOffsetX = -1.0;
01895       quadOffsetY = 0.0;
01896       break;
01897     case QuadrantRight:
01898       quadOffsetX = 1.0;
01899       quadOffsetY = 0.0;
01900       break;
01901     case QuadrantBelowLeft:
01902       quadOffsetX = -1.0;
01903       quadOffsetY = -1.0;
01904       break;
01905     case QuadrantBelow:
01906       quadOffsetX = 0.0;
01907       quadOffsetY = -1.0;
01908       break;
01909     case QuadrantBelowRight:
01910       quadOffsetX = 1.0;
01911       quadOffsetY = -1.0;
01912       break;
01913     case QuadrantOver:
01914     default:
01915       break;
01916   }
01917 
01918   //data defined label offset?
01919   double xOff = xOffset;
01920   double yOff = yOffset;
01921   if ( dataDefinedEvaluate( QgsPalLayerSettings::OffsetXY, exprVal ) )
01922   {
01923     QString ptstr = exprVal.toString().trimmed();
01924     QgsDebugMsgLevel( QString( "exprVal OffsetXY:%1" ).arg( ptstr ), 4 );
01925 
01926     if ( !ptstr.isEmpty() )
01927     {
01928       QPointF ddOffPt = QgsSymbolLayerV2Utils::decodePoint( ptstr );
01929       xOff = ddOffPt.x();
01930       yOff = ddOffPt.y();
01931     }
01932   }
01933 
01934   // data defined label offset units?
01935   bool offinmapunits = labelOffsetInMapUnits;
01936   if ( dataDefinedEvaluate( QgsPalLayerSettings::OffsetUnits, exprVal ) )
01937   {
01938     QString units = exprVal.toString().trimmed();
01939     QgsDebugMsgLevel( QString( "exprVal OffsetUnits:%1" ).arg( units ), 4 );
01940     if ( !units.isEmpty() )
01941     {
01942       offinmapunits = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
01943     }
01944   }
01945 
01946   // adjust offset of labels to match chosen unit and map scale
01947   // offsets match those of symbology: -x = left, -y = up
01948   double mapUntsPerMM = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
01949   if ( xOff != 0 )
01950   {
01951     offsetX = xOff;  // must be positive to match symbology offset direction
01952     if ( !offinmapunits )
01953     {
01954       offsetX *= mapUntsPerMM; //convert offset from mm to map units
01955     }
01956   }
01957   if ( yOff != 0 )
01958   {
01959     offsetY = -yOff; // must be negative to match symbology offset direction
01960     if ( !offinmapunits )
01961     {
01962       offsetY *= mapUntsPerMM; //convert offset from mm to map units
01963     }
01964   }
01965 
01966   // layer defined rotation?
01967   // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
01968   if ( placement == QgsPalLayerSettings::OverPoint && angleOffset != 0 )
01969   {
01970     layerDefinedRotation = true;
01971     angle = angleOffset * M_PI / 180; // convert to radians
01972   }
01973 
01974   //data defined rotation?
01975   if ( dataDefinedEvaluate( QgsPalLayerSettings::Rotation, exprVal ) )
01976   {
01977     bool ok;
01978     double rotD = exprVal.toDouble( &ok );
01979     QgsDebugMsgLevel( QString( "exprVal Rotation:%1" ).arg( rotD ), 4 );
01980     if ( ok )
01981     {
01982       dataDefinedRotation = true;
01983       angle = rotD * M_PI / 180.0;
01984     }
01985   }
01986 
01987   if ( dataDefinedEvaluate( QgsPalLayerSettings::PositionX, exprVal ) )
01988   {
01989     xPos = exprVal.toDouble( &ddXPos );
01990     QgsDebugMsgLevel( QString( "exprVal PositionX:%1" ).arg( xPos ), 4 );
01991 
01992     if ( dataDefinedEvaluate( QgsPalLayerSettings::PositionY, exprVal ) )
01993     {
01994       //data defined position. But field values could be NULL -> positions will be generated by PAL
01995       yPos = exprVal.toDouble( &ddYPos );
01996       QgsDebugMsgLevel( QString( "exprVal PositionY:%1" ).arg( yPos ), 4 );
01997 
01998       if ( ddXPos && ddYPos )
01999       {
02000         dataDefinedPosition = true;
02001         labelIsPinned = true;
02002         // layer rotation set, but don't rotate pinned labels unless data defined
02003         if ( layerDefinedRotation && !dataDefinedRotation )
02004         {
02005           angle = 0.0;
02006         }
02007 
02008         //x/y shift in case of alignment
02009         double xdiff = 0.0;
02010         double ydiff = 0.0;
02011 
02012         //horizontal alignment
02013         if ( dataDefinedEvaluate( QgsPalLayerSettings::Hali, exprVal ) )
02014         {
02015           QString haliString = exprVal.toString();
02016           QgsDebugMsgLevel( QString( "exprVal Hali:%1" ).arg( haliString ), 4 );
02017           if ( haliString.compare( "Center", Qt::CaseInsensitive ) == 0 )
02018           {
02019             xdiff -= labelX / 2.0;
02020           }
02021           else if ( haliString.compare( "Right", Qt::CaseInsensitive ) == 0 )
02022           {
02023             xdiff -= labelX;
02024           }
02025         }
02026 
02027         //vertical alignment
02028         if ( dataDefinedEvaluate( QgsPalLayerSettings::Vali, exprVal ) )
02029         {
02030           QString valiString = exprVal.toString();
02031           QgsDebugMsgLevel( QString( "exprVal Vali:%1" ).arg( valiString ), 4 );
02032           if ( valiString.compare( "Bottom", Qt::CaseInsensitive ) != 0 )
02033           {
02034             if ( valiString.compare( "Top", Qt::CaseInsensitive ) == 0
02035                  || valiString.compare( "Cap", Qt::CaseInsensitive ) == 0 )
02036             {
02037               ydiff -= labelY;
02038             }
02039             else
02040             {
02041               double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
02042 
02043               if ( valiString.compare( "Base", Qt::CaseInsensitive ) == 0 )
02044               {
02045                 ydiff -= labelY * descentRatio;
02046               }
02047               else if ( valiString.compare( "Half", Qt::CaseInsensitive ) == 0 )
02048               {
02049                 ydiff -= labelY * descentRatio;
02050                 ydiff -= labelY * 0.5 * ( 1 - descentRatio );
02051               }
02052             }
02053           }
02054         }
02055 
02056         if ( dataDefinedRotation )
02057         {
02058           //adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
02059           double xd = xdiff * cos( angle ) - ydiff * sin( angle );
02060           double yd = xdiff * sin( angle ) + ydiff * cos( angle );
02061           xdiff = xd;
02062           ydiff = yd;
02063         }
02064 
02065         //project xPos and yPos from layer to map CRS
02066         double z = 0;
02067         if ( ct )
02068         {
02069           ct->transformInPlace( xPos, yPos, z );
02070         }
02071 
02072         xPos += xdiff;
02073         yPos += ydiff;
02074       }
02075       else
02076       {
02077         // only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
02078         if ( dataDefinedRotation && placement != QgsPalLayerSettings::OverPoint )
02079         {
02080           angle = 0.0;
02081         }
02082       }
02083     }
02084   }
02085 
02086   // data defined always show?
02087   bool alwaysShow = false;
02088   if ( dataDefinedEvaluate( QgsPalLayerSettings::AlwaysShow, exprVal ) )
02089   {
02090     alwaysShow = exprVal.toBool();
02091   }
02092 
02093   QgsPalGeometry* lbl = new QgsPalGeometry(
02094     f.id(),
02095     labelText,
02096     geos_geom_clone,
02097     labelFont.letterSpacing(),
02098     labelFont.wordSpacing(),
02099     placement == QgsPalLayerSettings::Curved );
02100 
02101   // record the created geometry - it will be deleted at the end.
02102   geometries.append( lbl );
02103 
02104   // store the label's calculated font for later use during painting
02105 #if QT_VERSION >= 0x040800
02106   QgsDebugMsgLevel( QString( "PAL font stored definedFont: %1, Style: %2" ).arg( labelFont.toString() ).arg( labelFont.styleName() ), 4 );
02107 #endif
02108   lbl->setDefinedFont( labelFont );
02109 
02110   //  feature to the layer
02111   try
02112   {
02113     if ( !palLayer->registerFeature( lbl->strId(), lbl, labelX, labelY, labelText.toUtf8().constData(),
02114                                      xPos, yPos, dataDefinedPosition, angle, dataDefinedRotation,
02115                                      quadOffsetX, quadOffsetY, offsetX, offsetY, alwaysShow ) )
02116       return;
02117   }
02118   catch ( std::exception &e )
02119   {
02120     Q_UNUSED( e );
02121     QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( f.id() ) + QString::fromLatin1( e.what() ), 4 );
02122     return;
02123   }
02124 
02125   // TODO: only for placement which needs character info
02126   pal::Feature* feat = palLayer->getFeature( lbl->strId() );
02127   // account for any data defined font metrics adjustments
02128   feat->setLabelInfo( lbl->info( labelFontMetrics, xform, rasterCompressFactor, maxcharanglein, maxcharangleout ) );
02129   delete labelFontMetrics;
02130 
02131   // TODO: allow layer-wide feature dist in PAL...?
02132 
02133   // data defined label-feature distance?
02134   double distance = dist;
02135   if ( dataDefinedEvaluate( QgsPalLayerSettings::LabelDistance, exprVal ) )
02136   {
02137     bool ok;
02138     double distD = exprVal.toDouble( &ok );
02139     if ( ok )
02140     {
02141       distance = distD;
02142     }
02143   }
02144 
02145   // data defined label-feature distance units?
02146   bool distinmapunit = distInMapUnits;
02147   if ( dataDefinedEvaluate( QgsPalLayerSettings::DistanceUnits, exprVal ) )
02148   {
02149     QString units = exprVal.toString().trimmed();
02150     QgsDebugMsgLevel( QString( "exprVal DistanceUnits:%1" ).arg( units ), 4 );
02151     if ( !units.isEmpty() )
02152     {
02153       distinmapunit = ( _decodeUnits( units ) == QgsPalLayerSettings::MapUnits );
02154     }
02155   }
02156 
02157   if ( distance != 0 )
02158   {
02159     if ( distinmapunit ) //convert distance from mm/map units to pixels
02160     {
02161       distance /= context.mapToPixel().mapUnitsPerPixel();
02162     }
02163     else //mm
02164     {
02165       distance *= vectorScaleFactor;
02166     }
02167     feat->setDistLabel( qAbs( ptOne.x() - ptZero.x() )* distance );
02168   }
02169 
02170   //add parameters for data defined labeling to QgsPalGeometry
02171   QMap< DataDefinedProperties, QVariant >::const_iterator dIt = dataDefinedValues.constBegin();
02172   for ( ; dIt != dataDefinedValues.constEnd(); ++dIt )
02173   {
02174     lbl->addDataDefinedValue( dIt.key(), dIt.value() );
02175   }
02176 
02177   // set geometry's pinned property
02178   lbl->setIsPinned( labelIsPinned );
02179 }
02180 
02181 bool QgsPalLayerSettings::dataDefinedValEval( const QString& valType,
02182     QgsPalLayerSettings::DataDefinedProperties p,
02183     QVariant& exprVal )
02184 {
02185   if ( dataDefinedEvaluate( p, exprVal ) )
02186   {
02187     QString dbgStr = QString( "exprVal %1:" ).arg( mDataDefinedNames.value( p ).first ) + "%1";
02188 
02189     if ( valType == QString( "bool" ) )
02190     {
02191       bool bol = exprVal.toBool();
02192       QgsDebugMsgLevel( dbgStr.arg( bol ? "true" : "false" ), 4 );
02193       dataDefinedValues.insert( p, QVariant( bol ) );
02194       return true;
02195     }
02196     if ( valType == QString( "int" ) )
02197     {
02198       bool ok;
02199       int size = exprVal.toInt( &ok );
02200       QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
02201 
02202       if ( ok )
02203       {
02204         dataDefinedValues.insert( p, QVariant( size ) );
02205         return true;
02206       }
02207     }
02208     if ( valType == QString( "intpos" ) )
02209     {
02210       bool ok;
02211       int size = exprVal.toInt( &ok );
02212       QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
02213 
02214       if ( ok && size > 0 )
02215       {
02216         dataDefinedValues.insert( p, QVariant( size ) );
02217         return true;
02218       }
02219     }
02220     if ( valType == QString( "double" ) )
02221     {
02222       bool ok;
02223       double size = exprVal.toDouble( &ok );
02224       QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
02225 
02226       if ( ok )
02227       {
02228         dataDefinedValues.insert( p, QVariant( size ) );
02229         return true;
02230       }
02231     }
02232     if ( valType == QString( "doublepos" ) )
02233     {
02234       bool ok;
02235       double size = exprVal.toDouble( &ok );
02236       QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
02237 
02238       if ( ok && size > 0.0 )
02239       {
02240         dataDefinedValues.insert( p, QVariant( size ) );
02241         return true;
02242       }
02243     }
02244     if ( valType == QString( "rotation180" ) )
02245     {
02246       bool ok;
02247       double rot = exprVal.toDouble( &ok );
02248       QgsDebugMsgLevel( dbgStr.arg( rot ), 4 );
02249       if ( ok )
02250       {
02251         if ( rot < -180.0 && rot >= -360 )
02252         {
02253           rot += 360;
02254         }
02255         if ( rot > 180.0 && rot <= 360 )
02256         {
02257           rot -= 360;
02258         }
02259         if ( rot >= -180 && rot <= 180 )
02260         {
02261           dataDefinedValues.insert( p, QVariant( rot ) );
02262           return true;
02263         }
02264       }
02265     }
02266     if ( valType == QString( "transp" ) )
02267     {
02268       bool ok;
02269       int size = exprVal.toInt( &ok );
02270       QgsDebugMsgLevel( dbgStr.arg( size ), 4 );
02271       if ( ok && size >= 0 && size <= 100 )
02272       {
02273         dataDefinedValues.insert( p, QVariant( size ) );
02274         return true;
02275       }
02276     }
02277     if ( valType == QString( "string" ) )
02278     {
02279       QString str = exprVal.toString(); // don't trim whitespace
02280       QgsDebugMsgLevel( dbgStr.arg( str ), 4 );
02281 
02282       dataDefinedValues.insert( p, QVariant( str ) ); // let it stay empty if it is
02283       return true;
02284     }
02285     if ( valType == QString( "units" ) )
02286     {
02287       QString unitstr = exprVal.toString().trimmed();
02288       QgsDebugMsgLevel( dbgStr.arg( unitstr ), 4 );
02289 
02290       if ( !unitstr.isEmpty() )
02291       {
02292         dataDefinedValues.insert( p, QVariant(( int )_decodeUnits( unitstr ) ) );
02293         return true;
02294       }
02295     }
02296     if ( valType == QString( "color" ) )
02297     {
02298       QString colorstr = exprVal.toString().trimmed();
02299       QgsDebugMsgLevel( dbgStr.arg( colorstr ), 4 );
02300       QColor color = QgsSymbolLayerV2Utils::decodeColor( colorstr );
02301 
02302       if ( color.isValid() )
02303       {
02304         dataDefinedValues.insert( p, QVariant( color ) );
02305         return true;
02306       }
02307     }
02308     if ( valType == QString( "joinstyle" ) )
02309     {
02310       QString joinstr = exprVal.toString().trimmed();
02311       QgsDebugMsgLevel( dbgStr.arg( joinstr ), 4 );
02312 
02313       if ( !joinstr.isEmpty() )
02314       {
02315         dataDefinedValues.insert( p, QVariant(( int )_decodePenJoinStyle( joinstr ) ) );
02316         return true;
02317       }
02318     }
02319     if ( valType == QString( "blendmode" ) )
02320     {
02321       QString blendstr = exprVal.toString().trimmed();
02322       QgsDebugMsgLevel( dbgStr.arg( blendstr ), 4 );
02323 
02324       if ( !blendstr.isEmpty() )
02325       {
02326         dataDefinedValues.insert( p, QVariant(( int )_decodeBlendMode( blendstr ) ) );
02327         return true;
02328       }
02329     }
02330     if ( valType == QString( "pointf" ) )
02331     {
02332       QString ptstr = exprVal.toString().trimmed();
02333       QgsDebugMsgLevel( dbgStr.arg( ptstr ), 4 );
02334 
02335       if ( !ptstr.isEmpty() )
02336       {
02337         dataDefinedValues.insert( p, QVariant( QgsSymbolLayerV2Utils::decodePoint( ptstr ) ) );
02338         return true;
02339       }
02340     }
02341   }
02342   return false;
02343 }
02344 
02345 void QgsPalLayerSettings::parseTextStyle( QFont& labelFont,
02346     QgsPalLayerSettings::SizeUnit fontunits,
02347     const QgsRenderContext& context )
02348 {
02349   // NOTE: labelFont already has pixelSize set, so pointSize or pointSizeF might return -1
02350 
02351   QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
02352 
02353   // Two ways to generate new data defined font:
02354   // 1) Family + [bold] + [italic] (named style is ignored and font is built off of base family)
02355   // 2) Family + named style  (bold or italic is ignored)
02356 
02357   // data defined font family?
02358   QString ddFontFamily( "" );
02359   if ( dataDefinedEvaluate( QgsPalLayerSettings::Family, exprVal ) )
02360   {
02361     QString family = exprVal.toString().trimmed();
02362     QgsDebugMsgLevel( QString( "exprVal Font family:%1" ).arg( family ), 4 );
02363 
02364     if ( labelFont.family() != family )
02365     {
02366       // testing for ddFontFamily in QFontDatabase.families() may be slow to do for every feature
02367       // (i.e. don't use QgsFontUtils::fontFamilyMatchOnSystem( family ) here)
02368       if ( QgsFontUtils::fontFamilyOnSystem( family ) )
02369       {
02370         ddFontFamily = family;
02371       }
02372     }
02373   }
02374 
02375   // data defined named font style?
02376   QString ddFontStyle( "" );
02377   if ( dataDefinedEvaluate( QgsPalLayerSettings::FontStyle, exprVal ) )
02378   {
02379     QString fontstyle = exprVal.toString().trimmed();
02380     QgsDebugMsgLevel( QString( "exprVal Font style:%1" ).arg( fontstyle ), 4 );
02381     ddFontStyle = fontstyle;
02382   }
02383 
02384   // data defined bold font style?
02385   bool ddBold = false;
02386   if ( dataDefinedEvaluate( QgsPalLayerSettings::Bold, exprVal ) )
02387   {
02388     bool bold = exprVal.toBool();
02389     QgsDebugMsgLevel( QString( "exprVal Font bold:%1" ).arg( bold ? "true" : "false" ), 4 );
02390     ddBold = bold;
02391   }
02392 
02393   // data defined italic font style?
02394   bool ddItalic = false;
02395   if ( dataDefinedEvaluate( QgsPalLayerSettings::Italic, exprVal ) )
02396   {
02397     bool italic = exprVal.toBool();
02398     QgsDebugMsgLevel( QString( "exprVal Font italic:%1" ).arg( italic ? "true" : "false" ), 4 );
02399     ddItalic = italic;
02400   }
02401 
02402   // TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
02403   //       (currently defaults to what has been read in from layer settings)
02404   QFont newFont;
02405   QFont appFont = QApplication::font();
02406   bool newFontBuilt = false;
02407   if ( ddBold || ddItalic )
02408   {
02409     // new font needs built, since existing style needs removed
02410     newFont = QFont( !ddFontFamily.isEmpty() ? ddFontFamily : labelFont.family() );
02411     newFontBuilt = true;
02412     newFont.setBold( ddBold );
02413     newFont.setItalic( ddItalic );
02414   }
02415   else if ( !ddFontStyle.isEmpty()
02416             && ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
02417   {
02418     if ( !ddFontFamily.isEmpty() )
02419     {
02420       // both family and style are different, build font from database
02421       QFont styledfont = mFontDB.font( ddFontFamily, ddFontStyle, appFont.pointSize() );
02422       if ( appFont != styledfont )
02423       {
02424         newFont = styledfont;
02425         newFontBuilt = true;
02426       }
02427     }
02428 
02429     // update the font face style
02430     QgsFontUtils::updateFontViaStyle( newFontBuilt ? newFont : labelFont, ddFontStyle );
02431   }
02432   else if ( !ddFontFamily.isEmpty() )
02433   {
02434     if ( ddFontStyle.compare( "Ignore", Qt::CaseInsensitive ) != 0 )
02435     {
02436       // just family is different, build font from database
02437       QFont styledfont = mFontDB.font( ddFontFamily, textNamedStyle, appFont.pointSize() );
02438       if ( appFont != styledfont )
02439       {
02440         newFont = styledfont;
02441         newFontBuilt = true;
02442       }
02443     }
02444     else
02445     {
02446       newFont = QFont( ddFontFamily );
02447       newFontBuilt = true;
02448     }
02449   }
02450 
02451   if ( newFontBuilt )
02452   {
02453     // copy over existing font settings
02454     //newFont = newFont.resolve( labelFont ); // should work, but let's be sure what's being copied
02455     newFont.setPixelSize( labelFont.pixelSize() );
02456     newFont.setCapitalization( labelFont.capitalization() );
02457     newFont.setUnderline( labelFont.underline() );
02458     newFont.setStrikeOut( labelFont.strikeOut() );
02459     newFont.setWordSpacing( labelFont.wordSpacing() );
02460     newFont.setLetterSpacing( QFont::AbsoluteSpacing, labelFont.letterSpacing() );
02461 
02462     labelFont = newFont;
02463   }
02464 
02465   // data defined word spacing?
02466   double wordspace = labelFont.wordSpacing();
02467   if ( dataDefinedEvaluate( QgsPalLayerSettings::FontWordSpacing, exprVal ) )
02468   {
02469     bool ok;
02470     double wspacing = exprVal.toDouble( &ok );
02471     QgsDebugMsgLevel( QString( "exprVal FontWordSpacing:%1" ).arg( wspacing ), 4 );
02472     if ( ok )
02473     {
02474       wordspace = wspacing;
02475     }
02476   }
02477   labelFont.setWordSpacing( sizeToPixel( wordspace, context, fontunits, false ) );
02478 
02479   // data defined letter spacing?
02480   double letterspace = labelFont.letterSpacing();
02481   if ( dataDefinedEvaluate( QgsPalLayerSettings::FontLetterSpacing, exprVal ) )
02482   {
02483     bool ok;
02484     double lspacing = exprVal.toDouble( &ok );
02485     QgsDebugMsgLevel( QString( "exprVal FontLetterSpacing:%1" ).arg( lspacing ), 4 );
02486     if ( ok )
02487     {
02488       letterspace = lspacing;
02489     }
02490   }
02491   labelFont.setLetterSpacing( QFont::AbsoluteSpacing, sizeToPixel( letterspace, context, fontunits, false ) );
02492 
02493   // data defined font capitalization?
02494   QFont::Capitalization fontcaps = labelFont.capitalization();
02495   if ( dataDefinedEvaluate( QgsPalLayerSettings::FontCase, exprVal ) )
02496   {
02497     QString fcase = exprVal.toString().trimmed();
02498     QgsDebugMsgLevel( QString( "exprVal FontCase:%1" ).arg( fcase ), 4 );
02499 
02500     if ( !fcase.isEmpty() )
02501     {
02502       if ( fcase.compare( "NoChange", Qt::CaseInsensitive ) == 0 )
02503       {
02504         fontcaps = QFont::MixedCase;
02505       }
02506       else if ( fcase.compare( "Upper", Qt::CaseInsensitive ) == 0 )
02507       {
02508         fontcaps = QFont::AllUppercase;
02509       }
02510       else if ( fcase.compare( "Lower", Qt::CaseInsensitive ) == 0 )
02511       {
02512         fontcaps = QFont::AllLowercase;
02513       }
02514       else if ( fcase.compare( "Capitalize", Qt::CaseInsensitive ) == 0 )
02515       {
02516         fontcaps = QFont::Capitalize;
02517       }
02518 
02519       if ( fontcaps != labelFont.capitalization() )
02520       {
02521         labelFont.setCapitalization( fontcaps );
02522       }
02523     }
02524   }
02525 
02526   // data defined strikeout font style?
02527   if ( dataDefinedEvaluate( QgsPalLayerSettings::Strikeout, exprVal ) )
02528   {
02529     bool strikeout = exprVal.toBool();
02530     QgsDebugMsgLevel( QString( "exprVal Font strikeout:%1" ).arg( strikeout ? "true" : "false" ), 4 );
02531     labelFont.setStrikeOut( strikeout );
02532   }
02533 
02534   // data defined underline font style?
02535   if ( dataDefinedEvaluate( QgsPalLayerSettings::Underline, exprVal ) )
02536   {
02537     bool underline = exprVal.toBool();
02538     QgsDebugMsgLevel( QString( "exprVal Font underline:%1" ).arg( underline ? "true" : "false" ), 4 );
02539     labelFont.setUnderline( underline );
02540   }
02541 
02542   // pass the rest on to QgsPalLabeling::drawLabeling
02543 
02544   // data defined font color?
02545   dataDefinedValEval( "color", QgsPalLayerSettings::Color, exprVal );
02546 
02547   // data defined font transparency?
02548   dataDefinedValEval( "transp", QgsPalLayerSettings::FontTransp, exprVal );
02549 
02550   // data defined font blend mode?
02551   dataDefinedValEval( "blendmode", QgsPalLayerSettings::FontBlendMode, exprVal );
02552 
02553 }
02554 
02555 void QgsPalLayerSettings::parseTextBuffer()
02556 {
02557   QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
02558 
02559   // data defined draw buffer?
02560   bool drawBuffer = bufferDraw;
02561   if ( dataDefinedValEval( "bool", QgsPalLayerSettings::BufferDraw, exprVal ) )
02562   {
02563     drawBuffer = exprVal.toBool();
02564   }
02565 
02566   if ( !drawBuffer )
02567   {
02568     return;
02569   }
02570 
02571   // data defined buffer size?
02572   double bufrSize = bufferSize;
02573   if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::BufferSize, exprVal ) )
02574   {
02575     bufrSize = exprVal.toDouble();
02576   }
02577 
02578   // data defined buffer transparency?
02579   int bufTransp = bufferTransp;
02580   if ( dataDefinedValEval( "transp", QgsPalLayerSettings::BufferTransp, exprVal ) )
02581   {
02582     bufTransp = exprVal.toInt();
02583   }
02584 
02585   drawBuffer = ( drawBuffer && bufrSize > 0.0 && bufTransp < 100 );
02586 
02587   if ( !drawBuffer )
02588   {
02589     dataDefinedValues.insert( QgsPalLayerSettings::BufferDraw, QVariant( false ) ); // trigger value
02590     dataDefinedValues.remove( QgsPalLayerSettings::BufferSize );
02591     dataDefinedValues.remove( QgsPalLayerSettings::BufferTransp );
02592     return; // don't bother evaluating values that won't be used
02593   }
02594 
02595   // data defined buffer units?
02596   dataDefinedValEval( "units", QgsPalLayerSettings::BufferUnit, exprVal );
02597 
02598   // data defined buffer color?
02599   dataDefinedValEval( "color", QgsPalLayerSettings::BufferColor, exprVal );
02600 
02601   // data defined buffer pen join style?
02602   dataDefinedValEval( "joinstyle", QgsPalLayerSettings::BufferJoinStyle, exprVal );
02603 
02604   // data defined buffer blend mode?
02605   dataDefinedValEval( "blendmode", QgsPalLayerSettings::BufferBlendMode, exprVal );
02606 }
02607 
02608 void QgsPalLayerSettings::parseTextFormatting()
02609 {
02610   QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
02611 
02612   // data defined multiline wrap character?
02613   QString wrapchr = wrapChar;
02614   if ( dataDefinedValEval( "string", QgsPalLayerSettings::MultiLineWrapChar, exprVal ) )
02615   {
02616     wrapchr = exprVal.toString();
02617   }
02618 
02619   // data defined multiline height?
02620   dataDefinedValEval( "double", QgsPalLayerSettings::MultiLineHeight, exprVal );
02621 
02622   // data defined multiline text align?
02623   if ( dataDefinedEvaluate( QgsPalLayerSettings::MultiLineAlignment, exprVal ) )
02624   {
02625     QString str = exprVal.toString().trimmed();
02626     QgsDebugMsgLevel( QString( "exprVal MultiLineAlignment:%1" ).arg( str ), 4 );
02627 
02628     if ( !str.isEmpty() )
02629     {
02630       // "Left"
02631       QgsPalLayerSettings::MultiLineAlign aligntype = QgsPalLayerSettings::MultiLeft;
02632 
02633       if ( str.compare( "Center", Qt::CaseInsensitive ) == 0 )
02634       {
02635         aligntype = QgsPalLayerSettings::MultiCenter;
02636       }
02637       else if ( str.compare( "Right", Qt::CaseInsensitive ) == 0 )
02638       {
02639         aligntype = QgsPalLayerSettings::MultiRight;
02640       }
02641       dataDefinedValues.insert( QgsPalLayerSettings::MultiLineAlignment, QVariant(( int )aligntype ) );
02642     }
02643   }
02644 
02645   // data defined direction symbol?
02646   bool drawDirSymb = addDirectionSymbol;
02647   if ( dataDefinedValEval( "bool", QgsPalLayerSettings::DirSymbDraw, exprVal ) )
02648   {
02649     drawDirSymb = exprVal.toBool();
02650   }
02651 
02652   if ( drawDirSymb )
02653   {
02654     // data defined direction left symbol?
02655     dataDefinedValEval( "string", QgsPalLayerSettings::DirSymbLeft, exprVal );
02656 
02657     // data defined direction right symbol?
02658     dataDefinedValEval( "string", QgsPalLayerSettings::DirSymbRight, exprVal );
02659 
02660     // data defined direction symbol placement?
02661     if ( dataDefinedEvaluate( QgsPalLayerSettings::DirSymbPlacement, exprVal ) )
02662     {
02663       QString str = exprVal.toString().trimmed();
02664       QgsDebugMsgLevel( QString( "exprVal DirSymbPlacement:%1" ).arg( str ), 4 );
02665 
02666       if ( !str.isEmpty() )
02667       {
02668         // "LeftRight"
02669         QgsPalLayerSettings::DirectionSymbols placetype = QgsPalLayerSettings::SymbolLeftRight;
02670 
02671         if ( str.compare( "Above", Qt::CaseInsensitive ) == 0 )
02672         {
02673           placetype = QgsPalLayerSettings::SymbolAbove;
02674         }
02675         else if ( str.compare( "Below", Qt::CaseInsensitive ) == 0 )
02676         {
02677           placetype = QgsPalLayerSettings::SymbolBelow;
02678         }
02679         dataDefinedValues.insert( QgsPalLayerSettings::DirSymbPlacement, QVariant(( int )placetype ) );
02680       }
02681     }
02682 
02683     // data defined direction symbol reversed?
02684     dataDefinedValEval( "bool", QgsPalLayerSettings::DirSymbReverse, exprVal );
02685   }
02686 
02687   // formatting for numbers is inline with generation of base label text and not passed to label painting
02688 }
02689 
02690 void QgsPalLayerSettings::parseShapeBackground()
02691 {
02692   QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
02693 
02694   // data defined draw shape?
02695   bool drawShape = shapeDraw;
02696   if ( dataDefinedValEval( "bool", QgsPalLayerSettings::ShapeDraw, exprVal ) )
02697   {
02698     drawShape = exprVal.toBool();
02699   }
02700 
02701   if ( !drawShape )
02702   {
02703     return;
02704   }
02705 
02706   // data defined shape transparency?
02707   int shapeTransp = shapeTransparency;
02708   if ( dataDefinedValEval( "transp", QgsPalLayerSettings::ShapeTransparency, exprVal ) )
02709   {
02710     shapeTransp = exprVal.toInt();
02711   }
02712 
02713   drawShape = ( drawShape && shapeTransp < 100 ); // size is not taken into account (could be)
02714 
02715   if ( !drawShape )
02716   {
02717     dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
02718     dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
02719     return; // don't bother evaluating values that won't be used
02720   }
02721 
02722   // data defined shape kind?
02723   QgsPalLayerSettings::ShapeType shapeKind = shapeType;
02724   if ( dataDefinedEvaluate( QgsPalLayerSettings::ShapeKind, exprVal ) )
02725   {
02726     QString skind = exprVal.toString().trimmed();
02727     QgsDebugMsgLevel( QString( "exprVal ShapeKind:%1" ).arg( skind ), 4 );
02728 
02729     if ( !skind.isEmpty() )
02730     {
02731       // "Rectangle"
02732       QgsPalLayerSettings::ShapeType shpkind = QgsPalLayerSettings::ShapeRectangle;
02733 
02734       if ( skind.compare( "Square", Qt::CaseInsensitive ) == 0 )
02735       {
02736         shpkind = QgsPalLayerSettings::ShapeSquare;
02737       }
02738       else if ( skind.compare( "Ellipse", Qt::CaseInsensitive ) == 0 )
02739       {
02740         shpkind = QgsPalLayerSettings::ShapeEllipse;
02741       }
02742       else if ( skind.compare( "Circle", Qt::CaseInsensitive ) == 0 )
02743       {
02744         shpkind = QgsPalLayerSettings::ShapeCircle;
02745       }
02746       else if ( skind.compare( "SVG", Qt::CaseInsensitive ) == 0 )
02747       {
02748         shpkind = QgsPalLayerSettings::ShapeSVG;
02749       }
02750       shapeKind = shpkind;
02751       dataDefinedValues.insert( QgsPalLayerSettings::ShapeKind, QVariant(( int )shpkind ) );
02752     }
02753   }
02754 
02755   // data defined shape SVG path?
02756   QString svgPath = shapeSVGFile;
02757   if ( dataDefinedEvaluate( QgsPalLayerSettings::ShapeSVGFile, exprVal ) )
02758   {
02759     QString svgfile = exprVal.toString().trimmed();
02760     QgsDebugMsgLevel( QString( "exprVal ShapeSVGFile:%1" ).arg( svgfile ), 4 );
02761 
02762     // '' empty paths are allowed
02763     svgPath = svgfile;
02764     dataDefinedValues.insert( QgsPalLayerSettings::ShapeSVGFile, QVariant( svgfile ) );
02765   }
02766 
02767   // data defined shape size type?
02768   QgsPalLayerSettings::SizeType shpSizeType = shapeSizeType;
02769   if ( dataDefinedEvaluate( QgsPalLayerSettings::ShapeSizeType, exprVal ) )
02770   {
02771     QString stype = exprVal.toString().trimmed();
02772     QgsDebugMsgLevel( QString( "exprVal ShapeSizeType:%1" ).arg( stype ), 4 );
02773 
02774     if ( !stype.isEmpty() )
02775     {
02776       // "Buffer"
02777       QgsPalLayerSettings::SizeType sizType = QgsPalLayerSettings::SizeBuffer;
02778 
02779       if ( stype.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
02780       {
02781         sizType = QgsPalLayerSettings::SizeFixed;
02782       }
02783       shpSizeType = sizType;
02784       dataDefinedValues.insert( QgsPalLayerSettings::ShapeSizeType, QVariant(( int )sizType ) );
02785     }
02786   }
02787 
02788   // data defined shape size X? (SVGs only use X for sizing)
02789   double ddShpSizeX = shapeSize.x();
02790   if ( dataDefinedValEval( "double", QgsPalLayerSettings::ShapeSizeX, exprVal ) )
02791   {
02792     ddShpSizeX = exprVal.toDouble();
02793   }
02794 
02795   // data defined shape size Y?
02796   double ddShpSizeY = shapeSize.y();
02797   if ( dataDefinedValEval( "double", QgsPalLayerSettings::ShapeSizeY, exprVal ) )
02798   {
02799     ddShpSizeY = exprVal.toDouble();
02800   }
02801 
02802   // don't continue under certain circumstances (e.g. size is fixed)
02803   bool skip = false;
02804   if ( shapeKind == QgsPalLayerSettings::ShapeSVG
02805        && ( svgPath.isEmpty()
02806             || ( !svgPath.isEmpty()
02807                  && shpSizeType == QgsPalLayerSettings::SizeFixed
02808                  && ddShpSizeX == 0.0 ) ) )
02809   {
02810     skip = true;
02811   }
02812   if ( shapeKind != QgsPalLayerSettings::ShapeSVG
02813        && shpSizeType == QgsPalLayerSettings::SizeFixed
02814        && ( ddShpSizeX == 0.0 || ddShpSizeY == 0.0 ) )
02815   {
02816     skip = true;
02817   }
02818 
02819   if ( skip )
02820   {
02821     dataDefinedValues.insert( QgsPalLayerSettings::ShapeDraw, QVariant( false ) ); // trigger value
02822     dataDefinedValues.remove( QgsPalLayerSettings::ShapeTransparency );
02823     dataDefinedValues.remove( QgsPalLayerSettings::ShapeKind );
02824     dataDefinedValues.remove( QgsPalLayerSettings::ShapeSVGFile );
02825     dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeX );
02826     dataDefinedValues.remove( QgsPalLayerSettings::ShapeSizeY );
02827     return; // don't bother evaluating values that won't be used
02828   }
02829 
02830   // data defined shape size units?
02831   dataDefinedValEval( "units", QgsPalLayerSettings::ShapeSizeUnits, exprVal );
02832 
02833   // data defined shape rotation type?
02834   if ( dataDefinedEvaluate( QgsPalLayerSettings::ShapeRotationType, exprVal ) )
02835   {
02836     QString rotstr = exprVal.toString().trimmed();
02837     QgsDebugMsgLevel( QString( "exprVal ShapeRotationType:%1" ).arg( rotstr ), 4 );
02838 
02839     if ( !rotstr.isEmpty() )
02840     {
02841       // "Sync"
02842       QgsPalLayerSettings::RotationType rottype = QgsPalLayerSettings::RotationSync;
02843 
02844       if ( rotstr.compare( "Offset", Qt::CaseInsensitive ) == 0 )
02845       {
02846         rottype = QgsPalLayerSettings::RotationOffset;
02847       }
02848       else if ( rotstr.compare( "Fixed", Qt::CaseInsensitive ) == 0 )
02849       {
02850         rottype = QgsPalLayerSettings::RotationFixed;
02851       }
02852       dataDefinedValues.insert( QgsPalLayerSettings::ShapeRotationType, QVariant(( int )rottype ) );
02853     }
02854   }
02855 
02856   // data defined shape rotation?
02857   dataDefinedValEval( "rotation180", QgsPalLayerSettings::ShapeRotation, exprVal );
02858 
02859   // data defined shape offset?
02860   dataDefinedValEval( "pointf", QgsPalLayerSettings::ShapeOffset, exprVal );
02861 
02862   // data defined shape offset units?
02863   dataDefinedValEval( "units", QgsPalLayerSettings::ShapeOffsetUnits, exprVal );
02864 
02865   // data defined shape radii?
02866   dataDefinedValEval( "pointf", QgsPalLayerSettings::ShapeRadii, exprVal );
02867 
02868   // data defined shape radii units?
02869   dataDefinedValEval( "units", QgsPalLayerSettings::ShapeRadiiUnits, exprVal );
02870 
02871   // data defined shape blend mode?
02872   dataDefinedValEval( "blendmode", QgsPalLayerSettings::ShapeBlendMode, exprVal );
02873 
02874   // data defined shape fill color?
02875   dataDefinedValEval( "color", QgsPalLayerSettings::ShapeFillColor, exprVal );
02876 
02877   // data defined shape border color?
02878   dataDefinedValEval( "color", QgsPalLayerSettings::ShapeBorderColor, exprVal );
02879 
02880   // data defined shape border width?
02881   dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShapeBorderWidth, exprVal );
02882 
02883   // data defined shape border width units?
02884   dataDefinedValEval( "units", QgsPalLayerSettings::ShapeBorderWidthUnits, exprVal );
02885 
02886   // data defined shape join style?
02887   dataDefinedValEval( "joinstyle", QgsPalLayerSettings::ShapeJoinStyle, exprVal );
02888 
02889 }
02890 
02891 void QgsPalLayerSettings::parseDropShadow()
02892 {
02893   QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
02894 
02895   // data defined draw shadow?
02896   bool drawShadow = shadowDraw;
02897   if ( dataDefinedValEval( "bool", QgsPalLayerSettings::ShadowDraw, exprVal ) )
02898   {
02899     drawShadow = exprVal.toBool();
02900   }
02901 
02902   if ( !drawShadow )
02903   {
02904     return;
02905   }
02906 
02907   // data defined shadow transparency?
02908   int shadowTransp = shadowTransparency;
02909   if ( dataDefinedValEval( "transp", QgsPalLayerSettings::ShadowTransparency, exprVal ) )
02910   {
02911     shadowTransp = exprVal.toInt();
02912   }
02913 
02914   // data defined shadow offset distance?
02915   double shadowOffDist = shadowOffsetDist;
02916   if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShadowOffsetDist, exprVal ) )
02917   {
02918     shadowOffDist = exprVal.toDouble();
02919   }
02920 
02921   // data defined shadow offset distance?
02922   double shadowRad = shadowRadius;
02923   if ( dataDefinedValEval( "doublepos", QgsPalLayerSettings::ShadowRadius, exprVal ) )
02924   {
02925     shadowRad = exprVal.toDouble();
02926   }
02927 
02928   drawShadow = ( drawShadow && shadowTransp < 100 && !( shadowOffDist == 0.0 && shadowRad == 0.0 ) );
02929 
02930   if ( !drawShadow )
02931   {
02932     dataDefinedValues.insert( QgsPalLayerSettings::ShadowDraw, QVariant( false ) ); // trigger value
02933     dataDefinedValues.remove( QgsPalLayerSettings::ShadowTransparency );
02934     dataDefinedValues.remove( QgsPalLayerSettings::ShadowOffsetDist );
02935     dataDefinedValues.remove( QgsPalLayerSettings::ShadowRadius );
02936     return; // don't bother evaluating values that won't be used
02937   }
02938 
02939   // data defined shadow under type?
02940   if ( dataDefinedEvaluate( QgsPalLayerSettings::ShadowUnder, exprVal ) )
02941   {
02942     QString str = exprVal.toString().trimmed();
02943     QgsDebugMsgLevel( QString( "exprVal ShadowUnder:%1" ).arg( str ), 4 );
02944 
02945     if ( !str.isEmpty() )
02946     {
02947       // "Lowest"
02948       QgsPalLayerSettings::ShadowType shdwtype = QgsPalLayerSettings::ShadowLowest;
02949 
02950       if ( str.compare( "Text", Qt::CaseInsensitive ) == 0 )
02951       {
02952         shdwtype = QgsPalLayerSettings::ShadowText;
02953       }
02954       else if ( str.compare( "Buffer", Qt::CaseInsensitive ) == 0 )
02955       {
02956         shdwtype = QgsPalLayerSettings::ShadowBuffer;
02957       }
02958       else if ( str.compare( "Background", Qt::CaseInsensitive ) == 0 )
02959       {
02960         shdwtype = QgsPalLayerSettings::ShadowShape;
02961       }
02962       dataDefinedValues.insert( QgsPalLayerSettings::ShadowUnder, QVariant(( int )shdwtype ) );
02963     }
02964   }
02965 
02966   // data defined shadow offset angle?
02967   dataDefinedValEval( "rotation180", QgsPalLayerSettings::ShadowOffsetAngle, exprVal );
02968 
02969   // data defined shadow offset units?
02970   dataDefinedValEval( "units", QgsPalLayerSettings::ShadowOffsetUnits, exprVal );
02971 
02972   // data defined shadow radius?
02973   dataDefinedValEval( "double", QgsPalLayerSettings::ShadowRadius, exprVal );
02974 
02975   // data defined shadow radius units?
02976   dataDefinedValEval( "units", QgsPalLayerSettings::ShadowRadiusUnits, exprVal );
02977 
02978   // data defined shadow scale?  ( gui bounds to 0-2000, no upper bound here )
02979   dataDefinedValEval( "intpos", QgsPalLayerSettings::ShadowScale, exprVal );
02980 
02981   // data defined shadow color?
02982   dataDefinedValEval( "color", QgsPalLayerSettings::ShadowColor, exprVal );
02983 
02984   // data defined shadow blend mode?
02985   dataDefinedValEval( "blendmode", QgsPalLayerSettings::ShadowBlendMode, exprVal );
02986 }
02987 
02988 int QgsPalLayerSettings::sizeToPixel( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor ) const
02989 {
02990   return ( int )( scaleToPixelContext( size, c, unit, rasterfactor ) + 0.5 );
02991 }
02992 
02993 double QgsPalLayerSettings::scaleToPixelContext( double size, const QgsRenderContext& c, SizeUnit unit, bool rasterfactor ) const
02994 {
02995   // if render context is that of device (i.e. not a scaled map), just return size
02996   double mapUnitsPerPixel = c.mapToPixel().mapUnitsPerPixel();
02997 
02998   if ( unit == MapUnits && mapUnitsPerPixel > 0.0 )
02999   {
03000     size = size / mapUnitsPerPixel * ( rasterfactor ? c.rasterScaleFactor() : 1 );
03001   }
03002   else // e.g. in points or mm
03003   {
03004     double ptsTomm = ( unit == Points ? 0.352778 : 1 );
03005     size *= ptsTomm * c.scaleFactor() * ( rasterfactor ? c.rasterScaleFactor() : 1 );
03006   }
03007   return size;
03008 }
03009 
03010 // -------------
03011 
03012 QgsPalLabeling::QgsPalLabeling()
03013     : mMapRenderer( NULL ), mPal( NULL )
03014 {
03015 
03016   // find out engine defaults
03017   Pal p;
03018   mCandPoint = p.getPointP();
03019   mCandLine = p.getLineP();
03020   mCandPolygon = p.getPolyP();
03021 
03022   switch ( p.getSearch() )
03023   {
03024     case CHAIN: mSearch = Chain; break;
03025     case POPMUSIC_TABU: mSearch = Popmusic_Tabu; break;
03026     case POPMUSIC_CHAIN: mSearch = Popmusic_Chain; break;
03027     case POPMUSIC_TABU_CHAIN: mSearch = Popmusic_Tabu_Chain; break;
03028     case FALP: mSearch = Falp; break;
03029   }
03030 
03031   mShowingCandidates = false;
03032   mShowingAllLabels = false;
03033 
03034   mLabelSearchTree = new QgsLabelSearchTree();
03035 }
03036 
03037 QgsPalLabeling::~QgsPalLabeling()
03038 {
03039   // make sure we've freed everything
03040   exit();
03041 
03042   clearActiveLayers();
03043 
03044   delete mLabelSearchTree;
03045   mLabelSearchTree = NULL;
03046 }
03047 
03048 bool QgsPalLabeling::willUseLayer( QgsVectorLayer* layer )
03049 {
03050   // don't do QgsPalLayerSettings::readFromLayer( layer ) if not needed
03051   bool enabled = false;
03052   if ( layer->customProperty( "labeling" ).toString() == QString( "pal" ) )
03053     enabled = layer->customProperty( "labeling/enabled", QVariant( false ) ).toBool();
03054 
03055   return enabled;
03056 }
03057 
03058 void QgsPalLabeling::clearActiveLayers()
03059 {
03060   QHash<QgsVectorLayer*, QgsPalLayerSettings>::iterator lit;
03061   for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
03062   {
03063     clearActiveLayer( lit.key() );
03064   }
03065   mActiveLayers.clear();
03066 }
03067 
03068 void QgsPalLabeling::clearActiveLayer( QgsVectorLayer* layer )
03069 {
03070   QgsPalLayerSettings& lyr = mActiveLayers[layer];
03071 
03072   // delete all QgsDataDefined objects (which also deletes their QgsExpression object)
03073   QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::iterator it = lyr.dataDefinedProperties.begin();
03074   for ( ; it != lyr.dataDefinedProperties.constEnd(); ++it )
03075   {
03076     delete( it.value() );
03077     it.value() = 0;
03078   }
03079   lyr.dataDefinedProperties.clear();
03080 }
03081 
03082 int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QSet<int>& attrIndices, QgsRenderContext& ctx )
03083 {
03084   Q_ASSERT( mMapRenderer != NULL );
03085 
03086   if ( !willUseLayer( layer ) )
03087   {
03088     return 0;
03089   }
03090 
03091   QgsDebugMsgLevel( "PREPARE LAYER " + layer->id(), 4 );
03092 
03093   // start with a temporary settings class, find out labeling info
03094   QgsPalLayerSettings lyrTmp;
03095   lyrTmp.readFromLayer( layer );
03096 
03097   if ( lyrTmp.fieldName.isEmpty() )
03098   {
03099     return 0;
03100   }
03101 
03102   int fldIndex = -1;
03103   if ( lyrTmp.isExpression )
03104   {
03105     QgsExpression exp( lyrTmp.fieldName );
03106     if ( exp.hasEvalError() )
03107     {
03108       QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 );
03109       return 0;
03110     }
03111   }
03112   else
03113   {
03114     // If we aren't an expression, we check to see if we can find the column.
03115     fldIndex = layer->fieldNameIndex( lyrTmp.fieldName );
03116     if ( fldIndex == -1 )
03117     {
03118       return 0;
03119     }
03120   }
03121 
03122   // add layer settings to the pallabeling hashtable: <QgsVectorLayer*, QgsPalLayerSettings>
03123   mActiveLayers.insert( layer, lyrTmp );
03124   // start using the reference to the layer in hashtable instead of local instance
03125   QgsPalLayerSettings& lyr = mActiveLayers[layer];
03126 
03127   lyr.mCurFields = &( layer->pendingFields() );
03128 
03129   // add field indices for label's text, from expression or field
03130   if ( lyr.isExpression )
03131   {
03132     // prepare expression for use in QgsPalLayerSettings::registerFeature()
03133     QgsExpression* exp = lyr.getLabelExpression();
03134     exp->prepare( layer->pendingFields() );
03135     if ( exp->hasEvalError() )
03136     {
03137       QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 );
03138     }
03139     foreach ( QString name, exp->referencedColumns() )
03140     {
03141       QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 );
03142       attrIndices.insert( layer->fieldNameIndex( name ) );
03143     }
03144   }
03145   else
03146   {
03147     if ( fldIndex != -1 )
03148     {
03149       attrIndices.insert( fldIndex );
03150     }
03151   }
03152 
03153   // add field indices of data defined expression or field
03154   QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator dIt = lyr.dataDefinedProperties.constBegin();
03155   for ( ; dIt != lyr.dataDefinedProperties.constEnd(); ++dIt )
03156   {
03157     QgsDataDefined* dd = dIt.value();
03158     if ( !dd->isActive() )
03159     {
03160       continue;
03161     }
03162 
03163     // NOTE: the following also prepares any expressions for later use
03164 
03165     // store parameters for data defined expressions
03166     QMap<QString, QVariant> exprParams;
03167     exprParams.insert( "scale", ctx.rendererScale() );
03168 
03169     dd->setExpressionParams( exprParams );
03170 
03171     // this will return columns for expressions or field name, depending upon what is set to be used
03172     QStringList cols = dd->referencedColumns( layer ); // <-- prepares any expressions, too
03173 
03174     //QgsDebugMsgLevel( QString( "Data defined referenced columns:" ) + cols.join( "," ), 4 );
03175     foreach ( QString name, cols )
03176     {
03177       attrIndices.insert( layer->fieldNameIndex( name ) );
03178     }
03179   }
03180 
03181   // how to place the labels
03182   Arrangement arrangement;
03183   switch ( lyr.placement )
03184   {
03185     case QgsPalLayerSettings::AroundPoint: arrangement = P_POINT; break;
03186     case QgsPalLayerSettings::OverPoint:   arrangement = P_POINT_OVER; break;
03187     case QgsPalLayerSettings::Line:        arrangement = P_LINE; break;
03188     case QgsPalLayerSettings::Curved:      arrangement = P_CURVED; break;
03189     case QgsPalLayerSettings::Horizontal:  arrangement = P_HORIZ; break;
03190     case QgsPalLayerSettings::Free:        arrangement = P_FREE; break;
03191     default: Q_ASSERT( "unsupported placement" && 0 ); return 0;
03192   }
03193 
03194   // create the pal layer
03195   double priority = 1 - lyr.priority / 10.0; // convert 0..10 --> 1..0
03196   double min_scale = -1, max_scale = -1;
03197 
03198   // handled in QgsPalLayerSettings::registerFeature now
03199   //if ( lyr.scaleVisibility && !lyr.dataDefinedIsActive( QgsPalLayerSettings::ScaleVisibility ) )
03200   //{
03201   //  min_scale = lyr.scaleMin;
03202   //  max_scale = lyr.scaleMax;
03203   //}
03204 
03205   Layer* l = mPal->addLayer( layer->id().toUtf8().data(),
03206                              min_scale, max_scale, arrangement,
03207                              METER, priority, lyr.obstacle, true, true,
03208                              lyr.displayAll );
03209 
03210   if ( lyr.placementFlags )
03211     l->setArrangementFlags( lyr.placementFlags );
03212 
03213   // set label mode (label per feature is the default)
03214   l->setLabelMode( lyr.labelPerPart ? Layer::LabelPerFeaturePart : Layer::LabelPerFeature );
03215 
03216   // set whether adjacent lines should be merged
03217   l->setMergeConnectedLines( lyr.mergeLines );
03218 
03219   // set how to show upside-down labels
03220   Layer::UpsideDownLabels upsdnlabels;
03221   switch ( lyr.upsidedownLabels )
03222   {
03223     case QgsPalLayerSettings::Upright:     upsdnlabels = Layer::Upright; break;
03224     case QgsPalLayerSettings::ShowDefined: upsdnlabels = Layer::ShowDefined; break;
03225     case QgsPalLayerSettings::ShowAll:     upsdnlabels = Layer::ShowAll; break;
03226     default: Q_ASSERT( "unsupported upside-down label setting" && 0 ); return 0;
03227   }
03228   l->setUpsidedownLabels( upsdnlabels );
03229 
03230 //  // fix for font size in map units causing font to show pointsize at small map scales
03231 //  int pixelFontSize = lyr.sizeToPixel( lyr.textFont.pointSizeF(), ctx,
03232 //                                       lyr.fontSizeInMapUnits ? QgsPalLayerSettings::MapUnits : QgsPalLayerSettings::Points,
03233 //                                       true );
03234 
03235 //  if ( pixelFontSize < 1 )
03236 //  {
03237 //    lyr.textFont.setPointSize( 1 );
03238 //    lyr.textFont.setPixelSize( 1 );
03239 //  }
03240 //  else
03241 //  {
03242 //    lyr.textFont.setPixelSize( pixelFontSize );
03243 //  }
03244 
03245 //  // scale spacing sizes if using map units
03246 //  if ( lyr.fontSizeInMapUnits )
03247 //  {
03248 //    double spacingPixelSize;
03249 //    if ( lyr.textFont.wordSpacing() != 0 )
03250 //    {
03251 //      spacingPixelSize = lyr.textFont.wordSpacing() / ctx.mapToPixel().mapUnitsPerPixel() * ctx.rasterScaleFactor();
03252 //      lyr.textFont.setWordSpacing( spacingPixelSize );
03253 //    }
03254 
03255 //    if ( lyr.textFont.letterSpacing() != 0 )
03256 //    {
03257 //      spacingPixelSize = lyr.textFont.letterSpacing() / ctx.mapToPixel().mapUnitsPerPixel() * ctx.rasterScaleFactor();
03258 //      lyr.textFont.setLetterSpacing( QFont::AbsoluteSpacing, spacingPixelSize );
03259 //    }
03260 //  }
03261 
03262   //raster and vector scale factors
03263   lyr.vectorScaleFactor = ctx.scaleFactor();
03264   lyr.rasterCompressFactor = ctx.rasterScaleFactor();
03265 
03266   // save the pal layer to our layer context (with some additional info)
03267   lyr.palLayer = l;
03268   lyr.fieldIndex = fldIndex;
03269 
03270   lyr.xform = mMapRenderer->coordinateTransform();
03271   if ( mMapRenderer->hasCrsTransformEnabled() )
03272     lyr.ct = new QgsCoordinateTransform( layer->crs(), mMapRenderer->destinationCrs() );
03273   else
03274     lyr.ct = NULL;
03275   lyr.ptZero = lyr.xform->toMapCoordinates( 0, 0 );
03276   lyr.ptOne = lyr.xform->toMapCoordinates( 1, 0 );
03277 
03278   // rect for clipping
03279   lyr.extentGeom = QgsGeometry::fromRect( mMapRenderer->extent() );
03280 
03281   lyr.mFeatsSendingToPal = 0;
03282 
03283   return 1; // init successful
03284 }
03285 
03286 int QgsPalLabeling::addDiagramLayer( QgsVectorLayer* layer, QgsDiagramLayerSettings *s )
03287 {
03288   Layer* l = mPal->addLayer( layer->id().append( "d" ).toUtf8().data(), -1, -1, pal::Arrangement( s->placement ), METER, s->priority, s->obstacle, true, true );
03289   l->setArrangementFlags( s->placementFlags );
03290 
03291   s->palLayer = l;
03292   if ( mMapRenderer->hasCrsTransformEnabled() )
03293     s->ct = new QgsCoordinateTransform( layer->crs(), mMapRenderer->destinationCrs() );
03294   else
03295     s->ct = NULL;
03296   s->xform = mMapRenderer->coordinateTransform();
03297   mActiveDiagramLayers.insert( layer, *s );
03298   return 1;
03299 }
03300 
03301 void QgsPalLabeling::registerFeature( QgsVectorLayer* layer, QgsFeature& f, const QgsRenderContext& context )
03302 {
03303   QgsPalLayerSettings& lyr = mActiveLayers[layer];
03304   lyr.registerFeature( layer, f, context );
03305 }
03306 
03307 void QgsPalLabeling::registerDiagramFeature( QgsVectorLayer* layer, QgsFeature& feat, const QgsRenderContext& context )
03308 {
03309   //get diagram layer settings, diagram renderer
03310   QHash<QgsVectorLayer*, QgsDiagramLayerSettings>::iterator layerIt = mActiveDiagramLayers.find( layer );
03311   if ( layerIt == mActiveDiagramLayers.constEnd() )
03312   {
03313     return;
03314   }
03315 
03316   //convert geom to geos
03317   QgsGeometry* geom = feat.geometry();
03318 
03319   if ( layerIt.value().ct && !willUseLayer( layer ) ) // reproject the geometry if feature not already transformed for labeling
03320   {
03321     geom->transform( *( layerIt.value().ct ) );
03322   }
03323 
03324   GEOSGeometry* geos_geom = geom->asGeos();
03325   if ( geos_geom == 0 )
03326   {
03327     return; // invalid geometry
03328   }
03329 
03330   //create PALGeometry with diagram = true
03331   QgsPalGeometry* lbl = new QgsPalGeometry( feat.id(), "", GEOSGeom_clone( geos_geom ) );
03332   lbl->setIsDiagram( true );
03333 
03334   // record the created geometry - it will be deleted at the end.
03335   layerIt.value().geometries.append( lbl );
03336 
03337   double diagramWidth = 0;
03338   double diagramHeight = 0;
03339   QgsDiagramRendererV2* dr = layerIt.value().renderer;
03340   if ( dr )
03341   {
03342     QSizeF diagSize = dr->sizeMapUnits( feat.attributes(), context );
03343     if ( diagSize.isValid() )
03344     {
03345       diagramWidth = diagSize.width();
03346       diagramHeight = diagSize.height();
03347     }
03348 
03349     //append the diagram attributes to lbl
03350     lbl->setDiagramAttributes( feat.attributes() );
03351   }
03352 
03353   //  feature to the layer
03354   int ddColX = layerIt.value().xPosColumn;
03355   int ddColY = layerIt.value().yPosColumn;
03356   double ddPosX = 0.0;
03357   double ddPosY = 0.0;
03358   bool ddPos = ( ddColX >= 0 && ddColY >= 0 );
03359   if ( ddPos )
03360   {
03361     bool posXOk, posYOk;
03362     //data defined diagram position is always centered
03363     ddPosX = feat.attribute( ddColX ).toDouble( &posXOk ) - diagramWidth / 2.0;
03364     ddPosY = feat.attribute( ddColY ).toDouble( &posYOk ) - diagramHeight / 2.0;
03365     if ( !posXOk || !posYOk )
03366     {
03367       ddPos = false;
03368     }
03369     else
03370     {
03371       const QgsCoordinateTransform* ct = layerIt.value().ct;
03372       if ( ct )
03373       {
03374         double z = 0;
03375         ct->transformInPlace( ddPosX, ddPosY, z );
03376       }
03377     }
03378   }
03379 
03380   try
03381   {
03382     if ( !layerIt.value().palLayer->registerFeature( lbl->strId(), lbl, diagramWidth, diagramHeight, "", ddPosX, ddPosY, ddPos ) )
03383     {
03384       return;
03385     }
03386   }
03387   catch ( std::exception &e )
03388   {
03389     Q_UNUSED( e );
03390     QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( feat.id() ) + QString::fromLatin1( e.what() ), 4 );
03391     return;
03392   }
03393 
03394   pal::Feature* palFeat = layerIt.value().palLayer->getFeature( lbl->strId() );
03395   QgsPoint ptZero = layerIt.value().xform->toMapCoordinates( 0, 0 );
03396   QgsPoint ptOne = layerIt.value().xform->toMapCoordinates( 1, 0 );
03397   palFeat->setDistLabel( qAbs( ptOne.x() - ptZero.x() ) * layerIt.value().dist );
03398 }
03399 
03400 
03401 void QgsPalLabeling::init( QgsMapRenderer* mr )
03402 {
03403   mMapRenderer = mr;
03404 
03405   // delete if exists already
03406   if ( mPal )
03407     delete mPal;
03408 
03409   mPal = new Pal;
03410 
03411   SearchMethod s;
03412   switch ( mSearch )
03413   {
03414     default:
03415     case Chain: s = CHAIN; break;
03416     case Popmusic_Tabu: s = POPMUSIC_TABU; break;
03417     case Popmusic_Chain: s = POPMUSIC_CHAIN; break;
03418     case Popmusic_Tabu_Chain: s = POPMUSIC_TABU_CHAIN; break;
03419     case Falp: s = FALP; break;
03420   }
03421   mPal->setSearch( s );
03422 
03423   // set number of candidates generated per feature
03424   mPal->setPointP( mCandPoint );
03425   mPal->setLineP( mCandLine );
03426   mPal->setPolyP( mCandPolygon );
03427 
03428   clearActiveLayers(); // free any previous QgsDataDefined objects
03429   mActiveDiagramLayers.clear();
03430 }
03431 
03432 void QgsPalLabeling::exit()
03433 {
03434   delete mPal;
03435   mPal = NULL;
03436   mMapRenderer = NULL;
03437 }
03438 
03439 QgsPalLayerSettings& QgsPalLabeling::layer( const QString& layerName )
03440 {
03441   QHash<QgsVectorLayer*, QgsPalLayerSettings>::iterator lit;
03442   for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
03443   {
03444     if ( lit.key() && lit.key()->id() == layerName )
03445     {
03446       return lit.value();
03447     }
03448   }
03449   return mInvalidLayerSettings;
03450 }
03451 
03452 void QgsPalLabeling::dataDefinedTextStyle( QgsPalLayerSettings& tmpLyr,
03453     const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
03454 {
03455   //font color
03456   if ( ddValues.contains( QgsPalLayerSettings::Color ) )
03457   {
03458     QVariant ddColor = ddValues.value( QgsPalLayerSettings::Color );
03459     tmpLyr.textColor = ddColor.value<QColor>();
03460   }
03461 
03462   //font transparency
03463   if ( ddValues.contains( QgsPalLayerSettings::FontTransp ) )
03464   {
03465     tmpLyr.textTransp = ddValues.value( QgsPalLayerSettings::FontTransp ).toInt();
03466   }
03467 
03468   tmpLyr.textColor.setAlphaF(( 100.0 - ( double )( tmpLyr.textTransp ) ) / 100.0 );
03469 
03470   //font blend mode
03471   if ( ddValues.contains( QgsPalLayerSettings::FontBlendMode ) )
03472   {
03473     tmpLyr.blendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::FontBlendMode ).toInt();
03474   }
03475 }
03476 
03477 void QgsPalLabeling::dataDefinedTextFormatting( QgsPalLayerSettings& tmpLyr,
03478     const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
03479 {
03480   if ( ddValues.contains( QgsPalLayerSettings::MultiLineWrapChar ) )
03481   {
03482     tmpLyr.wrapChar = ddValues.value( QgsPalLayerSettings::MultiLineWrapChar ).toString();
03483   }
03484 
03485   if ( !tmpLyr.wrapChar.isEmpty() )
03486   {
03487 
03488     if ( ddValues.contains( QgsPalLayerSettings::MultiLineHeight ) )
03489     {
03490       tmpLyr.multilineHeight = ddValues.value( QgsPalLayerSettings::MultiLineHeight ).toDouble();
03491     }
03492 
03493     if ( ddValues.contains( QgsPalLayerSettings::MultiLineAlignment ) )
03494     {
03495       tmpLyr.multilineAlign = ( QgsPalLayerSettings::MultiLineAlign )ddValues.value( QgsPalLayerSettings::MultiLineAlignment ).toInt();
03496     }
03497 
03498   }
03499 
03500   if ( ddValues.contains( QgsPalLayerSettings::DirSymbDraw ) )
03501   {
03502     tmpLyr.addDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbDraw ).toBool();
03503   }
03504 
03505   if ( tmpLyr.addDirectionSymbol )
03506   {
03507 
03508     if ( ddValues.contains( QgsPalLayerSettings::DirSymbLeft ) )
03509     {
03510       tmpLyr.leftDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbLeft ).toString();
03511     }
03512     if ( ddValues.contains( QgsPalLayerSettings::DirSymbRight ) )
03513     {
03514       tmpLyr.rightDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbRight ).toString();
03515     }
03516 
03517     if ( ddValues.contains( QgsPalLayerSettings::DirSymbPlacement ) )
03518     {
03519       tmpLyr.placeDirectionSymbol = ( QgsPalLayerSettings::DirectionSymbols )ddValues.value( QgsPalLayerSettings::DirSymbPlacement ).toInt();
03520     }
03521 
03522     if ( ddValues.contains( QgsPalLayerSettings::DirSymbReverse ) )
03523     {
03524       tmpLyr.reverseDirectionSymbol = ddValues.value( QgsPalLayerSettings::DirSymbReverse ).toBool();
03525     }
03526 
03527   }
03528 }
03529 
03530 void QgsPalLabeling::dataDefinedTextBuffer( QgsPalLayerSettings& tmpLyr,
03531     const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
03532 {
03533   //buffer draw
03534   if ( ddValues.contains( QgsPalLayerSettings::BufferDraw ) )
03535   {
03536     tmpLyr.bufferDraw = ddValues.value( QgsPalLayerSettings::BufferDraw ).toBool();
03537   }
03538 
03539   if ( !tmpLyr.bufferDraw )
03540   {
03541     // tmpLyr.bufferSize > 0.0 && tmpLyr.bufferTransp < 100 figured in during evaluation
03542     return; // don't continue looking for unused values
03543   }
03544 
03545   //buffer size
03546   if ( ddValues.contains( QgsPalLayerSettings::BufferSize ) )
03547   {
03548     tmpLyr.bufferSize = ddValues.value( QgsPalLayerSettings::BufferSize ).toDouble();
03549   }
03550 
03551   //buffer transparency
03552   if ( ddValues.contains( QgsPalLayerSettings::BufferTransp ) )
03553   {
03554     tmpLyr.bufferTransp = ddValues.value( QgsPalLayerSettings::BufferTransp ).toInt();
03555   }
03556 
03557   //buffer size units
03558   if ( ddValues.contains( QgsPalLayerSettings::BufferUnit ) )
03559   {
03560     QgsPalLayerSettings::SizeUnit bufunit = ( QgsPalLayerSettings::SizeUnit )ddValues.value( QgsPalLayerSettings::BufferUnit ).toInt();
03561     tmpLyr.bufferSizeInMapUnits = ( bufunit == QgsPalLayerSettings::MapUnits );
03562   }
03563 
03564   //buffer color
03565   if ( ddValues.contains( QgsPalLayerSettings::BufferColor ) )
03566   {
03567     QVariant ddColor = ddValues.value( QgsPalLayerSettings::BufferColor );
03568     tmpLyr.bufferColor = ddColor.value<QColor>();
03569   }
03570 
03571   // apply any transparency
03572   tmpLyr.bufferColor.setAlphaF(( 100.0 - ( double )( tmpLyr.bufferTransp ) ) / 100.0 );
03573 
03574   //buffer pen join style
03575   if ( ddValues.contains( QgsPalLayerSettings::BufferJoinStyle ) )
03576   {
03577     tmpLyr.bufferJoinStyle = ( Qt::PenJoinStyle )ddValues.value( QgsPalLayerSettings::BufferJoinStyle ).toInt();
03578   }
03579 
03580   //buffer blend mode
03581   if ( ddValues.contains( QgsPalLayerSettings::BufferBlendMode ) )
03582   {
03583     tmpLyr.bufferBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::BufferBlendMode ).toInt();
03584   }
03585 }
03586 
03587 void QgsPalLabeling::dataDefinedShapeBackground( QgsPalLayerSettings& tmpLyr,
03588     const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
03589 {
03590   //shape draw
03591   if ( ddValues.contains( QgsPalLayerSettings::ShapeDraw ) )
03592   {
03593     tmpLyr.shapeDraw = ddValues.value( QgsPalLayerSettings::ShapeDraw ).toBool();
03594   }
03595 
03596   if ( !tmpLyr.shapeDraw )
03597   {
03598     return; // don't continue looking for unused values
03599   }
03600 
03601   if ( ddValues.contains( QgsPalLayerSettings::ShapeKind ) )
03602   {
03603     tmpLyr.shapeType = ( QgsPalLayerSettings::ShapeType )ddValues.value( QgsPalLayerSettings::ShapeKind ).toInt();
03604   }
03605 
03606   if ( ddValues.contains( QgsPalLayerSettings::ShapeSVGFile ) )
03607   {
03608     tmpLyr.shapeSVGFile = ddValues.value( QgsPalLayerSettings::ShapeSVGFile ).toString();
03609   }
03610 
03611   if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeType ) )
03612   {
03613     tmpLyr.shapeSizeType = ( QgsPalLayerSettings::SizeType )ddValues.value( QgsPalLayerSettings::ShapeSizeType ).toInt();
03614   }
03615 
03616   if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeX ) )
03617   {
03618     tmpLyr.shapeSize.setX( ddValues.value( QgsPalLayerSettings::ShapeSizeX ).toDouble() );
03619   }
03620   if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeY ) )
03621   {
03622     tmpLyr.shapeSize.setY( ddValues.value( QgsPalLayerSettings::ShapeSizeY ).toDouble() );
03623   }
03624 
03625   if ( ddValues.contains( QgsPalLayerSettings::ShapeSizeUnits ) )
03626   {
03627     tmpLyr.shapeSizeUnits = ( QgsPalLayerSettings::SizeUnit )ddValues.value( QgsPalLayerSettings::ShapeSizeUnits ).toInt();
03628   }
03629 
03630   if ( ddValues.contains( QgsPalLayerSettings::ShapeRotationType ) )
03631   {
03632     tmpLyr.shapeRotationType = ( QgsPalLayerSettings::RotationType )ddValues.value( QgsPalLayerSettings::ShapeRotationType ).toInt();
03633   }
03634 
03635   if ( ddValues.contains( QgsPalLayerSettings::ShapeRotation ) )
03636   {
03637     tmpLyr.shapeRotation = ddValues.value( QgsPalLayerSettings::ShapeRotation ).toDouble();
03638   }
03639 
03640   if ( ddValues.contains( QgsPalLayerSettings::ShapeOffset ) )
03641   {
03642     tmpLyr.shapeOffset = ddValues.value( QgsPalLayerSettings::ShapeOffset ).toPointF();
03643   }
03644 
03645   if ( ddValues.contains( QgsPalLayerSettings::ShapeOffsetUnits ) )
03646   {
03647     tmpLyr.shapeOffsetUnits = ( QgsPalLayerSettings::SizeUnit )ddValues.value( QgsPalLayerSettings::ShapeOffsetUnits ).toInt();
03648   }
03649 
03650   if ( ddValues.contains( QgsPalLayerSettings::ShapeRadii ) )
03651   {
03652     tmpLyr.shapeRadii = ddValues.value( QgsPalLayerSettings::ShapeRadii ).toPointF();
03653   }
03654 
03655   if ( ddValues.contains( QgsPalLayerSettings::ShapeRadiiUnits ) )
03656   {
03657     tmpLyr.shapeRadiiUnits = ( QgsPalLayerSettings::SizeUnit )ddValues.value( QgsPalLayerSettings::ShapeRadiiUnits ).toInt();
03658   }
03659 
03660   if ( ddValues.contains( QgsPalLayerSettings::ShapeTransparency ) )
03661   {
03662     tmpLyr.shapeTransparency = ddValues.value( QgsPalLayerSettings::ShapeTransparency ).toInt();
03663   }
03664 
03665   if ( ddValues.contains( QgsPalLayerSettings::ShapeBlendMode ) )
03666   {
03667     tmpLyr.shapeBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::ShapeBlendMode ).toInt();
03668   }
03669 
03670   if ( ddValues.contains( QgsPalLayerSettings::ShapeFillColor ) )
03671   {
03672     QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeFillColor );
03673     tmpLyr.shapeFillColor = ddColor.value<QColor>();
03674   }
03675 
03676   if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderColor ) )
03677   {
03678     QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShapeBorderColor );
03679     tmpLyr.shapeBorderColor = ddColor.value<QColor>();
03680   }
03681 
03682   if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderWidth ) )
03683   {
03684     tmpLyr.shapeBorderWidth = ddValues.value( QgsPalLayerSettings::ShapeBorderWidth ).toDouble();
03685   }
03686 
03687   if ( ddValues.contains( QgsPalLayerSettings::ShapeBorderWidthUnits ) )
03688   {
03689     tmpLyr.shapeBorderWidthUnits = ( QgsPalLayerSettings::SizeUnit )ddValues.value( QgsPalLayerSettings::ShapeBorderWidthUnits ).toInt();
03690   }
03691 
03692   if ( ddValues.contains( QgsPalLayerSettings::ShapeJoinStyle ) )
03693   {
03694     tmpLyr.shapeJoinStyle = ( Qt::PenJoinStyle )ddValues.value( QgsPalLayerSettings::ShapeJoinStyle ).toInt();
03695   }
03696 }
03697 
03698 void QgsPalLabeling::dataDefinedDropShadow( QgsPalLayerSettings& tmpLyr,
03699     const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues )
03700 {
03701   //shadow draw
03702   if ( ddValues.contains( QgsPalLayerSettings::ShadowDraw ) )
03703   {
03704     tmpLyr.shadowDraw = ddValues.value( QgsPalLayerSettings::ShadowDraw ).toBool();
03705   }
03706 
03707   if ( !tmpLyr.shadowDraw )
03708   {
03709     return; // don't continue looking for unused values
03710   }
03711 
03712   if ( ddValues.contains( QgsPalLayerSettings::ShadowUnder ) )
03713   {
03714     tmpLyr.shadowUnder = ( QgsPalLayerSettings::ShadowType )ddValues.value( QgsPalLayerSettings::ShadowUnder ).toInt();
03715   }
03716 
03717   if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetAngle ) )
03718   {
03719     tmpLyr.shadowOffsetAngle = ddValues.value( QgsPalLayerSettings::ShadowOffsetAngle ).toInt();
03720   }
03721 
03722   if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetDist ) )
03723   {
03724     tmpLyr.shadowOffsetDist = ddValues.value( QgsPalLayerSettings::ShadowOffsetDist ).toDouble();
03725   }
03726 
03727   if ( ddValues.contains( QgsPalLayerSettings::ShadowOffsetUnits ) )
03728   {
03729     tmpLyr.shadowOffsetUnits = ( QgsPalLayerSettings::SizeUnit )ddValues.value( QgsPalLayerSettings::ShadowOffsetUnits ).toInt();
03730   }
03731 
03732   if ( ddValues.contains( QgsPalLayerSettings::ShadowRadius ) )
03733   {
03734     tmpLyr.shadowRadius = ddValues.value( QgsPalLayerSettings::ShadowRadius ).toDouble();
03735   }
03736 
03737   if ( ddValues.contains( QgsPalLayerSettings::ShadowRadiusUnits ) )
03738   {
03739     tmpLyr.shadowRadiusUnits = ( QgsPalLayerSettings::SizeUnit )ddValues.value( QgsPalLayerSettings::ShadowRadiusUnits ).toInt();
03740   }
03741 
03742   if ( ddValues.contains( QgsPalLayerSettings::ShadowTransparency ) )
03743   {
03744     tmpLyr.shadowTransparency = ddValues.value( QgsPalLayerSettings::ShadowTransparency ).toInt();
03745   }
03746 
03747   if ( ddValues.contains( QgsPalLayerSettings::ShadowScale ) )
03748   {
03749     tmpLyr.shadowScale = ddValues.value( QgsPalLayerSettings::ShadowScale ).toInt();
03750   }
03751 
03752   if ( ddValues.contains( QgsPalLayerSettings::ShadowColor ) )
03753   {
03754     QVariant ddColor = ddValues.value( QgsPalLayerSettings::ShadowColor );
03755     tmpLyr.shadowColor = ddColor.value<QColor>();
03756   }
03757 
03758   if ( ddValues.contains( QgsPalLayerSettings::ShadowBlendMode ) )
03759   {
03760     tmpLyr.shadowBlendMode = ( QPainter::CompositionMode )ddValues.value( QgsPalLayerSettings::ShadowBlendMode ).toInt();
03761   }
03762 }
03763 
03764 void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
03765 {
03766   Q_ASSERT( mMapRenderer != NULL );
03767   QPainter* painter = context.painter();
03768   QgsRectangle extent = context.extent();
03769 
03770   if ( mLabelSearchTree )
03771   {
03772     mLabelSearchTree->clear();
03773   }
03774 
03775   QTime t;
03776   t.start();
03777 
03778   // do the labeling itself
03779   double scale = mMapRenderer->scale(); // scale denominator
03780   QgsRectangle r = extent;
03781   double bbox[] = { r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum() };
03782 
03783   std::list<LabelPosition*>* labels;
03784   pal::Problem* problem;
03785   try
03786   {
03787     problem = mPal->extractProblem( scale, bbox );
03788   }
03789   catch ( std::exception& e )
03790   {
03791     Q_UNUSED( e );
03792     QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
03793     //mActiveLayers.clear(); // clean up
03794     return;
03795   }
03796 
03797   const QgsMapToPixel* xform = mMapRenderer->coordinateTransform();
03798 
03799   // draw rectangles with all candidates
03800   // this is done before actual solution of the problem
03801   // before number of candidates gets reduced
03802   mCandidates.clear();
03803   if ( mShowingCandidates && problem )
03804   {
03805     painter->setPen( QColor( 0, 0, 0, 64 ) );
03806     painter->setBrush( Qt::NoBrush );
03807     for ( int i = 0; i < problem->getNumFeatures(); i++ )
03808     {
03809       for ( int j = 0; j < problem->getFeatureCandidateCount( i ); j++ )
03810       {
03811         pal::LabelPosition* lp = problem->getFeatureCandidate( i, j );
03812 
03813         drawLabelCandidateRect( lp, painter, xform );
03814       }
03815     }
03816   }
03817 
03818   // find the solution
03819   labels = mPal->solveProblem( problem, mShowingAllLabels );
03820 
03821   QgsDebugMsgLevel( QString( "LABELING work:  %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( labels->size() ), 4 );
03822   t.restart();
03823 
03824   painter->setRenderHint( QPainter::Antialiasing );
03825 
03826   // draw the labels
03827   std::list<LabelPosition*>::iterator it = labels->begin();
03828   for ( ; it != labels->end(); ++it )
03829   {
03830     QgsPalGeometry* palGeometry = dynamic_cast< QgsPalGeometry* >(( *it )->getFeaturePart()->getUserGeometry() );
03831     if ( !palGeometry )
03832     {
03833       continue;
03834     }
03835 
03836     //layer names
03837     QString layerNameUtf8 = QString::fromUtf8(( *it )->getLayerName() );
03838     if ( palGeometry->isDiagram() )
03839     {
03840       //render diagram
03841       QHash<QgsVectorLayer*, QgsDiagramLayerSettings>::iterator dit = mActiveDiagramLayers.begin();
03842       for ( dit = mActiveDiagramLayers.begin(); dit != mActiveDiagramLayers.end(); ++dit )
03843       {
03844         if ( dit.key() && dit.key()->id().append( "d" ) == layerNameUtf8 )
03845         {
03846           QgsPoint outPt = xform->transform(( *it )->getX(), ( *it )->getY() );
03847           dit.value().renderer->renderDiagram( palGeometry->diagramAttributes(), context, QPointF( outPt.x(), outPt.y() ) );
03848         }
03849       }
03850 
03851       //insert into label search tree to manipulate position interactively
03852       if ( mLabelSearchTree )
03853       {
03854         //for diagrams, remove the additional 'd' at the end of the layer id
03855         QString layerId = layerNameUtf8;
03856         layerId.chop( 1 );
03857         mLabelSearchTree->insertLabel( *it,  QString( palGeometry->strId() ).toInt(), QString( "" ), layerId, QFont(), true, false );
03858       }
03859       continue;
03860     }
03861 
03862     const QgsPalLayerSettings& lyr = layer( layerNameUtf8 );
03863 
03864     // Copy to temp, editable layer settings
03865     // these settings will be changed by any data defined values, then used for rendering label components
03866     // settings may be adjusted during rendering of components
03867     QgsPalLayerSettings tmpLyr( lyr );
03868 
03869     // apply any previously applied data defined settings for the label
03870     const QMap< QgsPalLayerSettings::DataDefinedProperties, QVariant >& ddValues = palGeometry->dataDefinedValues();
03871 
03872     //font
03873     QFont dFont = palGeometry->definedFont();
03874     // following debug is >= Qt 4.8 only ( because of QFont::styleName() )
03875 #if QT_VERSION >= 0x040800
03876     QgsDebugMsgLevel( QString( "PAL font tmpLyr: %1, Style: %2" ).arg( tmpLyr.textFont.toString() ).arg( QFontInfo( tmpLyr.textFont ).styleName() ), 4 );
03877     QgsDebugMsgLevel( QString( "PAL font definedFont: %1, Style: %2" ).arg( dFont.toString() ).arg( dFont.styleName() ), 4 );
03878 #endif
03879     tmpLyr.textFont = dFont;
03880 
03881     // update tmpLyr with any data defined text style values
03882     dataDefinedTextStyle( tmpLyr, ddValues );
03883 
03884     // update tmpLyr with any data defined text buffer values
03885     dataDefinedTextBuffer( tmpLyr, ddValues );
03886 
03887     // update tmpLyr with any data defined text formatting values
03888     dataDefinedTextFormatting( tmpLyr, ddValues );
03889 
03890     // update tmpLyr with any data defined shape background values
03891     dataDefinedShapeBackground( tmpLyr, ddValues );
03892 
03893     // update tmpLyr with any data defined drop shadow values
03894     dataDefinedDropShadow( tmpLyr, ddValues );
03895 
03896 
03897     tmpLyr.showingShadowRects = mShowingShadowRects;
03898 
03899     // Render the components of a label in reverse order
03900     //   (backgrounds -> text)
03901 
03902     if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowLowest )
03903     {
03904       if ( tmpLyr.shapeDraw )
03905       {
03906         tmpLyr.shadowUnder = QgsPalLayerSettings::ShadowShape;
03907       }
03908       else if ( tmpLyr.bufferDraw )
03909       {
03910         tmpLyr.shadowUnder = QgsPalLayerSettings::ShadowBuffer;
03911       }
03912       else
03913       {
03914         tmpLyr.shadowUnder = QgsPalLayerSettings::ShadowText;
03915       }
03916     }
03917 
03918     if ( tmpLyr.shapeDraw )
03919     {
03920       drawLabel( *it, context, tmpLyr, LabelShape );
03921     }
03922 
03923     if ( tmpLyr.bufferDraw )
03924     {
03925       drawLabel( *it, context, tmpLyr, LabelBuffer );
03926     }
03927 
03928     drawLabel( *it, context, tmpLyr, LabelText );
03929 
03930     if ( mLabelSearchTree )
03931     {
03932       QString labeltext = (( QgsPalGeometry* )( *it )->getFeaturePart()->getUserGeometry() )->text();
03933       mLabelSearchTree->insertLabel( *it,  QString( palGeometry->strId() ).toInt(), ( *it )->getLayerName(), labeltext, dFont, false, palGeometry->isPinned() );
03934     }
03935   }
03936 
03937   // Reset composition mode for further drawing operations
03938   painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
03939 
03940   QgsDebugMsgLevel( QString( "LABELING draw:  %1 ms" ).arg( t.elapsed() ), 4 );
03941 
03942   delete problem;
03943   delete labels;
03944 
03945   // delete all allocated geometries for features
03946   QHash<QgsVectorLayer*, QgsPalLayerSettings>::iterator lit;
03947   for ( lit = mActiveLayers.begin(); lit != mActiveLayers.end(); ++lit )
03948   {
03949     QgsPalLayerSettings& lyr = lit.value();
03950     for ( QList<QgsPalGeometry*>::iterator git = lyr.geometries.begin(); git != lyr.geometries.end(); ++git )
03951       delete *git;
03952     if ( lyr.limitNumLabels )
03953     {
03954       QgsDebugMsgLevel( QString( "mFeaturesToLabel: %1" ).arg( lyr.mFeaturesToLabel ), 4 );
03955       QgsDebugMsgLevel( QString( "maxNumLabels: %1" ).arg( lyr.maxNumLabels ), 4 );
03956       QgsDebugMsgLevel( QString( "mFeatsSendingToPal: %1" ).arg( lyr.mFeatsSendingToPal ), 4 );
03957       QgsDebugMsgLevel( QString( "mFeatsRegPal: %1" ).arg( lyr.geometries.count() ), 4 );
03958     }
03959     lyr.geometries.clear();
03960   }
03961 
03962   //delete all allocated geometries for diagrams
03963   QHash<QgsVectorLayer*, QgsDiagramLayerSettings>::iterator dIt = mActiveDiagramLayers.begin();
03964   for ( ; dIt != mActiveDiagramLayers.end(); ++dIt )
03965   {
03966     QgsDiagramLayerSettings& dls = dIt.value();
03967     for ( QList<QgsPalGeometry*>::iterator git = dls.geometries.begin(); git != dls.geometries.end(); ++git )
03968     {
03969       delete *git;
03970     }
03971     dls.geometries.clear();
03972   }
03973 }
03974 
03975 QList<QgsLabelPosition> QgsPalLabeling::labelsAtPosition( const QgsPoint& p )
03976 {
03977   QList<QgsLabelPosition> positions;
03978 
03979   QList<QgsLabelPosition*> positionPointers;
03980   if ( mLabelSearchTree )
03981   {
03982     mLabelSearchTree->label( p, positionPointers );
03983     QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
03984     for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
03985     {
03986       positions.push_back( QgsLabelPosition( **pointerIt ) );
03987     }
03988   }
03989 
03990   return positions;
03991 }
03992 
03993 QList<QgsLabelPosition> QgsPalLabeling::labelsWithinRect( const QgsRectangle& r )
03994 {
03995   QList<QgsLabelPosition> positions;
03996 
03997   QList<QgsLabelPosition*> positionPointers;
03998   if ( mLabelSearchTree )
03999   {
04000     mLabelSearchTree->labelsInRect( r, positionPointers );
04001     QList<QgsLabelPosition*>::const_iterator pointerIt = positionPointers.constBegin();
04002     for ( ; pointerIt != positionPointers.constEnd(); ++pointerIt )
04003     {
04004       positions.push_back( QgsLabelPosition( **pointerIt ) );
04005     }
04006   }
04007 
04008   return positions;
04009 }
04010 
04011 void QgsPalLabeling::numCandidatePositions( int& candPoint, int& candLine, int& candPolygon )
04012 {
04013   candPoint = mCandPoint;
04014   candLine = mCandLine;
04015   candPolygon = mCandPolygon;
04016 }
04017 
04018 void QgsPalLabeling::setNumCandidatePositions( int candPoint, int candLine, int candPolygon )
04019 {
04020   mCandPoint = candPoint;
04021   mCandLine = candLine;
04022   mCandPolygon = candPolygon;
04023 }
04024 
04025 void QgsPalLabeling::setSearchMethod( QgsPalLabeling::Search s )
04026 {
04027   mSearch = s;
04028 }
04029 
04030 QgsPalLabeling::Search QgsPalLabeling::searchMethod() const
04031 {
04032   return mSearch;
04033 }
04034 
04035 void QgsPalLabeling::drawLabelCandidateRect( pal::LabelPosition* lp, QPainter* painter, const QgsMapToPixel* xform )
04036 {
04037   QgsPoint outPt = xform->transform( lp->getX(), lp->getY() );
04038   QgsPoint outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
04039 
04040   painter->save();
04041   painter->translate( QPointF( outPt.x(), outPt.y() ) );
04042   painter->rotate( -lp->getAlpha() * 180 / M_PI );
04043   QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
04044   painter->drawRect( rect );
04045   painter->restore();
04046 
04047   // save the rect
04048   rect.moveTo( outPt.x(), outPt.y() );
04049   mCandidates.append( QgsLabelCandidate( rect, lp->getCost() * 1000 ) );
04050 
04051   // show all parts of the multipart label
04052   if ( lp->getNextPart() )
04053     drawLabelCandidateRect( lp->getNextPart(), painter, xform );
04054 }
04055 
04056 void QgsPalLabeling::drawLabel( pal::LabelPosition* label, QgsRenderContext& context, QgsPalLayerSettings& tmpLyr, DrawLabelType drawType )
04057 {
04058   // NOTE: this is repeatedly called for multi-part labels
04059   QPainter* painter = context.painter();
04060   const QgsMapToPixel* xform = &context.mapToPixel();
04061 
04062   QgsLabelComponent component;
04063 
04064   // account for print output or image saving @ specific dpi
04065   if ( !qgsDoubleNear( context.rasterScaleFactor(), 1.0, 0.1 ) )
04066   {
04067     // find relative dpi scaling for local painter
04068     QPicture localPict;
04069     QPainter localp;
04070     localp.begin( &localPict );
04071 
04072     double localdpi = ( localp.device()->logicalDpiX() + localp.device()->logicalDpiY() ) / 2;
04073     double contextdpi = ( painter->device()->logicalDpiX() + painter->device()->logicalDpiY() ) / 2;
04074     component.setDpiRatio( localdpi / contextdpi );
04075 
04076     localp.end();
04077   }
04078 
04079   QgsPoint outPt = xform->transform( label->getX(), label->getY() );
04080 //  QgsPoint outPt2 = xform->transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() );
04081 //  QRectF labelRect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
04082 
04083   component.setOrigin( outPt );
04084   component.setRotation( label->getAlpha() );
04085 
04086   if ( drawType == QgsPalLabeling::LabelShape )
04087   {
04088     // get rotated label's center point
04089     QgsPoint centerPt( outPt );
04090     QgsPoint outPt2 = xform->transform( label->getX() + label->getWidth() / 2,
04091                                         label->getY() + label->getHeight() / 2 );
04092 
04093     double xc = outPt2.x() - outPt.x();
04094     double yc = outPt2.y() - outPt.y();
04095 
04096     double angle = -label->getAlpha();
04097     double xd = xc * cos( angle ) - yc * sin( angle );
04098     double yd = xc * sin( angle ) + yc * cos( angle );
04099 
04100     centerPt.setX( centerPt.x() + xd );
04101     centerPt.setY( centerPt.y() + yd );
04102 
04103     component.setCenter( centerPt );
04104     component.setSize( QgsPoint( label->getWidth(), label->getHeight() ) );
04105 
04106     drawLabelBackground( context, component, tmpLyr );
04107   }
04108 
04109   if ( drawType == QgsPalLabeling::LabelBuffer
04110        || drawType == QgsPalLabeling::LabelText )
04111   {
04112 
04113     // TODO: optimize access :)
04114     QString text = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->text();
04115     QString txt = ( label->getPartId() == -1 ? text : QString( text[label->getPartId()] ) );
04116     QFontMetricsF* labelfm = (( QgsPalGeometry* )label->getFeaturePart()->getUserGeometry() )->getLabelFontMetrics();
04117 
04118     QString wrapchr = !tmpLyr.wrapChar.isEmpty() ? tmpLyr.wrapChar : QString( "\n" );
04119 
04120     //add the direction symbol if needed
04121     if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line &&
04122          tmpLyr.addDirectionSymbol )
04123     {
04124       bool prependSymb = false;
04125       QString symb = tmpLyr.rightDirectionSymbol;
04126 
04127       if ( label->getReversed() )
04128       {
04129         prependSymb = true;
04130         symb = tmpLyr.leftDirectionSymbol;
04131       }
04132 
04133       if ( tmpLyr.reverseDirectionSymbol )
04134       {
04135         if ( symb == tmpLyr.rightDirectionSymbol )
04136         {
04137           prependSymb = true;
04138           symb = tmpLyr.leftDirectionSymbol;
04139         }
04140         else
04141         {
04142           prependSymb = false;
04143           symb = tmpLyr.rightDirectionSymbol;
04144         }
04145       }
04146 
04147       if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolAbove )
04148       {
04149         prependSymb = true;
04150         symb = symb + wrapchr;
04151       }
04152       else if ( tmpLyr.placeDirectionSymbol == QgsPalLayerSettings::SymbolBelow )
04153       {
04154         prependSymb = false;
04155         symb = wrapchr + symb;
04156       }
04157 
04158       if ( prependSymb )
04159       {
04160         txt.prepend( symb );
04161       }
04162       else
04163       {
04164         txt.append( symb );
04165       }
04166     }
04167 
04168     //QgsDebugMsgLevel( "drawLabel " + txt, 4 );
04169 
04170     QStringList multiLineList = txt.split( wrapchr );
04171     int lines = multiLineList.size();
04172 
04173     double labelWidest = 0.0;
04174     for ( int i = 0; i < lines; ++i )
04175     {
04176       double labelWidth = labelfm->width( multiLineList.at( i ) );
04177       if ( labelWidth > labelWidest )
04178       {
04179         labelWidest = labelWidth;
04180       }
04181     }
04182 
04183     double labelHeight = labelfm->ascent() + labelfm->descent(); // ignore +1 for baseline
04184     //  double labelHighest = labelfm->height() + ( double )(( lines - 1 ) * labelHeight * tmpLyr.multilineHeight );
04185 
04186     // needed to move bottom of text's descender to within bottom edge of label
04187     double ascentOffset = 0.25 * labelfm->ascent(); // labelfm->descent() is not enough
04188 
04189     for ( int i = 0; i < lines; ++i )
04190     {
04191       painter->save();
04192       painter->translate( QPointF( outPt.x(), outPt.y() ) );
04193       painter->rotate( -label->getAlpha() * 180 / M_PI );
04194 
04195       // scale down painter: the font size has been multiplied by raster scale factor
04196       // to workaround a Qt font scaling bug with small font sizes
04197       painter->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
04198 
04199       // figure x offset for horizontal alignment of multiple lines
04200       double xMultiLineOffset = 0.0;
04201       double labelWidth = labelfm->width( multiLineList.at( i ) );
04202       if ( lines > 1 && tmpLyr.multilineAlign != QgsPalLayerSettings::MultiLeft )
04203       {
04204         double labelWidthDiff = labelWidest - labelWidth;
04205         if ( tmpLyr.multilineAlign == QgsPalLayerSettings::MultiCenter )
04206         {
04207           labelWidthDiff /= 2;
04208         }
04209         xMultiLineOffset = labelWidthDiff;
04210         //QgsDebugMsgLevel( QString( "xMultiLineOffset: %1" ).arg( xMultiLineOffset ), 4 );
04211       }
04212 
04213       double yMultiLineOffset = ( lines - 1 - i ) * labelHeight * tmpLyr.multilineHeight;
04214       painter->translate( QPointF( xMultiLineOffset, - ascentOffset - yMultiLineOffset ) );
04215 
04216       component.setText( multiLineList.at( i ) );
04217       component.setSize( QgsPoint( labelWidth, labelHeight ) );
04218       component.setOffset( QgsPoint( 0.0, -ascentOffset ) );
04219       component.setRotation( -component.rotation() * 180 / M_PI );
04220       component.setRotationOffset( 0.0 );
04221 
04222       if ( drawType == QgsPalLabeling::LabelBuffer )
04223       {
04224         // draw label's buffer
04225         drawLabelBuffer( context, component, tmpLyr );
04226       }
04227       else
04228       {
04229         // draw label's text, QPainterPath method
04230         QPainterPath path;
04231         path.addText( 0, 0, tmpLyr.textFont, component.text() );
04232 
04233         // store text's drawing in QPicture for drop shadow call
04234         QPicture textPict;
04235         QPainter textp;
04236         textp.begin( &textPict );
04237         textp.setPen( Qt::NoPen );
04238         textp.setBrush( tmpLyr.textColor );
04239         textp.drawPath( path );
04240         textp.end();
04241 
04242         if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowText )
04243         {
04244           component.setPicture( &textPict );
04245           component.setPictureBuffer( 0.0 ); // no pen width to deal with
04246           component.setOrigin( QgsPoint( 0.0, 0.0 ) );
04247 
04248           drawLabelShadow( context, component, tmpLyr );
04249         }
04250 
04251         // paint the text
04252         if ( context.useAdvancedEffects() )
04253         {
04254           painter->setCompositionMode( tmpLyr.blendMode );
04255         }
04256 //        painter->setPen( Qt::NoPen );
04257 //        painter->setBrush( tmpLyr.textColor );
04258 //        painter->drawPath( path );
04259 
04260         // scale for any print output or image saving @ specific dpi
04261         painter->scale( component.dpiRatio(), component.dpiRatio() );
04262         painter->drawPicture( 0, 0, textPict );
04263 
04264         // regular text draw, for testing optimization
04265 //        painter->setFont( tmpLyr.textFont );
04266 //        painter->setPen( tmpLyr.textColor );
04267 //        painter->drawText( 0, 0, multiLineList.at( i ) );
04268 
04269       }
04270       painter->restore();
04271     }
04272   }
04273 
04274   // NOTE: this used to be within above multi-line loop block, at end. (a mistake since 2010? [LS])
04275   if ( label->getNextPart() )
04276     drawLabel( label->getNextPart(), context, tmpLyr, drawType );
04277 }
04278 
04279 void QgsPalLabeling::drawLabelBuffer( QgsRenderContext& context,
04280                                       QgsLabelComponent component,
04281                                       const QgsPalLayerSettings& tmpLyr )
04282 {
04283   QPainter* p = context.painter();
04284 
04285   double penSize = tmpLyr.scaleToPixelContext( tmpLyr.bufferSize, context,
04286                    ( tmpLyr.bufferSizeInMapUnits ? QgsPalLayerSettings::MapUnits : QgsPalLayerSettings::MM ), true );
04287 
04288   QPainterPath path;
04289   path.addText( 0, 0, tmpLyr.textFont, component.text() );
04290   QPen pen( tmpLyr.bufferColor );
04291   pen.setWidthF( penSize );
04292   pen.setJoinStyle( tmpLyr.bufferJoinStyle );
04293   QColor tmpColor( tmpLyr.bufferColor );
04294   // honor pref for whether to fill buffer interior
04295   if ( tmpLyr.bufferNoFill )
04296   {
04297     tmpColor.setAlpha( 0 );
04298   }
04299 
04300   // store buffer's drawing in QPicture for drop shadow call
04301   QPicture buffPict;
04302   QPainter buffp;
04303   buffp.begin( &buffPict );
04304   buffp.setPen( pen );
04305   buffp.setBrush( tmpColor );
04306   buffp.drawPath( path );
04307   buffp.end();
04308 
04309   if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowBuffer )
04310   {
04311     component.setOrigin( QgsPoint( 0.0, 0.0 ) );
04312     component.setPicture( &buffPict );
04313     component.setPictureBuffer( penSize / 2.0 );
04314 
04315     drawLabelShadow( context, component, tmpLyr );
04316   }
04317 
04318   p->save();
04319   if ( context.useAdvancedEffects() )
04320   {
04321     p->setCompositionMode( tmpLyr.bufferBlendMode );
04322   }
04323 //  p->setPen( pen );
04324 //  p->setBrush( tmpColor );
04325 //  p->drawPath( path );
04326 
04327   // scale for any print output or image saving @ specific dpi
04328   p->scale( component.dpiRatio(), component.dpiRatio() );
04329   p->drawPicture( 0, 0, buffPict );
04330   p->restore();
04331 }
04332 
04333 void QgsPalLabeling::drawLabelBackground( QgsRenderContext& context,
04334     QgsLabelComponent component,
04335     const QgsPalLayerSettings& tmpLyr )
04336 {
04337   QPainter* p = context.painter();
04338   double labelWidth = component.size().x(), labelHeight = component.size().y();
04339   //QgsDebugMsgLevel( QString( "Background label rotation: %1" ).arg( component.rotation() ), 4 );
04340 
04341   // shared calculations between shapes and SVG
04342 
04343   // configure angles, set component rotation and rotationOffset
04344   if ( tmpLyr.shapeRotationType != QgsPalLayerSettings::RotationFixed )
04345   {
04346     component.setRotation( -( component.rotation() * 180 / M_PI ) ); // RotationSync
04347     component.setRotationOffset(
04348       tmpLyr.shapeRotationType == QgsPalLayerSettings::RotationOffset ? tmpLyr.shapeRotation : 0.0 );
04349   }
04350   else // RotationFixed
04351   {
04352     component.setRotation( 0.0 ); // don't use label's rotation
04353     component.setRotationOffset( tmpLyr.shapeRotation );
04354   }
04355 
04356   // mm to map units conversion factor
04357   double mmToMapUnits = context.mapToPixel().mapUnitsPerPixel() * context.scaleFactor();
04358 
04359   // TODO: the following label-buffered generated shapes and SVG symbols should be moved into marker symbology classes
04360 
04361   if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeSVG )
04362   {
04363     // all calculations done in shapeSizeUnits, which are then passed to symbology class for painting
04364 
04365     if ( tmpLyr.shapeSVGFile.isEmpty() )
04366       return;
04367 
04368     double sizeOut = 0.0;
04369     // only one size used for SVG sizing/scaling (no use of shapeSize.y() or Y field in gui)
04370     if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeFixed )
04371     {
04372       sizeOut = tmpLyr.shapeSize.x();
04373     }
04374     else if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeBuffer )
04375     {
04376       // add buffer to greatest dimension of label
04377       if ( labelWidth >= labelHeight )
04378         sizeOut = labelWidth;
04379       else if ( labelHeight > labelWidth )
04380         sizeOut = labelHeight;
04381 
04382       // label size in map units, convert to shapeSizeUnits, if different
04383       if ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM )
04384       {
04385         sizeOut /= mmToMapUnits;
04386       }
04387 
04388       // add buffer
04389       sizeOut += tmpLyr.shapeSize.x() * 2;
04390     }
04391 
04392     // don't bother rendering symbols smaller than 1x1 pixels in size
04393     // TODO: add option to not show any svgs under/over a certian size
04394     if ( tmpLyr.scaleToPixelContext( sizeOut, context, tmpLyr.shapeSizeUnits ) < 1.0 )
04395       return;
04396 
04397     QgsStringMap map; // for SVG symbology marker
04398     map["name"] = QgsSymbolLayerV2Utils::symbolNameToPath( tmpLyr.shapeSVGFile.trimmed() );
04399     map["size"] = QString::number( sizeOut );
04400     map["size_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
04401                          tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
04402     map["angle"] = QString::number( 0.0 ); // angle is handled by this local painter
04403 
04404     // offset is handled by this local painter
04405     // TODO: see why the marker renderer doesn't seem to translate offset *after* applying rotation
04406     //map["offset"] = QgsSymbolLayerV2Utils::encodePoint( tmpLyr.shapeOffset );
04407     //map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
04408     //                       tmpLyr.shapeOffsetUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
04409 
04410     map["fill"] = tmpLyr.shapeFillColor.name();
04411     map["outline"] = tmpLyr.shapeBorderColor.name();
04412     map["outline-width"] = QString::number( tmpLyr.shapeBorderWidth );
04413 
04414     // TODO: fix overriding SVG symbol's border width/units in QgsSvgCache
04415     // currently broken, fall back to symbol's
04416     //map["outline_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit(
04417     //                              tmpLyr.shapeBorderWidthUnits == QgsPalLayerSettings::MapUnits ? QgsSymbolV2::MapUnit : QgsSymbolV2::MM );
04418 
04419     if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowShape )
04420     {
04421       // configure SVG shadow specs
04422       QgsStringMap shdwmap( map );
04423       shdwmap["fill"] = tmpLyr.shadowColor.name();
04424       shdwmap["outline"] = tmpLyr.shadowColor.name();
04425       shdwmap["size"] = QString::number( sizeOut * tmpLyr.rasterCompressFactor );
04426 
04427       // store SVG's drawing in QPicture for drop shadow call
04428       QPicture svgPict;
04429       QPainter svgp;
04430       svgp.begin( &svgPict );
04431 
04432       // draw shadow symbol
04433 
04434       // clone current render context map unit/mm conversion factors, but not
04435       // other map canvas parameters, then substitute this painter for use in symbology painting
04436       // NOTE: this is because the shadow needs to be scaled correctly for output to map canvas,
04437       //       but will be created relative to the SVG's computed size, not the current map canvas
04438       QgsRenderContext shdwContext;
04439       shdwContext.setMapToPixel( context.mapToPixel() );
04440       shdwContext.setScaleFactor( context.scaleFactor() );
04441       shdwContext.setPainter( &svgp );
04442 
04443       QgsSymbolLayerV2* symShdwL = QgsSvgMarkerSymbolLayerV2::create( shdwmap );
04444       QgsSvgMarkerSymbolLayerV2* svgShdwM = static_cast<QgsSvgMarkerSymbolLayerV2*>( symShdwL );
04445       QgsSymbolV2RenderContext svgShdwContext( shdwContext, QgsSymbolV2::Mixed,
04446           ( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
04447 
04448       double svgSize = tmpLyr.scaleToPixelContext( sizeOut, context, tmpLyr.shapeSizeUnits, true );
04449       svgShdwM->renderPoint( QPointF( svgSize / 2, -svgSize / 2 ), svgShdwContext );
04450       svgp.end();
04451 
04452       component.setPicture( &svgPict );
04453       // TODO: when SVG symbol's border width/units is fixed in QgsSvgCache, adjust for it here
04454       component.setPictureBuffer( 0.0 );
04455 
04456       component.setSize( QgsPoint( svgSize, svgSize ) );
04457       component.setOffset( QgsPoint( 0.0, 0.0 ) );
04458 
04459       // rotate about origin center of SVG
04460       p->save();
04461       p->translate( component.center().x(), component.center().y() );
04462       p->rotate( component.rotation() );
04463       p->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
04464       double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits, true );
04465       double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits, true );
04466       p->translate( QPointF( xoff, yoff ) );
04467       p->rotate( component.rotationOffset() );
04468       p->translate( -svgSize / 2, svgSize / 2 );
04469 
04470       drawLabelShadow( context, component, tmpLyr );
04471       p->restore();
04472 
04473       delete svgShdwM;
04474       svgShdwM = 0;
04475     }
04476 
04477     // draw the actual symbol
04478     QgsSymbolLayerV2* symL = QgsSvgMarkerSymbolLayerV2::create( map );
04479     QgsSvgMarkerSymbolLayerV2* svgM = static_cast<QgsSvgMarkerSymbolLayerV2*>( symL );
04480     QgsSymbolV2RenderContext svgContext( context, QgsSymbolV2::Mixed,
04481                                          ( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
04482 
04483     p->save();
04484     if ( context.useAdvancedEffects() )
04485     {
04486       p->setCompositionMode( tmpLyr.shapeBlendMode );
04487     }
04488     p->translate( component.center().x(), component.center().y() );
04489     p->rotate( component.rotation() );
04490     double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits );
04491     double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits );
04492     p->translate( QPointF( xoff, yoff ) );
04493     p->rotate( component.rotationOffset() );
04494     svgM->renderPoint( QPointF( 0, 0 ), svgContext );
04495     p->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
04496     p->restore();
04497 
04498     delete svgM;
04499     svgM = 0;
04500 
04501   }
04502   else  // Generated Shapes
04503   {
04504     // all calculations done in shapeSizeUnits
04505 
04506     double w = labelWidth / ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM ? mmToMapUnits : 1 );
04507     double h = labelHeight / ( tmpLyr.shapeSizeUnits == QgsPalLayerSettings::MM ? mmToMapUnits : 1 );
04508 
04509     double xsize = tmpLyr.shapeSize.x();
04510     double ysize = tmpLyr.shapeSize.y();
04511 
04512     if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeFixed )
04513     {
04514       w = xsize;
04515       h = ysize;
04516     }
04517     else if ( tmpLyr.shapeSizeType == QgsPalLayerSettings::SizeBuffer )
04518     {
04519       if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeSquare )
04520       {
04521         if ( w > h )
04522           h = w;
04523         else if ( h > w )
04524           w = h;
04525       }
04526       else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeCircle )
04527       {
04528         // start with label bound by circle
04529         h = sqrt( pow( w, 2 ) + pow( h, 2 ) );
04530         w = h;
04531       }
04532       else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeEllipse )
04533       {
04534         // start with label bound by ellipse
04535         h = h / sqrt( 2.0 ) * 2;
04536         w = w / sqrt( 2.0 ) * 2;
04537       }
04538 
04539       w += xsize * 2;
04540       h += ysize * 2;
04541     }
04542 
04543     // convert everything over to map pixels from here on
04544     w = tmpLyr.scaleToPixelContext( w, context, tmpLyr.shapeSizeUnits, true );
04545     h = tmpLyr.scaleToPixelContext( h, context, tmpLyr.shapeSizeUnits, true );
04546 
04547     // offsets match those of symbology: -x = left, -y = up
04548     QRectF rect( -w / 2.0, - h / 2.0, w, h );
04549 
04550     if ( rect.isNull() )
04551       return;
04552 
04553     p->save();
04554     p->translate( QPointF( component.center().x(), component.center().y() ) );
04555     p->rotate( component.rotation() );
04556     double xoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.x(), context, tmpLyr.shapeOffsetUnits, true );
04557     double yoff = tmpLyr.scaleToPixelContext( tmpLyr.shapeOffset.y(), context, tmpLyr.shapeOffsetUnits, true );
04558     p->translate( QPointF( xoff, yoff ) );
04559     p->rotate( component.rotationOffset() );
04560 
04561     double penSize = tmpLyr.scaleToPixelContext( tmpLyr.shapeBorderWidth, context, tmpLyr.shapeBorderWidthUnits, true );
04562 
04563     QPen pen;
04564     if ( tmpLyr.shapeBorderWidth > 0 )
04565     {
04566       pen.setColor( tmpLyr.shapeBorderColor );
04567       pen.setWidthF( penSize );
04568       if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeRectangle )
04569         pen.setJoinStyle( tmpLyr.shapeJoinStyle );
04570     }
04571     else
04572     {
04573       pen = Qt::NoPen;
04574     }
04575 
04576     // store painting in QPicture for shadow drawing
04577     QPicture shapePict;
04578     QPainter shapep;
04579     shapep.begin( &shapePict );
04580     shapep.setPen( pen );
04581     shapep.setBrush( tmpLyr.shapeFillColor );
04582 
04583     if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeRectangle
04584          || tmpLyr.shapeType == QgsPalLayerSettings::ShapeSquare )
04585     {
04586       if ( tmpLyr.shapeRadiiUnits == QgsPalLayerSettings::Percent )
04587       {
04588         shapep.drawRoundedRect( rect, tmpLyr.shapeRadii.x(), tmpLyr.shapeRadii.y(), Qt::RelativeSize );
04589       }
04590       else
04591       {
04592         double xRadius = tmpLyr.scaleToPixelContext( tmpLyr.shapeRadii.x(), context, tmpLyr.shapeRadiiUnits, true );
04593         double yRadius = tmpLyr.scaleToPixelContext( tmpLyr.shapeRadii.y(), context, tmpLyr.shapeRadiiUnits, true );
04594         shapep.drawRoundedRect( rect, xRadius, yRadius );
04595       }
04596     }
04597     else if ( tmpLyr.shapeType == QgsPalLayerSettings::ShapeEllipse
04598               || tmpLyr.shapeType == QgsPalLayerSettings::ShapeCircle )
04599     {
04600       shapep.drawEllipse( rect );
04601     }
04602     shapep.end();
04603 
04604     p->scale( 1.0 / tmpLyr.rasterCompressFactor, 1.0 / tmpLyr.rasterCompressFactor );
04605 
04606     if ( tmpLyr.shadowDraw && tmpLyr.shadowUnder == QgsPalLayerSettings::ShadowShape )
04607     {
04608       component.setPicture( &shapePict );
04609       component.setPictureBuffer( penSize / 2.0 );
04610 
04611       component.setSize( QgsPoint( rect.width(), rect.height() ) );
04612       component.setOffset( QgsPoint( rect.width() / 2, -rect.height() / 2 ) );
04613       drawLabelShadow( context, component, tmpLyr );
04614     }
04615 
04616     p->setOpacity(( 100.0 - ( double )( tmpLyr.shapeTransparency ) ) / 100.0 );
04617     if ( context.useAdvancedEffects() )
04618     {
04619       p->setCompositionMode( tmpLyr.shapeBlendMode );
04620     }
04621 
04622     // scale for any print output or image saving @ specific dpi
04623     p->scale( component.dpiRatio(), component.dpiRatio() );
04624     p->drawPicture( 0, 0, shapePict );
04625     p->restore();
04626   }
04627 }
04628 
04629 void QgsPalLabeling::drawLabelShadow( QgsRenderContext& context,
04630                                       QgsLabelComponent component,
04631                                       const QgsPalLayerSettings& tmpLyr )
04632 {
04633   // incoming component sizes should be multiplied by rasterCompressFactor, as
04634   // this allows shadows to be created at paint device dpi (e.g. high resolution),
04635   // then scale device painter by 1.0 / rasterCompressFactor for output
04636 
04637   QPainter* p = context.painter();
04638   double componentWidth = component.size().x(), componentHeight = component.size().y();
04639   double xOffset = component.offset().x(), yOffset = component.offset().y();
04640   double pictbuffer = component.pictureBuffer();
04641 
04642   // generate pixmap representation of label component drawing
04643   bool mapUnits = ( tmpLyr.shadowRadiusUnits == QgsPalLayerSettings::MapUnits );
04644   double radius = tmpLyr.scaleToPixelContext( tmpLyr.shadowRadius , context, tmpLyr.shadowRadiusUnits, !mapUnits );
04645   radius /= ( mapUnits ? tmpLyr.vectorScaleFactor / component.dpiRatio() : 1 );
04646   radius = ( int )( radius + 0.5 );
04647 
04648   // TODO: add labeling gui option to adjust blurBufferClippingScale to minimize pixels, or
04649   //       to ensure shadow isn't clipped too tight. (Or, find a better method of buffering)
04650   double blurBufferClippingScale = 3.75;
04651   int blurbuffer = ( radius > 17 ? 16 : radius ) * blurBufferClippingScale;
04652 
04653   QImage blurImg( componentWidth + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
04654                   componentHeight + ( pictbuffer * 2.0 ) + ( blurbuffer * 2.0 ),
04655                   QImage::Format_ARGB32_Premultiplied );
04656 
04657   // TODO: add labeling gui option to not show any shadows under/over a certian size
04658   // keep very small QImages from causing paint device issues, i.e. must be at least > 1
04659   int minBlurImgSize = 1;
04660   // max limitation on QgsSvgCache is 10,000 for screen, which will probably be reasonable for future caching here, too
04661   // 4 x QgsSvgCache limit for output to print/image at higher dpi
04662   // TODO: should it be higher, scale with dpi, or have no limit? Needs testing with very large labels rendered at high dpi output
04663   int maxBlurImgSize = 40000;
04664   if ( blurImg.isNull()
04665        || ( blurImg.width() < minBlurImgSize || blurImg.height() < minBlurImgSize )
04666        || ( blurImg.width() > maxBlurImgSize || blurImg.height() > maxBlurImgSize ) )
04667     return;
04668 
04669   blurImg.fill( QColor( Qt::transparent ).rgba() );
04670   QPainter pictp;
04671   if ( !pictp.begin( &blurImg ) )
04672     return;
04673   pictp.setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
04674   QPointF imgOffset( blurbuffer + pictbuffer + xOffset,
04675                      blurbuffer + pictbuffer + componentHeight + yOffset );
04676 
04677   pictp.drawPicture( imgOffset,
04678                      *component.picture() );
04679 
04680   // overlay shadow color
04681   pictp.setCompositionMode( QPainter::CompositionMode_SourceIn );
04682   pictp.fillRect( blurImg.rect(), tmpLyr.shadowColor );
04683   pictp.end();
04684 
04685   // blur the QImage in-place
04686   if ( tmpLyr.shadowRadius > 0.0 && radius > 0 )
04687   {
04688     QgsSymbolLayerV2Utils::blurImageInPlace( blurImg, blurImg.rect(), radius, tmpLyr.shadowRadiusAlphaOnly );
04689   }
04690 
04691   if ( tmpLyr.showingShadowRects ) // engine setting, not per layer
04692   {
04693     // debug rect for QImage shadow registration and clipping visualization
04694     QPainter picti;
04695     picti.begin( &blurImg );
04696     picti.setBrush( Qt::Dense7Pattern );
04697     QPen imgPen( QColor( 0, 0, 255, 255 ) );
04698     imgPen.setWidth( 1 );
04699     picti.setPen( imgPen );
04700     picti.setOpacity( 0.1 );
04701     picti.drawRect( 0, 0, blurImg.width(), blurImg.height() );
04702     picti.end();
04703   }
04704 
04705   double offsetDist = tmpLyr.scaleToPixelContext( tmpLyr.shadowOffsetDist, context, tmpLyr.shadowOffsetUnits, true );
04706   double angleRad = tmpLyr.shadowOffsetAngle * M_PI / 180; // to radians
04707   if ( tmpLyr.shadowOffsetGlobal )
04708   {
04709     // TODO: check for differences in rotation origin and cw/ccw direction,
04710     //       when this shadow function is used for something other than labels
04711 
04712     // it's 0-->cw-->360 for labels
04713     //QgsDebugMsgLevel( QString( "Shadow aggregated label rotation (degrees): %1" ).arg( component.rotation() + component.rotationOffset() ), 4 );
04714     angleRad -= ( component.rotation() * M_PI / 180 + component.rotationOffset() * M_PI / 180 );
04715   }
04716 
04717   QPointF transPt( -offsetDist * cos( angleRad + M_PI / 2 ),
04718                    -offsetDist * sin( angleRad + M_PI / 2 ) );
04719 
04720   p->save();
04721   p->setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform );
04722   if ( context.useAdvancedEffects() )
04723   {
04724     p->setCompositionMode( tmpLyr.shadowBlendMode );
04725   }
04726   p->setOpacity(( 100.0 - ( double )( tmpLyr.shadowTransparency ) ) / 100.0 );
04727 
04728   double scale = ( double )tmpLyr.shadowScale / 100.0;
04729   // TODO: scale from center/center, left/center or left/top, instead of default left/bottom?
04730   p->scale( scale, scale );
04731   if ( component.useOrigin() )
04732   {
04733     p->translate( component.origin().x(), component.origin().y() );
04734   }
04735   p->translate( transPt );
04736   p->translate( -imgOffset.x(),
04737                 -imgOffset.y() );
04738   p->drawImage( 0, 0, blurImg );
04739   p->restore();
04740 
04741   // debug rects
04742   if ( tmpLyr.showingShadowRects ) // engine setting, not per layer
04743   {
04744     // draw debug rect for QImage painting registration
04745     p->save();
04746     p->setBrush( Qt::NoBrush );
04747     QPen imgPen( QColor( 255, 0, 0, 10 ) );
04748     imgPen.setWidth( 2 );
04749     imgPen.setStyle( Qt::DashLine );
04750     p->setPen( imgPen );
04751     p->scale( scale, scale );
04752     if ( component.useOrigin() )
04753     {
04754       p->translate( component.origin().x(), component.origin().y() );
04755     }
04756     p->translate( transPt );
04757     p->translate( -imgOffset.x(),
04758                   -imgOffset.y() );
04759     p->drawRect( 0, 0, blurImg.width(), blurImg.height() );
04760     p->restore();
04761 
04762     // draw debug rect for passed in component dimensions
04763     p->save();
04764     p->setBrush( Qt::NoBrush );
04765     QPen componentRectPen( QColor( 0, 255, 0, 70 ) );
04766     componentRectPen.setWidth( 1 );
04767     if ( component.useOrigin() )
04768     {
04769       p->translate( component.origin().x(), component.origin().y() );
04770     }
04771     p->setPen( componentRectPen );
04772     p->drawRect( QRect( -xOffset, -componentHeight - yOffset, componentWidth, componentHeight ) );
04773     p->restore();
04774   }
04775 }
04776 
04777 void QgsPalLabeling::loadEngineSettings()
04778 {
04779   // start with engine defaults for new project, or project that has no saved settings
04780   Pal p;
04781   bool saved = false;
04782   mSearch = ( QgsPalLabeling::Search )( QgsProject::instance()->readNumEntry(
04783                                           "PAL", "/SearchMethod", ( int )p.getSearch(), &saved ) );
04784   mCandPoint = QgsProject::instance()->readNumEntry(
04785                  "PAL", "/CandidatesPoint", p.getPointP(), &saved );
04786   mCandLine = QgsProject::instance()->readNumEntry(
04787                 "PAL", "/CandidatesLine", p.getLineP(), &saved );
04788   mCandPolygon = QgsProject::instance()->readNumEntry(
04789                    "PAL", "/CandidatesPolygon", p.getPolyP(), &saved );
04790   mShowingCandidates = QgsProject::instance()->readBoolEntry(
04791                          "PAL", "/ShowingCandidates", false, &saved );
04792   mShowingShadowRects = QgsProject::instance()->readBoolEntry(
04793                           "PAL", "/ShowingShadowRects", false, &saved );
04794   mShowingAllLabels = QgsProject::instance()->readBoolEntry(
04795                         "PAL", "/ShowingAllLabels", false, &saved );
04796   mSavedWithProject = saved;
04797 }
04798 
04799 void QgsPalLabeling::saveEngineSettings()
04800 {
04801   QgsProject::instance()->writeEntry( "PAL", "/SearchMethod", ( int )mSearch );
04802   QgsProject::instance()->writeEntry( "PAL", "/CandidatesPoint", mCandPoint );
04803   QgsProject::instance()->writeEntry( "PAL", "/CandidatesLine", mCandLine );
04804   QgsProject::instance()->writeEntry( "PAL", "/CandidatesPolygon", mCandPolygon );
04805   QgsProject::instance()->writeEntry( "PAL", "/ShowingCandidates", mShowingCandidates );
04806   QgsProject::instance()->writeEntry( "PAL", "/ShowingShadowRects", mShowingShadowRects );
04807   QgsProject::instance()->writeEntry( "PAL", "/ShowingAllLabels", mShowingAllLabels );
04808   mSavedWithProject = true;
04809 }
04810 
04811 void QgsPalLabeling::clearEngineSettings()
04812 {
04813   QgsProject::instance()->removeEntry( "PAL", "/SearchMethod" );
04814   QgsProject::instance()->removeEntry( "PAL", "/CandidatesPoint" );
04815   QgsProject::instance()->removeEntry( "PAL", "/CandidatesLine" );
04816   QgsProject::instance()->removeEntry( "PAL", "/CandidatesPolygon" );
04817   QgsProject::instance()->removeEntry( "PAL", "/ShowingCandidates" );
04818   QgsProject::instance()->removeEntry( "PAL", "/ShowingShadowRects" );
04819   QgsProject::instance()->removeEntry( "PAL", "/ShowingAllLabels" );
04820   mSavedWithProject = false;
04821 }
04822 
04823 QgsLabelingEngineInterface* QgsPalLabeling::clone()
04824 {
04825   QgsPalLabeling* lbl = new QgsPalLabeling();
04826   lbl->mShowingAllLabels = mShowingAllLabels;
04827   lbl->mShowingCandidates = mShowingCandidates;
04828   lbl->mShowingShadowRects = mShowingShadowRects;
04829   return lbl;
04830 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines