Quantum GIS API Documentation
1.8
|
00001 /*************************************************************************** 00002 qgslinesymbollayerv2.cpp 00003 --------------------- 00004 begin : November 2009 00005 copyright : (C) 2009 by Martin Dobias 00006 email : wonder.sk at gmail.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 00019 #include "qgsrendercontext.h" 00020 #include "qgslogger.h" 00021 00022 #include <QPainter> 00023 #include <QDomDocument> 00024 #include <QDomElement> 00025 00026 #include <cmath> 00027 00028 QgsSimpleLineSymbolLayerV2::QgsSimpleLineSymbolLayerV2( QColor color, double width, Qt::PenStyle penStyle ) 00029 : mPenStyle( penStyle ), mPenJoinStyle( DEFAULT_SIMPLELINE_JOINSTYLE ), mPenCapStyle( DEFAULT_SIMPLELINE_CAPSTYLE ), mOffset( 0 ), mUseCustomDashPattern( false ) 00030 { 00031 mColor = color; 00032 mWidth = width; 00033 mCustomDashVector << 5 << 2; 00034 } 00035 00036 00037 QgsSymbolLayerV2* QgsSimpleLineSymbolLayerV2::create( const QgsStringMap& props ) 00038 { 00039 QColor color = DEFAULT_SIMPLELINE_COLOR; 00040 double width = DEFAULT_SIMPLELINE_WIDTH; 00041 Qt::PenStyle penStyle = DEFAULT_SIMPLELINE_PENSTYLE; 00042 00043 if ( props.contains( "color" ) ) 00044 color = QgsSymbolLayerV2Utils::decodeColor( props["color"] ); 00045 if ( props.contains( "width" ) ) 00046 width = props["width"].toDouble(); 00047 if ( props.contains( "penstyle" ) ) 00048 penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["penstyle"] ); 00049 00050 00051 QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle ); 00052 if ( props.contains( "offset" ) ) 00053 l->setOffset( props["offset"].toDouble() ); 00054 if ( props.contains( "joinstyle" ) ) 00055 l->setPenJoinStyle( QgsSymbolLayerV2Utils::decodePenJoinStyle( props["joinstyle"] ) ); 00056 if ( props.contains( "capstyle" ) ) 00057 l->setPenCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( props["capstyle"] ) ); 00058 00059 if ( props.contains( "use_custom_dash" ) ) 00060 { 00061 l->setUseCustomDashPattern( props["use_custom_dash"].toInt() ); 00062 } 00063 if ( props.contains( "customdash" ) ) 00064 { 00065 l->setCustomDashVector( QgsSymbolLayerV2Utils::decodeRealVector( props["customdash"] ) ); 00066 } 00067 return l; 00068 } 00069 00070 00071 QString QgsSimpleLineSymbolLayerV2::layerType() const 00072 { 00073 return "SimpleLine"; 00074 } 00075 00076 void QgsSimpleLineSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 00077 { 00078 QColor penColor = mColor; 00079 penColor.setAlphaF( context.alpha() ); 00080 mPen.setColor( penColor ); 00081 double scaledWidth = context.outputLineWidth( mWidth ); 00082 mPen.setWidthF( scaledWidth ); 00083 if ( mUseCustomDashPattern && scaledWidth != 0 ) 00084 { 00085 mPen.setStyle( Qt::CustomDashLine ); 00086 00087 //scale pattern vector 00088 QVector<qreal> scaledVector; 00089 QVector<qreal>::const_iterator it = mCustomDashVector.constBegin(); 00090 for ( ; it != mCustomDashVector.constEnd(); ++it ) 00091 { 00092 //the dash is specified in terms of pen widths, therefore the division 00093 scaledVector << context.outputLineWidth(( *it ) / scaledWidth ); 00094 } 00095 mPen.setDashPattern( scaledVector ); 00096 } 00097 else 00098 { 00099 mPen.setStyle( mPenStyle ); 00100 } 00101 mPen.setJoinStyle( mPenJoinStyle ); 00102 mPen.setCapStyle( mPenCapStyle ); 00103 00104 mSelPen = mPen; 00105 QColor selColor = context.selectionColor(); 00106 if ( ! selectionIsOpaque ) 00107 selColor.setAlphaF( context.alpha() ); 00108 mSelPen.setColor( selColor ); 00109 } 00110 00111 void QgsSimpleLineSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context ) 00112 { 00113 Q_UNUSED( context ); 00114 } 00115 00116 void QgsSimpleLineSymbolLayerV2::renderPolyline( const QPolygonF& points, QgsSymbolV2RenderContext& context ) 00117 { 00118 QPainter* p = context.renderContext().painter(); 00119 if ( !p ) 00120 { 00121 return; 00122 } 00123 00124 if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) 00125 { 00126 double scaledWidth = context.outputLineWidth( mWidth ); 00127 mPen.setWidthF( scaledWidth ); 00128 mSelPen.setWidthF( scaledWidth ); 00129 } 00130 00131 p->setPen( context.selected() ? mSelPen : mPen ); 00132 if ( mOffset == 0 ) 00133 { 00134 p->drawPolyline( points ); 00135 } 00136 else 00137 { 00138 double scaledOffset = context.outputLineWidth( mOffset ); 00139 p->drawPolyline( ::offsetLine( points, scaledOffset ) ); 00140 } 00141 } 00142 00143 QgsStringMap QgsSimpleLineSymbolLayerV2::properties() const 00144 { 00145 QgsStringMap map; 00146 map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor ); 00147 map["width"] = QString::number( mWidth ); 00148 map["penstyle"] = QgsSymbolLayerV2Utils::encodePenStyle( mPenStyle ); 00149 map["joinstyle"] = QgsSymbolLayerV2Utils::encodePenJoinStyle( mPenJoinStyle ); 00150 map["capstyle"] = QgsSymbolLayerV2Utils::encodePenCapStyle( mPenCapStyle ); 00151 map["offset"] = QString::number( mOffset ); 00152 map["use_custom_dash"] = ( mUseCustomDashPattern ? "1" : "0" ); 00153 map["customdash"] = QgsSymbolLayerV2Utils::encodeRealVector( mCustomDashVector ); 00154 return map; 00155 } 00156 00157 QgsSymbolLayerV2* QgsSimpleLineSymbolLayerV2::clone() const 00158 { 00159 QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( mColor, mWidth, mPenStyle ); 00160 l->setOffset( mOffset ); 00161 l->setPenJoinStyle( mPenJoinStyle ); 00162 l->setPenCapStyle( mPenCapStyle ); 00163 l->setUseCustomDashPattern( mUseCustomDashPattern ); 00164 l->setCustomDashVector( mCustomDashVector ); 00165 return l; 00166 } 00167 00168 void QgsSimpleLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 00169 { 00170 if ( mPenStyle == Qt::NoPen ) 00171 return; 00172 00173 QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" ); 00174 if ( !props.value( "uom", "" ).isEmpty() ) 00175 symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) ); 00176 element.appendChild( symbolizerElem ); 00177 00178 // <Geometry> 00179 QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) ); 00180 00181 // <Stroke> 00182 QDomElement strokeElem = doc.createElement( "se:Stroke" ); 00183 symbolizerElem.appendChild( strokeElem ); 00184 00185 Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle; 00186 QgsSymbolLayerV2Utils::lineToSld( doc, strokeElem, penStyle, mColor, mWidth, 00187 &mPenJoinStyle, &mPenCapStyle, &mCustomDashVector ); 00188 00189 // <se:PerpendicularOffset> 00190 if ( mOffset != 0 ) 00191 { 00192 QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" ); 00193 perpOffsetElem.appendChild( doc.createTextNode( QString::number( mOffset ) ) ); 00194 symbolizerElem.appendChild( perpOffsetElem ); 00195 } 00196 } 00197 00198 QgsSymbolLayerV2* QgsSimpleLineSymbolLayerV2::createFromSld( QDomElement &element ) 00199 { 00200 QgsDebugMsg( "Entered." ); 00201 00202 QDomElement strokeElem = element.firstChildElement( "Stroke" ); 00203 if ( strokeElem.isNull() ) 00204 return NULL; 00205 00206 Qt::PenStyle penStyle; 00207 QColor color; 00208 double width; 00209 Qt::PenJoinStyle penJoinStyle; 00210 Qt::PenCapStyle penCapStyle; 00211 QVector<qreal> customDashVector; 00212 00213 if ( !QgsSymbolLayerV2Utils::lineFromSld( strokeElem, penStyle, 00214 color, width, 00215 &penJoinStyle, &penCapStyle, 00216 &customDashVector ) ) 00217 return NULL; 00218 00219 double offset = 0.0; 00220 QDomElement perpOffsetElem = element.firstChildElement( "PerpendicularOffset" ); 00221 if ( !perpOffsetElem.isNull() ) 00222 { 00223 bool ok; 00224 double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok ); 00225 if ( ok ) 00226 offset = d; 00227 } 00228 00229 QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle ); 00230 l->setOffset( offset ); 00231 l->setPenJoinStyle( penJoinStyle ); 00232 l->setPenCapStyle( penCapStyle ); 00233 l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine ); 00234 l->setCustomDashVector( customDashVector ); 00235 return l; 00236 } 00237 00238 00239 00241 00242 00243 class MyLine 00244 { 00245 public: 00246 MyLine( QPointF p1, QPointF p2 ) : mVertical( false ), mIncreasing( false ), mT( 0.0 ), mLength( 0.0 ) 00247 { 00248 if ( p1 == p2 ) 00249 return; // invalid 00250 00251 // tangent and direction 00252 if ( p1.x() == p2.x() ) 00253 { 00254 // vertical line - tangent undefined 00255 mVertical = true; 00256 mIncreasing = ( p2.y() > p1.y() ); 00257 } 00258 else 00259 { 00260 mVertical = false; 00261 mT = float( p2.y() - p1.y() ) / ( p2.x() - p1.x() ); 00262 mIncreasing = ( p2.x() > p1.x() ); 00263 } 00264 00265 // length 00266 double x = ( p2.x() - p1.x() ); 00267 double y = ( p2.y() - p1.y() ); 00268 mLength = sqrt( x * x + y * y ); 00269 } 00270 00271 // return angle in radians 00272 double angle() 00273 { 00274 double a = ( mVertical ? M_PI / 2 : atan( mT ) ); 00275 00276 if ( !mIncreasing ) 00277 a += M_PI; 00278 return a; 00279 } 00280 00281 // return difference for x,y when going along the line with specified interval 00282 QPointF diffForInterval( double interval ) 00283 { 00284 if ( mVertical ) 00285 return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) ); 00286 00287 double alpha = atan( mT ); 00288 double dx = cos( alpha ) * interval; 00289 double dy = sin( alpha ) * interval; 00290 return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) ); 00291 } 00292 00293 double length() { return mLength; } 00294 00295 protected: 00296 bool mVertical; 00297 bool mIncreasing; 00298 double mT; 00299 double mLength; 00300 }; 00301 00302 00303 QgsMarkerLineSymbolLayerV2::QgsMarkerLineSymbolLayerV2( bool rotateMarker, double interval ) 00304 { 00305 mRotateMarker = rotateMarker; 00306 mInterval = interval; 00307 mMarker = NULL; 00308 mOffset = 0; 00309 mPlacement = Interval; 00310 00311 setSubSymbol( new QgsMarkerSymbolV2() ); 00312 } 00313 00314 QgsMarkerLineSymbolLayerV2::~QgsMarkerLineSymbolLayerV2() 00315 { 00316 delete mMarker; 00317 } 00318 00319 QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::create( const QgsStringMap& props ) 00320 { 00321 bool rotate = DEFAULT_MARKERLINE_ROTATE; 00322 double interval = DEFAULT_MARKERLINE_INTERVAL; 00323 00324 if ( props.contains( "interval" ) ) 00325 interval = props["interval"].toDouble(); 00326 if ( props.contains( "rotate" ) ) 00327 rotate = ( props["rotate"] == "1" ); 00328 00329 QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotate, interval ); 00330 if ( props.contains( "offset" ) ) 00331 { 00332 x->setOffset( props["offset"].toDouble() ); 00333 } 00334 00335 if ( props.contains( "placement" ) ) 00336 { 00337 if ( props["placement"] == "vertex" ) 00338 x->setPlacement( Vertex ); 00339 else if ( props["placement"] == "lastvertex" ) 00340 x->setPlacement( LastVertex ); 00341 else if ( props["placement"] == "firstvertex" ) 00342 x->setPlacement( FirstVertex ); 00343 else if ( props["placement"] == "centralpoint" ) 00344 x->setPlacement( CentralPoint ); 00345 else 00346 x->setPlacement( Interval ); 00347 } 00348 return x; 00349 } 00350 00351 QString QgsMarkerLineSymbolLayerV2::layerType() const 00352 { 00353 return "MarkerLine"; 00354 } 00355 00356 void QgsMarkerLineSymbolLayerV2::setColor( const QColor& color ) 00357 { 00358 mMarker->setColor( color ); 00359 mColor = color; 00360 } 00361 00362 void QgsMarkerLineSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 00363 { 00364 mMarker->setAlpha( context.alpha() ); 00365 mMarker->setOutputUnit( context.outputUnit() ); 00366 00367 // if being rotated, it gets initialized with every line segment 00368 int hints = 0; 00369 if ( mRotateMarker ) 00370 hints |= QgsSymbolV2::DataDefinedRotation; 00371 if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale ) 00372 hints |= QgsSymbolV2::DataDefinedSizeScale; 00373 mMarker->setRenderHints( hints ); 00374 00375 mMarker->startRender( context.renderContext() ); 00376 } 00377 00378 void QgsMarkerLineSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context ) 00379 { 00380 mMarker->stopRender( context.renderContext() ); 00381 } 00382 00383 void QgsMarkerLineSymbolLayerV2::renderPolyline( const QPolygonF& points, QgsSymbolV2RenderContext& context ) 00384 { 00385 if ( mOffset == 0 ) 00386 { 00387 if ( mPlacement == Interval ) 00388 renderPolylineInterval( points, context ); 00389 else if ( mPlacement == CentralPoint ) 00390 renderPolylineCentral( points, context ); 00391 else 00392 renderPolylineVertex( points, context ); 00393 } 00394 else 00395 { 00396 QPolygonF points2 = ::offsetLine( points, context.outputLineWidth( mOffset ) ); 00397 if ( mPlacement == Interval ) 00398 renderPolylineInterval( points2, context ); 00399 else if ( mPlacement == CentralPoint ) 00400 renderPolylineCentral( points2, context ); 00401 else 00402 renderPolylineVertex( points2, context ); 00403 } 00404 } 00405 00406 void QgsMarkerLineSymbolLayerV2::renderPolylineInterval( const QPolygonF& points, QgsSymbolV2RenderContext& context ) 00407 { 00408 if ( points.isEmpty() ) 00409 return; 00410 00411 QPointF lastPt = points[0]; 00412 double lengthLeft = 0; // how much is left until next marker 00413 bool first = true; 00414 double origAngle = mMarker->angle(); 00415 00416 double painterUnitInterval = context.outputLineWidth( mInterval > 0 ? mInterval : 0.1 ); 00417 00418 QgsRenderContext& rc = context.renderContext(); 00419 00420 for ( int i = 1; i < points.count(); ++i ) 00421 { 00422 const QPointF& pt = points[i]; 00423 00424 if ( lastPt == pt ) // must not be equal! 00425 continue; 00426 00427 // for each line, find out dx and dy, and length 00428 MyLine l( lastPt, pt ); 00429 QPointF diff = l.diffForInterval( painterUnitInterval ); 00430 00431 // if there's some length left from previous line 00432 // use only the rest for the first point in new line segment 00433 double c = 1 - lengthLeft / painterUnitInterval; 00434 00435 lengthLeft += l.length(); 00436 00437 // rotate marker (if desired) 00438 if ( mRotateMarker ) 00439 { 00440 mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) ); 00441 } 00442 00443 // draw first marker 00444 if ( first ) 00445 { 00446 mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() ); 00447 first = false; 00448 } 00449 00450 // while we're not at the end of line segment, draw! 00451 while ( lengthLeft > painterUnitInterval ) 00452 { 00453 // "c" is 1 for regular point or in interval (0,1] for begin of line segment 00454 lastPt += c * diff; 00455 lengthLeft -= painterUnitInterval; 00456 mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() ); 00457 c = 1; // reset c (if wasn't 1 already) 00458 } 00459 00460 lastPt = pt; 00461 } 00462 00463 // restore original rotation 00464 mMarker->setAngle( origAngle ); 00465 00466 } 00467 00468 static double _averageAngle( const QPointF& prevPt, const QPointF& pt, const QPointF& nextPt ) 00469 { 00470 // calc average angle between the previous and next point 00471 double a1 = MyLine( prevPt, pt ).angle(); 00472 double a2 = MyLine( pt, nextPt ).angle(); 00473 double unitX = cos( a1 ) + cos( a2 ), unitY = sin( a1 ) + sin( a2 ); 00474 00475 return atan2( unitY, unitX ); 00476 } 00477 00478 void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points, QgsSymbolV2RenderContext& context ) 00479 { 00480 if ( points.isEmpty() ) 00481 return; 00482 00483 QgsRenderContext& rc = context.renderContext(); 00484 00485 double origAngle = mMarker->angle(); 00486 double angle; 00487 int i, maxCount; 00488 bool isRing = false; 00489 00490 if ( mPlacement == FirstVertex ) 00491 { 00492 i = 0; 00493 maxCount = 1; 00494 } 00495 else if ( mPlacement == LastVertex ) 00496 { 00497 i = points.count() - 1; 00498 maxCount = points.count(); 00499 } 00500 else 00501 { 00502 i = 0; 00503 maxCount = points.count(); 00504 if ( points.first() == points.last() ) 00505 isRing = true; 00506 } 00507 00508 for ( ; i < maxCount; ++i ) 00509 { 00510 const QPointF& pt = points[i]; 00511 00512 // rotate marker (if desired) 00513 if ( mRotateMarker ) 00514 { 00515 if ( i == 0 ) 00516 { 00517 if ( !isRing ) 00518 { 00519 // use first segment's angle 00520 const QPointF& nextPt = points[i+1]; 00521 if ( pt == nextPt ) 00522 continue; 00523 angle = MyLine( pt, nextPt ).angle(); 00524 } 00525 else 00526 { 00527 // closed ring: use average angle between first and last segment 00528 const QPointF& prevPt = points[points.count() - 2]; 00529 const QPointF& nextPt = points[1]; 00530 if ( prevPt == pt || nextPt == pt ) 00531 continue; 00532 00533 angle = _averageAngle( prevPt, pt, nextPt ); 00534 } 00535 } 00536 else if ( i == points.count() - 1 ) 00537 { 00538 if ( !isRing ) 00539 { 00540 // use last segment's angle 00541 const QPointF& prevPt = points[i-1]; 00542 if ( pt == prevPt ) 00543 continue; 00544 angle = MyLine( prevPt, pt ).angle(); 00545 } 00546 else 00547 { 00548 // don't draw the last marker - it has been drawn already 00549 continue; 00550 } 00551 } 00552 else 00553 { 00554 // use average angle 00555 const QPointF& prevPt = points[i-1]; 00556 const QPointF& nextPt = points[i+1]; 00557 if ( prevPt == pt || nextPt == pt ) 00558 continue; 00559 00560 angle = _averageAngle( prevPt, pt, nextPt ); 00561 } 00562 mMarker->setAngle( origAngle + angle * 180 / M_PI ); 00563 } 00564 00565 mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() ); 00566 } 00567 00568 // restore original rotation 00569 mMarker->setAngle( origAngle ); 00570 } 00571 00572 void QgsMarkerLineSymbolLayerV2::renderPolylineCentral( const QPolygonF& points, QgsSymbolV2RenderContext& context ) 00573 { 00574 // calc length 00575 qreal length = 0; 00576 QPolygonF::const_iterator it = points.constBegin(); 00577 QPointF last = *it; 00578 for ( ++it; it != points.constEnd(); ++it ) 00579 { 00580 length += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) + 00581 ( last.y() - it->y() ) * ( last.y() - it->y() ) ); 00582 last = *it; 00583 } 00584 00585 // find the segment where the central point lies 00586 it = points.constBegin(); 00587 last = *it; 00588 qreal last_at = 0, next_at = 0; 00589 QPointF next; 00590 int segment = 0; 00591 for ( ++it; it != points.constEnd(); ++it ) 00592 { 00593 next = *it; 00594 next_at += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) + 00595 ( last.y() - it->y() ) * ( last.y() - it->y() ) ); 00596 if ( next_at >= length / 2 ) 00597 break; // we have reached the center 00598 last = *it; 00599 last_at = next_at; 00600 segment++; 00601 } 00602 00603 // find out the central point on segment 00604 MyLine l( last, next ); // for line angle 00605 qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at ); 00606 QPointF pt = last + ( next - last ) * k; 00607 00608 // draw the marker 00609 double origAngle = mMarker->angle(); 00610 if ( mRotateMarker ) 00611 mMarker->setAngle( origAngle + l.angle() * 180 / M_PI ); 00612 mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() ); 00613 if ( mRotateMarker ) 00614 mMarker->setAngle( origAngle ); 00615 } 00616 00617 00618 QgsStringMap QgsMarkerLineSymbolLayerV2::properties() const 00619 { 00620 QgsStringMap map; 00621 map["rotate"] = ( mRotateMarker ? "1" : "0" ); 00622 map["interval"] = QString::number( mInterval ); 00623 map["offset"] = QString::number( mOffset ); 00624 if ( mPlacement == Vertex ) 00625 map["placement"] = "vertex"; 00626 else if ( mPlacement == LastVertex ) 00627 map["placement"] = "lastvertex"; 00628 else if ( mPlacement == FirstVertex ) 00629 map["placement"] = "firstvertex"; 00630 else if ( mPlacement == CentralPoint ) 00631 map["placement"] = "centralpoint"; 00632 else 00633 map["placement"] = "interval"; 00634 return map; 00635 } 00636 00637 QgsSymbolV2* QgsMarkerLineSymbolLayerV2::subSymbol() 00638 { 00639 return mMarker; 00640 } 00641 00642 bool QgsMarkerLineSymbolLayerV2::setSubSymbol( QgsSymbolV2* symbol ) 00643 { 00644 if ( symbol == NULL || symbol->type() != QgsSymbolV2::Marker ) 00645 { 00646 delete symbol; 00647 return false; 00648 } 00649 00650 delete mMarker; 00651 mMarker = static_cast<QgsMarkerSymbolV2*>( symbol ); 00652 mColor = mMarker->color(); 00653 return true; 00654 } 00655 00656 QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::clone() const 00657 { 00658 QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( mRotateMarker, mInterval ); 00659 x->setSubSymbol( mMarker->clone() ); 00660 x->setOffset( mOffset ); 00661 x->setPlacement( mPlacement ); 00662 return x; 00663 } 00664 00665 void QgsMarkerLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 00666 { 00667 for ( int i = 0; i < mMarker->symbolLayerCount(); i++ ) 00668 { 00669 QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" ); 00670 if ( !props.value( "uom", "" ).isEmpty() ) 00671 symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) ); 00672 element.appendChild( symbolizerElem ); 00673 00674 // <Geometry> 00675 QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) ); 00676 00677 QString gap; 00678 switch ( mPlacement ) 00679 { 00680 case FirstVertex: 00681 symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "firstPoint" ) ); 00682 break; 00683 case LastVertex: 00684 symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) ); 00685 break; 00686 case CentralPoint: 00687 symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "centralPoint" ) ); 00688 break; 00689 case Vertex: 00690 // no way to get line/polygon's vertices, use a VendorOption 00691 symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "points" ) ); 00692 break; 00693 default: 00694 gap = QString::number( mInterval ); 00695 break; 00696 } 00697 00698 if ( !mRotateMarker ) 00699 { 00700 // markers in LineSymbolizer must be drawn following the line orientation, 00701 // use a VendorOption when no marker rotation 00702 symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "rotateMarker", "0" ) ); 00703 } 00704 00705 // <Stroke> 00706 QDomElement strokeElem = doc.createElement( "se:Stroke" ); 00707 symbolizerElem.appendChild( strokeElem ); 00708 00709 // <GraphicStroke> 00710 QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" ); 00711 strokeElem.appendChild( graphicStrokeElem ); 00712 00713 QgsSymbolLayerV2 *layer = mMarker->symbolLayer( i ); 00714 QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer ); 00715 if ( !markerLayer ) 00716 { 00717 graphicStrokeElem.appendChild( doc.createComment( QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( markerLayer->layerType() ) ) ); 00718 } 00719 else 00720 { 00721 markerLayer->writeSldMarker( doc, graphicStrokeElem, props ); 00722 } 00723 00724 if ( !gap.isEmpty() ) 00725 { 00726 QDomElement gapElem = doc.createElement( "se:Gap" ); 00727 QgsSymbolLayerV2Utils::createFunctionElement( doc, gapElem, gap ); 00728 graphicStrokeElem.appendChild( gapElem ); 00729 } 00730 00731 if ( !doubleNear( mOffset, 0.0 ) ) 00732 { 00733 QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" ); 00734 perpOffsetElem.appendChild( doc.createTextNode( QString::number( mOffset ) ) ); 00735 symbolizerElem.appendChild( perpOffsetElem ); 00736 } 00737 } 00738 } 00739 00740 QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::createFromSld( QDomElement &element ) 00741 { 00742 QgsDebugMsg( "Entered." ); 00743 00744 QDomElement strokeElem = element.firstChildElement( "Stroke" ); 00745 if ( strokeElem.isNull() ) 00746 return NULL; 00747 00748 QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" ); 00749 if ( graphicStrokeElem.isNull() ) 00750 return NULL; 00751 00752 // retrieve vendor options 00753 bool rotateMarker = true; 00754 Placement placement = Interval; 00755 00756 QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( element ); 00757 for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it ) 00758 { 00759 if ( it.key() == "placement" ) 00760 { 00761 if ( it.value() == "points" ) placement = Vertex; 00762 else if ( it.value() == "firstPoint" ) placement = FirstVertex; 00763 else if ( it.value() == "lastPoint" ) placement = LastVertex; 00764 else if ( it.value() == "centralPoint" ) placement = CentralPoint; 00765 } 00766 else if ( it.value() == "rotateMarker" ) 00767 { 00768 rotateMarker = it.value() == "0"; 00769 } 00770 } 00771 00772 QgsMarkerSymbolV2 *marker = 0; 00773 00774 QgsSymbolLayerV2 *l = QgsSymbolLayerV2Utils::createMarkerLayerFromSld( graphicStrokeElem ); 00775 if ( l ) 00776 { 00777 QgsSymbolLayerV2List layers; 00778 layers.append( l ); 00779 marker = new QgsMarkerSymbolV2( layers ); 00780 } 00781 00782 double interval = 0.0; 00783 QDomElement gapElem = element.firstChildElement( "Gap" ); 00784 if ( !gapElem.isNull() ) 00785 { 00786 bool ok; 00787 double d = gapElem.firstChild().nodeValue().toDouble( &ok ); 00788 if ( ok ) 00789 interval = d; 00790 } 00791 00792 double offset = 0.0; 00793 QDomElement perpOffsetElem = element.firstChildElement( "PerpendicularOffset" ); 00794 if ( !perpOffsetElem.isNull() ) 00795 { 00796 bool ok; 00797 double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok ); 00798 if ( ok ) 00799 offset = d; 00800 } 00801 00802 QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotateMarker ); 00803 x->setPlacement( placement ); 00804 if ( !doubleNear( interval, 0.0 ) && interval > 0 ) 00805 x->setInterval( interval ); 00806 if ( marker ) 00807 x->setSubSymbol( marker ); 00808 if ( !doubleNear( offset, 0.0 ) ) 00809 x->setOffset( offset ); 00810 return x; 00811 } 00812 00813 void QgsMarkerLineSymbolLayerV2::setWidth( double width ) 00814 { 00815 mMarker->setSize( width ); 00816 } 00817 00818 double QgsMarkerLineSymbolLayerV2::width() const 00819 { 00820 return mMarker->size(); 00821 } 00822 00824 00825 QgsLineDecorationSymbolLayerV2::QgsLineDecorationSymbolLayerV2( QColor color, double width ) 00826 { 00827 mColor = color; 00828 mWidth = width; 00829 } 00830 00831 QgsLineDecorationSymbolLayerV2::~QgsLineDecorationSymbolLayerV2() 00832 { 00833 } 00834 00835 QgsSymbolLayerV2* QgsLineDecorationSymbolLayerV2::create( const QgsStringMap& props ) 00836 { 00837 QColor color = DEFAULT_LINEDECORATION_COLOR; 00838 double width = DEFAULT_LINEDECORATION_WIDTH; 00839 00840 if ( props.contains( "color" ) ) 00841 color = QgsSymbolLayerV2Utils::decodeColor( props["color"] ); 00842 if ( props.contains( "width" ) ) 00843 width = props["width"].toDouble(); 00844 00845 return new QgsLineDecorationSymbolLayerV2( color, width ); 00846 } 00847 00848 QString QgsLineDecorationSymbolLayerV2::layerType() const 00849 { 00850 return "LineDecoration"; 00851 } 00852 00853 void QgsLineDecorationSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context ) 00854 { 00855 QColor penColor = mColor; 00856 penColor.setAlphaF( context.alpha() ); 00857 mPen.setWidth( context.outputLineWidth( mWidth ) ); 00858 mPen.setColor( penColor ); 00859 QColor selColor = context.selectionColor(); 00860 if ( ! selectionIsOpaque ) 00861 selColor.setAlphaF( context.alpha() ); 00862 mSelPen.setWidth( context.outputLineWidth( mWidth ) ); 00863 mSelPen.setColor( selColor ); 00864 } 00865 00866 void QgsLineDecorationSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context ) 00867 { 00868 Q_UNUSED( context ); 00869 } 00870 00871 void QgsLineDecorationSymbolLayerV2::renderPolyline( const QPolygonF& points, QgsSymbolV2RenderContext& context ) 00872 { 00873 // draw arrow at the end of line 00874 00875 QPainter* p = context.renderContext().painter(); 00876 if ( !p ) 00877 { 00878 return; 00879 } 00880 00881 int cnt = points.count(); 00882 if ( cnt < 2 ) 00883 { 00884 return; 00885 } 00886 QPointF p2 = points.at( --cnt ); 00887 QPointF p1 = points.at( --cnt ); 00888 while ( p2 == p1 && cnt ) 00889 p1 = points.at( --cnt ); 00890 if ( p1 == p2 ) 00891 { 00892 // this is a collapsed line... don't bother drawing an arrow 00893 // with arbitrary orientation 00894 return; 00895 } 00896 00897 double angle = atan2( p2.y() - p1.y(), p2.x() - p1.x() ); 00898 double size = context.outputLineWidth( mWidth * 8 ); 00899 double angle1 = angle + M_PI / 6; 00900 double angle2 = angle - M_PI / 6; 00901 00902 QPointF p2_1 = p2 - QPointF( size * cos( angle1 ), size * sin( angle1 ) ); 00903 QPointF p2_2 = p2 - QPointF( size * cos( angle2 ), size * sin( angle2 ) ); 00904 00905 p->setPen( context.selected() ? mSelPen : mPen ); 00906 p->drawLine( p2, p2_1 ); 00907 p->drawLine( p2, p2_2 ); 00908 } 00909 00910 QgsStringMap QgsLineDecorationSymbolLayerV2::properties() const 00911 { 00912 QgsStringMap map; 00913 map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor ); 00914 map["width"] = QString::number( mWidth ); 00915 return map; 00916 } 00917 00918 QgsSymbolLayerV2* QgsLineDecorationSymbolLayerV2::clone() const 00919 { 00920 return new QgsLineDecorationSymbolLayerV2( mColor, mWidth ); 00921 } 00922 00923 void QgsLineDecorationSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const 00924 { 00925 QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" ); 00926 if ( !props.value( "uom", "" ).isEmpty() ) 00927 symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) ); 00928 element.appendChild( symbolizerElem ); 00929 00930 QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom" , "" ) ); 00931 00932 // <Stroke> 00933 QDomElement strokeElem = doc.createElement( "se:Stroke" ); 00934 symbolizerElem.appendChild( strokeElem ); 00935 00936 // <GraphicStroke> 00937 QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" ); 00938 strokeElem.appendChild( graphicStrokeElem ); 00939 00940 // <Graphic> 00941 QDomElement graphicElem = doc.createElement( "se:Graphic" ); 00942 graphicStrokeElem.appendChild( graphicElem ); 00943 00944 // <Mark> 00945 QgsSymbolLayerV2Utils::wellKnownMarkerToSld( doc, graphicElem, "arrowhead", QColor(), mColor, mWidth, mWidth*8 ); 00946 00947 // <Rotation> 00948 QgsSymbolLayerV2Utils::createRotationElement( doc, graphicElem, props.value( "angle", "" ) ); 00949 00950 // use <VendorOption> to draw the decoration at end of the line 00951 symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) ); 00952 }