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