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