00001
00002 #include "qgslinesymbollayerv2.h"
00003 #include "qgssymbollayerv2utils.h"
00004
00005 #include "qgsrendercontext.h"
00006
00007 #include <QPainter>
00008
00009 #include <cmath>
00010
00011 QgsSimpleLineSymbolLayerV2::QgsSimpleLineSymbolLayerV2( QColor color, double width, Qt::PenStyle penStyle )
00012 : mPenStyle( penStyle ), mPenJoinStyle( DEFAULT_SIMPLELINE_JOINSTYLE ), mPenCapStyle( DEFAULT_SIMPLELINE_CAPSTYLE ), mOffset( 0 ), mUseCustomDashPattern( false )
00013 {
00014 mColor = color;
00015 mWidth = width;
00016 mCustomDashVector << 5 << 2;
00017 }
00018
00019
00020 QgsSymbolLayerV2* QgsSimpleLineSymbolLayerV2::create( const QgsStringMap& props )
00021 {
00022 QColor color = DEFAULT_SIMPLELINE_COLOR;
00023 double width = DEFAULT_SIMPLELINE_WIDTH;
00024 Qt::PenStyle penStyle = DEFAULT_SIMPLELINE_PENSTYLE;
00025
00026 if ( props.contains( "color" ) )
00027 color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
00028 if ( props.contains( "width" ) )
00029 width = props["width"].toDouble();
00030 if ( props.contains( "penstyle" ) )
00031 penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["penstyle"] );
00032
00033
00034 QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
00035 if ( props.contains( "offset" ) )
00036 l->setOffset( props["offset"].toDouble() );
00037 if ( props.contains( "joinstyle" ) )
00038 l->setPenJoinStyle( QgsSymbolLayerV2Utils::decodePenJoinStyle( props["joinstyle"] ) );
00039 if ( props.contains( "capstyle" ) )
00040 l->setPenCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( props["capstyle"] ) );
00041
00042 if ( props.contains( "use_custom_dash" ) )
00043 {
00044 l->setUseCustomDashPattern( props["use_custom_dash"].toInt() );
00045 }
00046 if ( props.contains( "customdash" ) )
00047 {
00048 l->setCustomDashVector( QgsSymbolLayerV2Utils::decodeRealVector( props["customdash"] ) );
00049 }
00050 return l;
00051 }
00052
00053
00054 QString QgsSimpleLineSymbolLayerV2::layerType() const
00055 {
00056 return "SimpleLine";
00057 }
00058
00059 void QgsSimpleLineSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context )
00060 {
00061 QColor penColor = mColor;
00062 penColor.setAlphaF( context.alpha() );
00063 mPen.setColor( penColor );
00064 double scaledWidth = context.outputLineWidth( mWidth );
00065 mPen.setWidthF( scaledWidth );
00066 if ( mUseCustomDashPattern && scaledWidth != 0 )
00067 {
00068 mPen.setStyle( Qt::CustomDashLine );
00069
00070
00071 QVector<qreal> scaledVector;
00072 QVector<qreal>::const_iterator it = mCustomDashVector.constBegin();
00073 for ( ; it != mCustomDashVector.constEnd(); ++it )
00074 {
00075
00076 scaledVector << context.outputLineWidth(( *it ) / scaledWidth );
00077 }
00078 mPen.setDashPattern( scaledVector );
00079 }
00080 else
00081 {
00082 mPen.setStyle( mPenStyle );
00083 }
00084 mPen.setJoinStyle( mPenJoinStyle );
00085 mPen.setCapStyle( mPenCapStyle );
00086
00087 mSelPen = mPen;
00088 QColor selColor = context.selectionColor();
00089 if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
00090 mSelPen.setColor( selColor );
00091 }
00092
00093 void QgsSimpleLineSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
00094 {
00095 }
00096
00097 void QgsSimpleLineSymbolLayerV2::renderPolyline( const QPolygonF& points, QgsSymbolV2RenderContext& context )
00098 {
00099 QPainter* p = context.renderContext().painter();
00100 if ( !p )
00101 {
00102 return;
00103 }
00104
00105 if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
00106 {
00107 double scaledWidth = context.outputLineWidth( mWidth );
00108 mPen.setWidthF( scaledWidth );
00109 mSelPen.setWidthF( scaledWidth );
00110 }
00111
00112 p->setPen( context.selected() ? mSelPen : mPen );
00113 if ( mOffset == 0 )
00114 {
00115 p->drawPolyline( points );
00116 }
00117 else
00118 {
00119 p->drawPolyline( ::offsetLine( points, mOffset ) );
00120 }
00121 }
00122
00123 QgsStringMap QgsSimpleLineSymbolLayerV2::properties() const
00124 {
00125 QgsStringMap map;
00126 map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
00127 map["width"] = QString::number( mWidth );
00128 map["penstyle"] = QgsSymbolLayerV2Utils::encodePenStyle( mPenStyle );
00129 map["joinstyle"] = QgsSymbolLayerV2Utils::encodePenJoinStyle( mPenJoinStyle );
00130 map["capstyle"] = QgsSymbolLayerV2Utils::encodePenCapStyle( mPenCapStyle );
00131 map["offset"] = QString::number( mOffset );
00132 map["use_custom_dash"] = ( mUseCustomDashPattern ? "1" : "0" );
00133 map["customdash"] = QgsSymbolLayerV2Utils::encodeRealVector( mCustomDashVector );
00134 return map;
00135 }
00136
00137 QgsSymbolLayerV2* QgsSimpleLineSymbolLayerV2::clone() const
00138 {
00139 QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( mColor, mWidth, mPenStyle );
00140 l->setOffset( mOffset );
00141 l->setPenJoinStyle( mPenJoinStyle );
00142 l->setPenCapStyle( mPenCapStyle );
00143 l->setUseCustomDashPattern( mUseCustomDashPattern );
00144 l->setCustomDashVector( mCustomDashVector );
00145 return l;
00146 }
00147
00148
00150
00151
00152 class MyLine
00153 {
00154 public:
00155 MyLine( QPointF p1, QPointF p2 ) : mVertical( false ), mIncreasing( false ), mT( 0.0 ), mLength( 0.0 )
00156 {
00157 if ( p1 == p2 )
00158 return;
00159
00160
00161 if ( p1.x() == p2.x() )
00162 {
00163
00164 mVertical = true;
00165 mIncreasing = ( p2.y() > p1.y() );
00166 }
00167 else
00168 {
00169 mVertical = false;
00170 mT = float( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
00171 mIncreasing = ( p2.x() > p1.x() );
00172 }
00173
00174
00175 double x = ( p2.x() - p1.x() );
00176 double y = ( p2.y() - p1.y() );
00177 mLength = sqrt( x * x + y * y );
00178 }
00179
00180
00181 double angle()
00182 {
00183 double a = ( mVertical ? M_PI / 2 : atan( mT ) );
00184
00185 if ( !mIncreasing )
00186 a += M_PI;
00187 return a;
00188 }
00189
00190
00191 QPointF diffForInterval( double interval )
00192 {
00193 if ( mVertical )
00194 return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
00195
00196 double alpha = atan( mT );
00197 double dx = cos( alpha ) * interval;
00198 double dy = sin( alpha ) * interval;
00199 return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
00200 }
00201
00202 double length() { return mLength; }
00203
00204 protected:
00205 bool mVertical;
00206 bool mIncreasing;
00207 double mT;
00208 double mLength;
00209 };
00210
00211
00212 QgsMarkerLineSymbolLayerV2::QgsMarkerLineSymbolLayerV2( bool rotateMarker, double interval )
00213 {
00214 mRotateMarker = rotateMarker;
00215 mInterval = interval;
00216 mMarker = NULL;
00217 mOffset = 0;
00218
00219 setSubSymbol( new QgsMarkerSymbolV2() );
00220 }
00221
00222 QgsMarkerLineSymbolLayerV2::~QgsMarkerLineSymbolLayerV2()
00223 {
00224 delete mMarker;
00225 }
00226
00227 QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::create( const QgsStringMap& props )
00228 {
00229 bool rotate = DEFAULT_MARKERLINE_ROTATE;
00230 double interval = DEFAULT_MARKERLINE_INTERVAL;
00231
00232 if ( props.contains( "interval" ) )
00233 interval = props["interval"].toDouble();
00234 if ( props.contains( "rotate" ) )
00235 rotate = ( props["rotate"] == "1" );
00236
00237 QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotate, interval );
00238 if ( props.contains( "offset" ) )
00239 x->setOffset( props["offset"].toDouble() );
00240 return x;
00241 }
00242
00243 QString QgsMarkerLineSymbolLayerV2::layerType() const
00244 {
00245 return "MarkerLine";
00246 }
00247
00248 void QgsMarkerLineSymbolLayerV2::setColor( QColor color )
00249 {
00250 mMarker->setColor( color );
00251 mColor = color;
00252 }
00253
00254 void QgsMarkerLineSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context )
00255 {
00256 mMarker->setAlpha( context.alpha() );
00257 mMarker->setOutputUnit( context.outputUnit() );
00258
00259
00260 int hints = 0;
00261 if ( mRotateMarker )
00262 hints |= QgsSymbolV2::DataDefinedRotation;
00263 if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
00264 hints |= QgsSymbolV2::DataDefinedSizeScale;
00265 mMarker->setRenderHints( hints );
00266
00267 mMarker->startRender( context.renderContext() );
00268 }
00269
00270 void QgsMarkerLineSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
00271 {
00272 mMarker->stopRender( context.renderContext() );
00273 }
00274
00275 void QgsMarkerLineSymbolLayerV2::renderPolyline( const QPolygonF& points, QgsSymbolV2RenderContext& context )
00276 {
00277 if ( mOffset == 0 )
00278 {
00279 renderPolylineNoOffset( points, context );
00280 }
00281 else
00282 {
00283 QPolygonF points2 = ::offsetLine( points, context.outputLineWidth( mOffset ) );
00284 renderPolylineNoOffset( points2, context );
00285 }
00286 }
00287
00288 void QgsMarkerLineSymbolLayerV2::renderPolylineNoOffset( const QPolygonF& points, QgsSymbolV2RenderContext& context )
00289 {
00290 QPointF lastPt = points[0];
00291 double lengthLeft = 0;
00292 bool first = true;
00293 double origAngle = mMarker->angle();
00294
00295 double painterUnitInterval = context.outputLineWidth( mInterval > 0 ? mInterval : 0.1 );
00296
00297 QgsRenderContext& rc = context.renderContext();
00298
00299 for ( int i = 1; i < points.count(); ++i )
00300 {
00301 const QPointF& pt = points[i];
00302
00303 if ( lastPt == pt )
00304 continue;
00305
00306
00307 MyLine l( lastPt, pt );
00308 QPointF diff = l.diffForInterval( painterUnitInterval );
00309
00310
00311
00312 double c = 1 - lengthLeft / painterUnitInterval;
00313
00314 lengthLeft += l.length();
00315
00316
00317 if ( mRotateMarker )
00318 {
00319 mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
00320 }
00321
00322
00323 if ( first )
00324 {
00325 mMarker->renderPoint( lastPt, rc, -1, context.selected() );
00326 first = false;
00327 }
00328
00329
00330 while ( lengthLeft > painterUnitInterval )
00331 {
00332
00333 lastPt += c * diff;
00334 lengthLeft -= painterUnitInterval;
00335 mMarker->renderPoint( lastPt, rc, -1, context.selected() );
00336 c = 1;
00337 }
00338
00339 lastPt = pt;
00340 }
00341
00342
00343 mMarker->setAngle( origAngle );
00344
00345 }
00346
00347 QgsStringMap QgsMarkerLineSymbolLayerV2::properties() const
00348 {
00349 QgsStringMap map;
00350 map["rotate"] = ( mRotateMarker ? "1" : "0" );
00351 map["interval"] = QString::number( mInterval );
00352 map["offset"] = QString::number( mOffset );
00353 return map;
00354 }
00355
00356 QgsSymbolV2* QgsMarkerLineSymbolLayerV2::subSymbol()
00357 {
00358 return mMarker;
00359 }
00360
00361 bool QgsMarkerLineSymbolLayerV2::setSubSymbol( QgsSymbolV2* symbol )
00362 {
00363 if ( symbol == NULL || symbol->type() != QgsSymbolV2::Marker )
00364 {
00365 delete symbol;
00366 return false;
00367 }
00368
00369 delete mMarker;
00370 mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
00371 mColor = mMarker->color();
00372 return true;
00373 }
00374
00375 QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::clone() const
00376 {
00377 QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( mRotateMarker, mInterval );
00378 x->setSubSymbol( mMarker->clone() );
00379 x->setOffset( mOffset );
00380 return x;
00381 }
00382
00383 void QgsMarkerLineSymbolLayerV2::setWidth( double width )
00384 {
00385 mMarker->setSize( width );
00386 }
00387
00388 double QgsMarkerLineSymbolLayerV2::width() const
00389 {
00390 return mMarker->size();
00391 }
00392
00394
00395 QgsLineDecorationSymbolLayerV2::QgsLineDecorationSymbolLayerV2( QColor color )
00396 {
00397 mColor = color;
00398 }
00399
00400 QgsLineDecorationSymbolLayerV2::~QgsLineDecorationSymbolLayerV2()
00401 {
00402 }
00403
00404 QgsSymbolLayerV2* QgsLineDecorationSymbolLayerV2::create( const QgsStringMap& props )
00405 {
00406 QColor color = DEFAULT_LINEDECORATION_COLOR;
00407
00408 if ( props.contains( "color" ) )
00409 color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
00410
00411 return new QgsLineDecorationSymbolLayerV2( color );
00412 }
00413
00414 QString QgsLineDecorationSymbolLayerV2::layerType() const
00415 {
00416 return "LineDecoration";
00417 }
00418
00419 void QgsLineDecorationSymbolLayerV2::startRender( QgsSymbolV2RenderContext& context )
00420 {
00421 QColor penColor = mColor;
00422 penColor.setAlphaF( context.alpha() );
00423 mPen.setColor( penColor );
00424 QColor selColor = context.selectionColor();
00425 if ( ! selectionIsOpaque ) selColor.setAlphaF( context.alpha() );
00426 mSelPen.setColor( selColor );
00427 }
00428
00429 void QgsLineDecorationSymbolLayerV2::stopRender( QgsSymbolV2RenderContext& context )
00430 {
00431 }
00432
00433 static double _calculateAngle( double x1, double y1, double x2, double y2 )
00434 {
00435
00436 if ( x1 == x2 )
00437 return M_PI *( y2 >= y1 ? 1 / 2 : 3 / 2 );
00438
00439 double t = ( y2 - y1 ) / ( x2 - x1 );
00440 if ( t >= 0 )
00441 return atan( t ) + ( y2 >= y1 ? 0 : M_PI );
00442 else
00443 return atan( t ) + ( y2 >= y1 ? M_PI : 0 );
00444 }
00445
00446 void QgsLineDecorationSymbolLayerV2::renderPolyline( const QPolygonF& points, QgsSymbolV2RenderContext& context )
00447 {
00448
00449
00450 QPainter* p = context.renderContext().painter();
00451 if ( !p )
00452 {
00453 return;
00454 }
00455
00456 int cnt = points.count();
00457 QPointF p1 = points.at( cnt - 2 );
00458 QPointF p2 = points.at( cnt - 1 );
00459 double angle = _calculateAngle( p1.x(), p1.y(), p2.x(), p2.y() );
00460
00461 double size = context.outputLineWidth( 2 );
00462 double angle1 = angle + M_PI / 6;
00463 double angle2 = angle - M_PI / 6;
00464
00465 QPointF p2_1 = p2 - QPointF( size * cos( angle1 ), size * sin( angle1 ) );
00466 QPointF p2_2 = p2 - QPointF( size * cos( angle2 ), size * sin( angle2 ) );
00467
00468 p->setPen( context.selected() ? mSelPen : mPen );
00469 p->drawLine( p2, p2_1 );
00470 p->drawLine( p2, p2_2 );
00471 }
00472
00473 QgsStringMap QgsLineDecorationSymbolLayerV2::properties() const
00474 {
00475 QgsStringMap map;
00476 map["color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
00477 return map;
00478 }
00479
00480 QgsSymbolLayerV2* QgsLineDecorationSymbolLayerV2::clone() const
00481 {
00482 return new QgsLineDecorationSymbolLayerV2( mColor );
00483 }