QGIS API Documentation  master-3f58142
src/core/symbology-ng/qgslinesymbollayerv2.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgslinesymbollayerv2.cpp
00003     ---------------------
00004     begin                : November 2009
00005     copyright            : (C) 2009 by Martin Dobias
00006     email                : wonder dot sk at gmail dot com
00007  ***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 
00016 #include "qgslinesymbollayerv2.h"
00017 #include "qgssymbollayerv2utils.h"
00018 #include "qgsexpression.h"
00019 #include "qgsrendercontext.h"
00020 #include "qgslogger.h"
00021 #include "qgsvectorlayer.h"
00022 
00023 #include <QPainter>
00024 #include <QDomDocument>
00025 #include <QDomElement>
00026 
00027 #include <cmath>
00028 
00029 QgsSimpleLineSymbolLayerV2::QgsSimpleLineSymbolLayerV2( QColor color, double width, Qt::PenStyle penStyle )
00030     : mPenStyle( penStyle ), mPenJoinStyle( DEFAULT_SIMPLELINE_JOINSTYLE ), mPenCapStyle( DEFAULT_SIMPLELINE_CAPSTYLE ), mOffset( 0 ), mOffsetUnit( QgsSymbolV2::MM ),
00031     mUseCustomDashPattern( false ), mCustomDashPatternUnit( QgsSymbolV2::MM )
00032 {
00033   mColor = color;
00034   mWidth = width;
00035   mCustomDashVector << 5 << 2;
00036 }
00037 
00038 void QgsSimpleLineSymbolLayerV2::setOutputUnit( QgsSymbolV2::OutputUnit unit )
00039 {
00040   mWidthUnit = unit;
00041   mOffsetUnit = unit;
00042   mCustomDashPatternUnit = unit;
00043 }
00044 
00045 QgsSymbolV2::OutputUnit  QgsSimpleLineSymbolLayerV2::outputUnit() const
00046 {
00047   QgsSymbolV2::OutputUnit unit = mWidthUnit;
00048   if ( mOffsetUnit != unit || mCustomDashPatternUnit != unit )
00049   {
00050     return QgsSymbolV2::Mixed;
00051   }
00052   return unit;
00053 }
00054 
00055 
00056 QgsSymbolLayerV2* QgsSimpleLineSymbolLayerV2::create( const QgsStringMap& props )
00057 {
00058   QColor color = DEFAULT_SIMPLELINE_COLOR;
00059   double width = DEFAULT_SIMPLELINE_WIDTH;
00060   Qt::PenStyle penStyle = DEFAULT_SIMPLELINE_PENSTYLE;
00061 
00062   if ( props.contains( "color" ) )
00063     color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
00064   if ( props.contains( "width" ) )
00065     width = props["width"].toDouble();
00066   if ( props.contains( "penstyle" ) )
00067     penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["penstyle"] );
00068 
00069 
00070   QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
00071   if ( props.contains( "width_unit" ) )
00072     l->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["width_unit"] ) );
00073   if ( props.contains( "offset" ) )
00074     l->setOffset( props["offset"].toDouble() );
00075   if ( props.contains( "offset_unit" ) )
00076     l->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
00077   if ( props.contains( "joinstyle" ) )
00078     l->setPenJoinStyle( QgsSymbolLayerV2Utils::decodePenJoinStyle( props["joinstyle"] ) );
00079   if ( props.contains( "capstyle" ) )
00080     l->setPenCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( props["capstyle"] ) );
00081 
00082   if ( props.contains( "use_custom_dash" ) )
00083   {
00084     l->setUseCustomDashPattern( props["use_custom_dash"].toInt() );
00085   }
00086   if ( props.contains( "customdash" ) )
00087   {
00088     l->setCustomDashVector( QgsSymbolLayerV2Utils::decodeRealVector( props["customdash"] ) );
00089   }
00090   if ( props.contains( "customdash_unit" ) )
00091   {
00092     l->setCustomDashPatternUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["customdash_unit"] ) );
00093   }
00094 
00095   //data defined properties
00096   if ( props.contains( "color_expression" ) )
00097     l->setDataDefinedProperty( "color", props["color_expression"] );
00098   if ( props.contains( "width_expression" ) )
00099     l->setDataDefinedProperty( "width", props["width_expression"] );
00100   if ( props.contains( "offset_expression" ) )
00101     l->setDataDefinedProperty( "offset", props["offset_expression"] );
00102   if ( props.contains( "customdash_expression" ) )
00103     l->setDataDefinedProperty( "customdash", props["customdash_expression"] );
00104   if ( props.contains( "joinstyle_expression" ) )
00105     l->setDataDefinedProperty( "joinstyle", props["joinstyle_expression"] );
00106   if ( props.contains( "capstyle_expression" ) )
00107     l->setDataDefinedProperty( "capstyle", props["capstyle_expression"] );
00108 
00109   return l;
00110 }
00111 
00112 
00113 QString QgsSimpleLineSymbolLayerV2::layerType() const
00114 {
00115   return "SimpleLine";
00116 }
00117 
00118 void QgsSimpleLineSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context )
00119 {
00120   QColor penColor = mColor;
00121   penColor.setAlphaF( mColor.alphaF() * context.alpha() );
00122   mPen.setColor( penColor );
00123   double scaledWidth = mWidth * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mWidthUnit );
00124   mPen.setWidthF( scaledWidth );
00125   if ( mUseCustomDashPattern && scaledWidth != 0 )
00126   {
00127     mPen.setStyle( Qt::CustomDashLine );
00128 
00129     //scale pattern vector
00130     QVector<qreal> scaledVector;
00131     QVector<qreal>::const_iterator it = mCustomDashVector.constBegin();
00132     for ( ; it != mCustomDashVector.constEnd(); ++it )
00133     {
00134       //the dash is specified in terms of pen widths, therefore the division
00135       scaledVector << ( *it ) *  QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mCustomDashPatternUnit ) / scaledWidth;
00136     }
00137     mPen.setDashPattern( scaledVector );
00138   }
00139   else
00140   {
00141     mPen.setStyle( mPenStyle );
00142   }
00143   mPen.setJoinStyle( mPenJoinStyle );
00144   mPen.setCapStyle( mPenCapStyle );
00145 
00146   mSelPen = mPen;
00147   QColor selColor = context.renderContext().selectionColor();
00148   if ( ! selectionIsOpaque )
00149     selColor.setAlphaF( context.alpha() );
00150   mSelPen.setColor( selColor );
00151 
00152   //prepare expressions for data defined properties
00153   prepareExpressions( context.layer() );
00154 }
00155 
00156 void QgsSimpleLineSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
00157 {
00158   Q_UNUSED( context );
00159 }
00160 
00161 void QgsSimpleLineSymbolLayerV2::renderPolyline( const QPolygonF& points, QgsSymbolV2RenderContext& context )
00162 {
00163   QPainter* p = context.renderContext().painter();
00164   if ( !p )
00165   {
00166     return;
00167   }
00168 
00169   double offset = 0.0;
00170   applyDataDefinedSymbology( context, mPen, mSelPen, offset );
00171 
00172   p->setPen( context.selected() ? mSelPen : mPen );
00173 
00174   if ( offset == 0 )
00175   {
00176     p->drawPolyline( points );
00177   }
00178   else
00179   {
00180     double scaledOffset = offset * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit );
00181     p->drawPolyline( ::offsetLine( points, scaledOffset ) );
00182   }
00183 }
00184 
00185 QgsStringMap QgsSimpleLineSymbolLayerV2::properties() const
00186 {
00187   QgsStringMap map;
00188   map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
00189   map["width"] = QString::number( mWidth );
00190   map["width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mWidthUnit );
00191   map["penstyle"] = QgsSymbolLayerV2Utils::encodePenStyle( mPenStyle );
00192   map["joinstyle"] = QgsSymbolLayerV2Utils::encodePenJoinStyle( mPenJoinStyle );
00193   map["capstyle"] = QgsSymbolLayerV2Utils::encodePenCapStyle( mPenCapStyle );
00194   map["offset"] = QString::number( mOffset );
00195   map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
00196   map["use_custom_dash"] = ( mUseCustomDashPattern ? "1" : "0" );
00197   map["customdash"] = QgsSymbolLayerV2Utils::encodeRealVector( mCustomDashVector );
00198   map["customdash_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mCustomDashPatternUnit );
00199   saveDataDefinedProperties( map );
00200   return map;
00201 }
00202 
00203 QgsSymbolLayerV2* QgsSimpleLineSymbolLayerV2::clone() const
00204 {
00205   QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( mColor, mWidth, mPenStyle );
00206   l->setWidthUnit( mWidthUnit );
00207   l->setOffsetUnit( mOffsetUnit );
00208   l->setCustomDashPatternUnit( mCustomDashPatternUnit );
00209   l->setOffset( mOffset );
00210   l->setPenJoinStyle( mPenJoinStyle );
00211   l->setPenCapStyle( mPenCapStyle );
00212   l->setUseCustomDashPattern( mUseCustomDashPattern );
00213   l->setCustomDashVector( mCustomDashVector );
00214   copyDataDefinedProperties( l );
00215   return l;
00216 }
00217 
00218 void QgsSimpleLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
00219 {
00220   if ( mPenStyle == Qt::NoPen )
00221     return;
00222 
00223   QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
00224   if ( !props.value( "uom", "" ).isEmpty() )
00225     symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
00226   element.appendChild( symbolizerElem );
00227 
00228   // <Geometry>
00229   QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
00230 
00231   // <Stroke>
00232   QDomElement strokeElem = doc.createElement( "se:Stroke" );
00233   symbolizerElem.appendChild( strokeElem );
00234 
00235   Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
00236   QgsSymbolLayerV2Utils::lineToSld( doc, strokeElem, penStyle, mColor, mWidth,
00237                                     &mPenJoinStyle, &mPenCapStyle, &mCustomDashVector );
00238 
00239   // <se:PerpendicularOffset>
00240   if ( mOffset != 0 )
00241   {
00242     QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
00243     perpOffsetElem.appendChild( doc.createTextNode( QString::number( mOffset ) ) );
00244     symbolizerElem.appendChild( perpOffsetElem );
00245   }
00246 }
00247 
00248 QString QgsSimpleLineSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
00249 {
00250   if ( mUseCustomDashPattern )
00251   {
00252     return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor,
00253            mPen.color(), mPenJoinStyle,
00254            mPenCapStyle, mOffset, &mCustomDashVector );
00255   }
00256   else
00257   {
00258     return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor, mPen.color(), mPenJoinStyle,
00259            mPenCapStyle, mOffset );
00260   }
00261 }
00262 
00263 QgsSymbolLayerV2* QgsSimpleLineSymbolLayerV2::createFromSld( QDomElement &element )
00264 {
00265   QgsDebugMsg( "Entered." );
00266 
00267   QDomElement strokeElem = element.firstChildElement( "Stroke" );
00268   if ( strokeElem.isNull() )
00269     return NULL;
00270 
00271   Qt::PenStyle penStyle;
00272   QColor color;
00273   double width;
00274   Qt::PenJoinStyle penJoinStyle;
00275   Qt::PenCapStyle penCapStyle;
00276   QVector<qreal> customDashVector;
00277 
00278   if ( !QgsSymbolLayerV2Utils::lineFromSld( strokeElem, penStyle,
00279        color, width,
00280        &penJoinStyle, &penCapStyle,
00281        &customDashVector ) )
00282     return NULL;
00283 
00284   double offset = 0.0;
00285   QDomElement perpOffsetElem = element.firstChildElement( "PerpendicularOffset" );
00286   if ( !perpOffsetElem.isNull() )
00287   {
00288     bool ok;
00289     double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
00290     if ( ok )
00291       offset = d;
00292   }
00293 
00294   QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
00295   l->setOffset( offset );
00296   l->setPenJoinStyle( penJoinStyle );
00297   l->setPenCapStyle( penCapStyle );
00298   l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine );
00299   l->setCustomDashVector( customDashVector );
00300   return l;
00301 }
00302 
00303 void QgsSimpleLineSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen, double& offset )
00304 {
00305   //data defined properties
00306   double scaledWidth = 0;
00307   QgsExpression* strokeWidthExpression = expression( "width" );
00308   if ( strokeWidthExpression )
00309   {
00310     scaledWidth = strokeWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble()
00311                   * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mWidthUnit );
00312     pen.setWidthF( scaledWidth );
00313     selPen.setWidthF( scaledWidth );
00314   }
00315   else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
00316   {
00317     scaledWidth = mWidth * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mWidthUnit );
00318     pen.setWidthF( scaledWidth );
00319     selPen.setWidthF( scaledWidth );
00320   }
00321 
00322   //color
00323   QgsExpression* strokeColorExpression = expression( "color" );
00324   if ( strokeColorExpression )
00325   {
00326     pen.setColor( QgsSymbolLayerV2Utils::decodeColor( strokeColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
00327   }
00328 
00329   //offset
00330   offset = mOffset;
00331   QgsExpression* lineOffsetExpression = expression( "offset" );
00332   if ( lineOffsetExpression )
00333   {
00334     offset = lineOffsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
00335   }
00336 
00337   //dash dot vector
00338   QgsExpression* dashPatternExpression = expression( "customdash" );
00339   if ( dashPatternExpression )
00340   {
00341     QVector<qreal> dashVector;
00342     QStringList dashList = dashPatternExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString().split( ";" );
00343     QStringList::const_iterator dashIt = dashList.constBegin();
00344     for ( ; dashIt != dashList.constEnd(); ++dashIt )
00345     {
00346       dashVector.push_back( dashIt->toDouble() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mCustomDashPatternUnit ) / mPen.widthF() );
00347     }
00348     pen.setDashPattern( dashVector );
00349   }
00350 
00351   //join style
00352   QgsExpression* joinStyleExpression = expression( "joinstyle" );
00353   if ( joinStyleExpression )
00354   {
00355     QString joinStyleString = joinStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
00356     pen.setJoinStyle( QgsSymbolLayerV2Utils::decodePenJoinStyle( joinStyleString ) );
00357   }
00358 
00359   //cap style
00360   QgsExpression* capStyleExpression = expression( "capstyle" );
00361   if ( capStyleExpression )
00362   {
00363     QString capStyleString = capStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
00364     pen.setCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( capStyleString ) );
00365   }
00366 }
00367 
00368 
00369 
00371 
00372 
00373 class MyLine
00374 {
00375   public:
00376     MyLine( QPointF p1, QPointF p2 ) : mVertical( false ), mIncreasing( false ), mT( 0.0 ), mLength( 0.0 )
00377     {
00378       if ( p1 == p2 )
00379         return; // invalid
00380 
00381       // tangent and direction
00382       if ( p1.x() == p2.x() )
00383       {
00384         // vertical line - tangent undefined
00385         mVertical = true;
00386         mIncreasing = ( p2.y() > p1.y() );
00387       }
00388       else
00389       {
00390         mVertical = false;
00391         mT = float( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
00392         mIncreasing = ( p2.x() > p1.x() );
00393       }
00394 
00395       // length
00396       double x = ( p2.x() - p1.x() );
00397       double y = ( p2.y() - p1.y() );
00398       mLength = sqrt( x * x + y * y );
00399     }
00400 
00401     // return angle in radians
00402     double angle()
00403     {
00404       double a = ( mVertical ? M_PI / 2 : atan( mT ) );
00405 
00406       if ( !mIncreasing )
00407         a += M_PI;
00408       return a;
00409     }
00410 
00411     // return difference for x,y when going along the line with specified interval
00412     QPointF diffForInterval( double interval )
00413     {
00414       if ( mVertical )
00415         return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
00416 
00417       double alpha = atan( mT );
00418       double dx = cos( alpha ) * interval;
00419       double dy = sin( alpha ) * interval;
00420       return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
00421     }
00422 
00423     double length() { return mLength; }
00424 
00425   protected:
00426     bool mVertical;
00427     bool mIncreasing;
00428     double mT;
00429     double mLength;
00430 };
00431 
00432 
00433 QgsMarkerLineSymbolLayerV2::QgsMarkerLineSymbolLayerV2( bool rotateMarker, double interval )
00434 {
00435   mRotateMarker = rotateMarker;
00436   mInterval = interval;
00437   mIntervalUnit = QgsSymbolV2::MM;
00438   mMarker = NULL;
00439   mOffset = 0;
00440   mOffsetUnit = QgsSymbolV2::MM;
00441   mPlacement = Interval;
00442 
00443   setSubSymbol( new QgsMarkerSymbolV2() );
00444 }
00445 
00446 QgsMarkerLineSymbolLayerV2::~QgsMarkerLineSymbolLayerV2()
00447 {
00448   delete mMarker;
00449 }
00450 
00451 QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::create( const QgsStringMap& props )
00452 {
00453   bool rotate = DEFAULT_MARKERLINE_ROTATE;
00454   double interval = DEFAULT_MARKERLINE_INTERVAL;
00455 
00456   if ( props.contains( "interval" ) )
00457     interval = props["interval"].toDouble();
00458   if ( props.contains( "rotate" ) )
00459     rotate = ( props["rotate"] == "1" );
00460 
00461   QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotate, interval );
00462   if ( props.contains( "offset" ) )
00463   {
00464     x->setOffset( props["offset"].toDouble() );
00465   }
00466   if ( props.contains( "offset_unit" ) )
00467   {
00468     x->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
00469   }
00470   if ( props.contains( "interval_unit" ) )
00471   {
00472     x->setIntervalUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["interval_unit"] ) );
00473   }
00474 
00475   if ( props.contains( "placement" ) )
00476   {
00477     if ( props["placement"] == "vertex" )
00478       x->setPlacement( Vertex );
00479     else if ( props["placement"] == "lastvertex" )
00480       x->setPlacement( LastVertex );
00481     else if ( props["placement"] == "firstvertex" )
00482       x->setPlacement( FirstVertex );
00483     else if ( props["placement"] == "centralpoint" )
00484       x->setPlacement( CentralPoint );
00485     else
00486       x->setPlacement( Interval );
00487   }
00488 
00489   //data defined properties
00490   if ( props.contains( "interval_expression" ) )
00491   {
00492     x->setDataDefinedProperty( "interval", props["interval_expression"] );
00493   }
00494   if ( props.contains( "offset_expression" ) )
00495   {
00496     x->setDataDefinedProperty( "offset", props["offset_expression"] );
00497   }
00498   if ( props.contains( "placement_expression" ) )
00499   {
00500     x->setDataDefinedProperty( "placement", props["placement_expression"] );
00501   }
00502 
00503   return x;
00504 }
00505 
00506 QString QgsMarkerLineSymbolLayerV2::layerType() const
00507 {
00508   return "MarkerLine";
00509 }
00510 
00511 void QgsMarkerLineSymbolLayerV2::setColor( const QColor& color )
00512 {
00513   mMarker->setColor( color );
00514   mColor = color;
00515 }
00516 
00517 void QgsMarkerLineSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context )
00518 {
00519   mMarker->setAlpha( context.alpha() );
00520 
00521   // if being rotated, it gets initialized with every line segment
00522   int hints = 0;
00523   if ( mRotateMarker )
00524     hints |= QgsSymbolV2::DataDefinedRotation;
00525   if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
00526     hints |= QgsSymbolV2::DataDefinedSizeScale;
00527   mMarker->setRenderHints( hints );
00528 
00529   mMarker->startRender( context.renderContext(), context.layer() );
00530 
00531   //prepare expressions for data defined properties
00532   prepareExpressions( context.layer() );
00533 }
00534 
00535 void QgsMarkerLineSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
00536 {
00537   mMarker->stopRender( context.renderContext() );
00538 }
00539 
00540 void QgsMarkerLineSymbolLayerV2::renderPolyline( const QPolygonF& points, QgsSymbolV2RenderContext& context )
00541 {
00542   double offset = mOffset;
00543   QgsExpression* offsetExpression = expression( "offset" );
00544   if ( offsetExpression )
00545   {
00546     offset = offsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
00547   }
00548 
00549   Placement placement = mPlacement;
00550   QgsExpression* placementExpression = expression( "placement" );
00551   if ( placementExpression )
00552   {
00553     QString placementString = placementExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
00554     if ( placementString.compare( "vertex", Qt::CaseInsensitive ) == 0 )
00555     {
00556       placement = Vertex;
00557     }
00558     else if ( placementString.compare( "lastvertex", Qt::CaseInsensitive ) == 0 )
00559     {
00560       placement = LastVertex;
00561     }
00562     else if ( placementString.compare( "firstvertex", Qt::CaseInsensitive ) == 0 )
00563     {
00564       placement = FirstVertex;
00565     }
00566     else if ( placementString.compare( "centerpoint", Qt::CaseInsensitive ) == 0 )
00567     {
00568       placement = CentralPoint;
00569     }
00570     else
00571     {
00572       placement = Interval;
00573     }
00574   }
00575 
00576   if ( offset == 0 )
00577   {
00578     if ( placement == Interval )
00579       renderPolylineInterval( points, context );
00580     else if ( placement == CentralPoint )
00581       renderPolylineCentral( points, context );
00582     else
00583       renderPolylineVertex( points, context, placement );
00584   }
00585   else
00586   {
00587     QPolygonF points2 = ::offsetLine( points, offset * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit ) );
00588     if ( placement == Interval )
00589       renderPolylineInterval( points2, context );
00590     else if ( placement == CentralPoint )
00591       renderPolylineCentral( points2, context );
00592     else
00593       renderPolylineVertex( points2, context, placement );
00594   }
00595 }
00596 
00597 void QgsMarkerLineSymbolLayerV2::renderPolylineInterval( const QPolygonF& points, QgsSymbolV2RenderContext& context )
00598 {
00599   if ( points.isEmpty() )
00600     return;
00601 
00602   QPointF lastPt = points[0];
00603   double lengthLeft = 0; // how much is left until next marker
00604   bool first = true;
00605   double origAngle = mMarker->angle();
00606 
00607   QgsRenderContext& rc = context.renderContext();
00608   double interval = mInterval;
00609 
00610   QgsExpression* intervalExpression = expression( "interval" );
00611   if ( intervalExpression )
00612   {
00613     interval = intervalExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
00614   }
00615   if ( interval <= 0 )
00616   {
00617     interval = 0.1;
00618   }
00619 
00620   double painterUnitInterval = interval * QgsSymbolLayerV2Utils::lineWidthScaleFactor( rc, mIntervalUnit );
00621 
00622   for ( int i = 1; i < points.count(); ++i )
00623   {
00624     const QPointF& pt = points[i];
00625 
00626     if ( lastPt == pt ) // must not be equal!
00627       continue;
00628 
00629     // for each line, find out dx and dy, and length
00630     MyLine l( lastPt, pt );
00631     QPointF diff = l.diffForInterval( painterUnitInterval );
00632 
00633     // if there's some length left from previous line
00634     // use only the rest for the first point in new line segment
00635     double c = 1 - lengthLeft / painterUnitInterval;
00636 
00637     lengthLeft += l.length();
00638 
00639     // rotate marker (if desired)
00640     if ( mRotateMarker )
00641     {
00642       mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
00643     }
00644 
00645     // draw first marker
00646     if ( first )
00647     {
00648       mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
00649       first = false;
00650     }
00651 
00652     // while we're not at the end of line segment, draw!
00653     while ( lengthLeft > painterUnitInterval )
00654     {
00655       // "c" is 1 for regular point or in interval (0,1] for begin of line segment
00656       lastPt += c * diff;
00657       lengthLeft -= painterUnitInterval;
00658       mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
00659       c = 1; // reset c (if wasn't 1 already)
00660     }
00661 
00662     lastPt = pt;
00663   }
00664 
00665   // restore original rotation
00666   mMarker->setAngle( origAngle );
00667 
00668 }
00669 
00670 static double _averageAngle( const QPointF& prevPt, const QPointF& pt, const QPointF& nextPt )
00671 {
00672   // calc average angle between the previous and next point
00673   double a1 = MyLine( prevPt, pt ).angle();
00674   double a2 = MyLine( pt, nextPt ).angle();
00675   double unitX = cos( a1 ) + cos( a2 ), unitY = sin( a1 ) + sin( a2 );
00676 
00677   return atan2( unitY, unitX );
00678 }
00679 
00680 void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points, QgsSymbolV2RenderContext& context, Placement placement )
00681 {
00682   if ( points.isEmpty() )
00683     return;
00684 
00685   QgsRenderContext& rc = context.renderContext();
00686 
00687   double origAngle = mMarker->angle();
00688   double angle;
00689   int i, maxCount;
00690   bool isRing = false;
00691 
00692   if ( placement == FirstVertex )
00693   {
00694     i = 0;
00695     maxCount = 1;
00696   }
00697   else if ( placement == LastVertex )
00698   {
00699     i = points.count() - 1;
00700     maxCount = points.count();
00701   }
00702   else
00703   {
00704     i = 0;
00705     maxCount = points.count();
00706     if ( points.first() == points.last() )
00707       isRing = true;
00708   }
00709 
00710   for ( ; i < maxCount; ++i )
00711   {
00712     const QPointF& pt = points[i];
00713 
00714     // rotate marker (if desired)
00715     if ( mRotateMarker )
00716     {
00717       if ( i == 0 )
00718       {
00719         if ( !isRing )
00720         {
00721           // use first segment's angle
00722           const QPointF& nextPt = points[i+1];
00723           if ( pt == nextPt )
00724             continue;
00725           angle = MyLine( pt, nextPt ).angle();
00726         }
00727         else
00728         {
00729           // closed ring: use average angle between first and last segment
00730           const QPointF& prevPt = points[points.count() - 2];
00731           const QPointF& nextPt = points[1];
00732           if ( prevPt == pt || nextPt == pt )
00733             continue;
00734 
00735           angle = _averageAngle( prevPt, pt, nextPt );
00736         }
00737       }
00738       else if ( i == points.count() - 1 )
00739       {
00740         if ( !isRing )
00741         {
00742           // use last segment's angle
00743           const QPointF& prevPt = points[i-1];
00744           if ( pt == prevPt )
00745             continue;
00746           angle = MyLine( prevPt, pt ).angle();
00747         }
00748         else
00749         {
00750           // don't draw the last marker - it has been drawn already
00751           continue;
00752         }
00753       }
00754       else
00755       {
00756         // use average angle
00757         const QPointF& prevPt = points[i-1];
00758         const QPointF& nextPt = points[i+1];
00759         if ( prevPt == pt || nextPt == pt )
00760           continue;
00761 
00762         angle = _averageAngle( prevPt, pt, nextPt );
00763       }
00764       mMarker->setAngle( origAngle + angle * 180 / M_PI );
00765     }
00766 
00767     mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() );
00768   }
00769 
00770   // restore original rotation
00771   mMarker->setAngle( origAngle );
00772 }
00773 
00774 void QgsMarkerLineSymbolLayerV2::renderPolylineCentral( const QPolygonF& points, QgsSymbolV2RenderContext& context )
00775 {
00776   // calc length
00777   qreal length = 0;
00778   QPolygonF::const_iterator it = points.constBegin();
00779   QPointF last = *it;
00780   for ( ++it; it != points.constEnd(); ++it )
00781   {
00782     length += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
00783                    ( last.y() - it->y() ) * ( last.y() - it->y() ) );
00784     last = *it;
00785   }
00786 
00787   // find the segment where the central point lies
00788   it = points.constBegin();
00789   last = *it;
00790   qreal last_at = 0, next_at = 0;
00791   QPointF next;
00792   int segment = 0;
00793   for ( ++it; it != points.constEnd(); ++it )
00794   {
00795     next = *it;
00796     next_at += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
00797                     ( last.y() - it->y() ) * ( last.y() - it->y() ) );
00798     if ( next_at >= length / 2 )
00799       break; // we have reached the center
00800     last = *it;
00801     last_at = next_at;
00802     segment++;
00803   }
00804 
00805   // find out the central point on segment
00806   MyLine l( last, next ); // for line angle
00807   qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
00808   QPointF pt = last + ( next - last ) * k;
00809 
00810   // draw the marker
00811   double origAngle = mMarker->angle();
00812   if ( mRotateMarker )
00813     mMarker->setAngle( origAngle + l.angle() * 180 / M_PI );
00814   mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() );
00815   if ( mRotateMarker )
00816     mMarker->setAngle( origAngle );
00817 }
00818 
00819 
00820 QgsStringMap QgsMarkerLineSymbolLayerV2::properties() const
00821 {
00822   QgsStringMap map;
00823   map["rotate"] = ( mRotateMarker ? "1" : "0" );
00824   map["interval"] = QString::number( mInterval );
00825   map["offset"] = QString::number( mOffset );
00826   map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
00827   map["interval_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mIntervalUnit );
00828   if ( mPlacement == Vertex )
00829     map["placement"] = "vertex";
00830   else if ( mPlacement == LastVertex )
00831     map["placement"] = "lastvertex";
00832   else if ( mPlacement == FirstVertex )
00833     map["placement"] = "firstvertex";
00834   else if ( mPlacement == CentralPoint )
00835     map["placement"] = "centralpoint";
00836   else
00837     map["placement"] = "interval";
00838 
00839   saveDataDefinedProperties( map );
00840   return map;
00841 }
00842 
00843 QgsSymbolV2* QgsMarkerLineSymbolLayerV2::subSymbol()
00844 {
00845   return mMarker;
00846 }
00847 
00848 bool QgsMarkerLineSymbolLayerV2::setSubSymbol( QgsSymbolV2* symbol )
00849 {
00850   if ( symbol == NULL || symbol->type() != QgsSymbolV2::Marker )
00851   {
00852     delete symbol;
00853     return false;
00854   }
00855 
00856   delete mMarker;
00857   mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
00858   mColor = mMarker->color();
00859   return true;
00860 }
00861 
00862 QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::clone() const
00863 {
00864   QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( mRotateMarker, mInterval );
00865   x->setSubSymbol( mMarker->clone() );
00866   x->setOffset( mOffset );
00867   x->setPlacement( mPlacement );
00868   x->setOffsetUnit( mOffsetUnit );
00869   x->setIntervalUnit( mIntervalUnit );
00870   copyDataDefinedProperties( x );
00871   return x;
00872 }
00873 
00874 void QgsMarkerLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
00875 {
00876   for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
00877   {
00878     QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
00879     if ( !props.value( "uom", "" ).isEmpty() )
00880       symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
00881     element.appendChild( symbolizerElem );
00882 
00883     // <Geometry>
00884     QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
00885 
00886     QString gap;
00887     switch ( mPlacement )
00888     {
00889       case FirstVertex:
00890         symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "firstPoint" ) );
00891         break;
00892       case LastVertex:
00893         symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) );
00894         break;
00895       case CentralPoint:
00896         symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "centralPoint" ) );
00897         break;
00898       case Vertex:
00899         // no way to get line/polygon's vertices, use a VendorOption
00900         symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "points" ) );
00901         break;
00902       default:
00903         gap = QString::number( mInterval );
00904         break;
00905     }
00906 
00907     if ( !mRotateMarker )
00908     {
00909       // markers in LineSymbolizer must be drawn following the line orientation,
00910       // use a VendorOption when no marker rotation
00911       symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "rotateMarker", "0" ) );
00912     }
00913 
00914     // <Stroke>
00915     QDomElement strokeElem = doc.createElement( "se:Stroke" );
00916     symbolizerElem.appendChild( strokeElem );
00917 
00918     // <GraphicStroke>
00919     QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" );
00920     strokeElem.appendChild( graphicStrokeElem );
00921 
00922     QgsSymbolLayerV2 *layer = mMarker->symbolLayer( i );
00923     QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
00924     if ( !markerLayer )
00925     {
00926       graphicStrokeElem.appendChild( doc.createComment( QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( markerLayer->layerType() ) ) );
00927     }
00928     else
00929     {
00930       markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
00931     }
00932 
00933     if ( !gap.isEmpty() )
00934     {
00935       QDomElement gapElem = doc.createElement( "se:Gap" );
00936       QgsSymbolLayerV2Utils::createFunctionElement( doc, gapElem, gap );
00937       graphicStrokeElem.appendChild( gapElem );
00938     }
00939 
00940     if ( !qgsDoubleNear( mOffset, 0.0 ) )
00941     {
00942       QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
00943       perpOffsetElem.appendChild( doc.createTextNode( QString::number( mOffset ) ) );
00944       symbolizerElem.appendChild( perpOffsetElem );
00945     }
00946   }
00947 }
00948 
00949 QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::createFromSld( QDomElement &element )
00950 {
00951   QgsDebugMsg( "Entered." );
00952 
00953   QDomElement strokeElem = element.firstChildElement( "Stroke" );
00954   if ( strokeElem.isNull() )
00955     return NULL;
00956 
00957   QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
00958   if ( graphicStrokeElem.isNull() )
00959     return NULL;
00960 
00961   // retrieve vendor options
00962   bool rotateMarker = true;
00963   Placement placement = Interval;
00964 
00965   QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( element );
00966   for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
00967   {
00968     if ( it.key() == "placement" )
00969     {
00970       if ( it.value() == "points" ) placement = Vertex;
00971       else if ( it.value() == "firstPoint" ) placement = FirstVertex;
00972       else if ( it.value() == "lastPoint" ) placement = LastVertex;
00973       else if ( it.value() == "centralPoint" ) placement = CentralPoint;
00974     }
00975     else if ( it.value() == "rotateMarker" )
00976     {
00977       rotateMarker = it.value() == "0";
00978     }
00979   }
00980 
00981   QgsMarkerSymbolV2 *marker = 0;
00982 
00983   QgsSymbolLayerV2 *l = QgsSymbolLayerV2Utils::createMarkerLayerFromSld( graphicStrokeElem );
00984   if ( l )
00985   {
00986     QgsSymbolLayerV2List layers;
00987     layers.append( l );
00988     marker = new QgsMarkerSymbolV2( layers );
00989   }
00990 
00991   if ( !marker )
00992     return NULL;
00993 
00994   double interval = 0.0;
00995   QDomElement gapElem = graphicStrokeElem.firstChildElement( "Gap" );
00996   if ( !gapElem.isNull() )
00997   {
00998     bool ok;
00999     double d = gapElem.firstChild().nodeValue().toDouble( &ok );
01000     if ( ok )
01001       interval = d;
01002   }
01003 
01004   double offset = 0.0;
01005   QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( "PerpendicularOffset" );
01006   if ( !perpOffsetElem.isNull() )
01007   {
01008     bool ok;
01009     double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
01010     if ( ok )
01011       offset = d;
01012   }
01013 
01014   QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotateMarker );
01015   x->setPlacement( placement );
01016   x->setInterval( interval );
01017   x->setSubSymbol( marker );
01018   x->setOffset( offset );
01019   return x;
01020 }
01021 
01022 void QgsMarkerLineSymbolLayerV2::setWidth( double width )
01023 {
01024   mMarker->setSize( width );
01025 }
01026 
01027 double QgsMarkerLineSymbolLayerV2::width() const
01028 {
01029   return mMarker->size();
01030 }
01031 
01032 void QgsMarkerLineSymbolLayerV2::setOutputUnit( QgsSymbolV2::OutputUnit unit )
01033 {
01034   mIntervalUnit = unit;
01035   mOffsetUnit = unit;
01036 }
01037 
01038 QgsSymbolV2::OutputUnit QgsMarkerLineSymbolLayerV2::outputUnit() const
01039 {
01040   QgsSymbolV2::OutputUnit unit = mIntervalUnit;
01041   if ( mOffsetUnit != unit )
01042   {
01043     return QgsSymbolV2::Mixed;
01044   }
01045   return unit;
01046 }
01047 
01049 
01050 QgsLineDecorationSymbolLayerV2::QgsLineDecorationSymbolLayerV2( QColor color, double width )
01051 {
01052   mColor = color;
01053   mWidth = width;
01054 }
01055 
01056 QgsLineDecorationSymbolLayerV2::~QgsLineDecorationSymbolLayerV2()
01057 {
01058 }
01059 
01060 QgsSymbolLayerV2* QgsLineDecorationSymbolLayerV2::create( const QgsStringMap& props )
01061 {
01062   QColor color = DEFAULT_LINEDECORATION_COLOR;
01063   double width = DEFAULT_LINEDECORATION_WIDTH;
01064 
01065   if ( props.contains( "color" ) )
01066     color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
01067   if ( props.contains( "width" ) )
01068     width = props["width"].toDouble();
01069 
01070 
01071   QgsLineDecorationSymbolLayerV2* layer = new QgsLineDecorationSymbolLayerV2( color, width );
01072   if ( props.contains( "width_unit" ) )
01073   {
01074     layer->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["width_unit"] ) );
01075   }
01076   return layer;
01077 }
01078 
01079 QString QgsLineDecorationSymbolLayerV2::layerType() const
01080 {
01081   return "LineDecoration";
01082 }
01083 
01084 void QgsLineDecorationSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context )
01085 {
01086   QColor penColor = mColor;
01087   penColor.setAlphaF( mColor.alphaF() * context.alpha() );
01088 
01089   double width = mWidth * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mWidthUnit );
01090   mPen.setWidth( context.outputLineWidth( width ) );
01091   mPen.setColor( penColor );
01092   QColor selColor = context.renderContext().selectionColor();
01093   if ( ! selectionIsOpaque )
01094     selColor.setAlphaF( context.alpha() );
01095   mSelPen.setWidth( context.outputLineWidth( width ) );
01096   mSelPen.setColor( selColor );
01097 }
01098 
01099 void QgsLineDecorationSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
01100 {
01101   Q_UNUSED( context );
01102 }
01103 
01104 void QgsLineDecorationSymbolLayerV2::renderPolyline( const QPolygonF& points, QgsSymbolV2RenderContext& context )
01105 {
01106   // draw arrow at the end of line
01107 
01108   QPainter* p = context.renderContext().painter();
01109   if ( !p )
01110   {
01111     return;
01112   }
01113 
01114   int cnt = points.count();
01115   if ( cnt < 2 )
01116   {
01117     return;
01118   }
01119   QPointF p2 = points.at( --cnt );
01120   QPointF p1 = points.at( --cnt );
01121   while ( p2 == p1 && cnt )
01122     p1 = points.at( --cnt );
01123   if ( p1 == p2 )
01124   {
01125     // this is a collapsed line... don't bother drawing an arrow
01126     // with arbitrary orientation
01127     return;
01128   }
01129 
01130   double angle = atan2( p2.y() - p1.y(), p2.x() - p1.x() );
01131   double size = ( mWidth * 8 ) * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mWidthUnit );
01132   double angle1 = angle + M_PI / 6;
01133   double angle2 = angle - M_PI / 6;
01134 
01135   QPointF p2_1 = p2 - QPointF( size * cos( angle1 ), size * sin( angle1 ) );
01136   QPointF p2_2 = p2 - QPointF( size * cos( angle2 ), size * sin( angle2 ) );
01137 
01138   p->setPen( context.selected() ? mSelPen : mPen );
01139   p->drawLine( p2, p2_1 );
01140   p->drawLine( p2, p2_2 );
01141 }
01142 
01143 QgsStringMap QgsLineDecorationSymbolLayerV2::properties() const
01144 {
01145   QgsStringMap map;
01146   map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
01147   map["width"] = QString::number( mWidth );
01148   map["width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mWidthUnit );
01149   return map;
01150 }
01151 
01152 QgsSymbolLayerV2* QgsLineDecorationSymbolLayerV2::clone() const
01153 {
01154   QgsLineDecorationSymbolLayerV2* layer = new QgsLineDecorationSymbolLayerV2( mColor, mWidth );
01155   layer->setWidthUnit( mWidthUnit );
01156   return layer;
01157 }
01158 
01159 void QgsLineDecorationSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
01160 {
01161   QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
01162   if ( !props.value( "uom", "" ).isEmpty() )
01163     symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
01164   element.appendChild( symbolizerElem );
01165 
01166   QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom" , "" ) );
01167 
01168   // <Stroke>
01169   QDomElement strokeElem = doc.createElement( "se:Stroke" );
01170   symbolizerElem.appendChild( strokeElem );
01171 
01172   // <GraphicStroke>
01173   QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" );
01174   strokeElem.appendChild( graphicStrokeElem );
01175 
01176   // <Graphic>
01177   QDomElement graphicElem = doc.createElement( "se:Graphic" );
01178   graphicStrokeElem.appendChild( graphicElem );
01179 
01180   // <Mark>
01181   QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, "arrowhead", QColor(), mColor, mWidth, mWidth*8 );
01182 
01183   // <Rotation>
01184   QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, props.value( "angle", "" ) );
01185 
01186   // use <VendorOption> to draw the decoration at end of the line
01187   symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) );
01188 }
01189 
01190 void QgsLineDecorationSymbolLayerV2::setOutputUnit( QgsSymbolV2::OutputUnit unit )
01191 {
01192   mWidthUnit = unit;
01193 }
01194 
01195 QgsSymbolV2::OutputUnit QgsLineDecorationSymbolLayerV2::outputUnit() const
01196 {
01197   return mWidthUnit;
01198 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines