Quantum GIS API Documentation  1.8
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.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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines