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