|
QGIS API Documentation
master-6164ace
|
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 }