QGIS API Documentation  2.9.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
qgslinesymbollayerv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslinesymbollayerv2.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgslinesymbollayerv2.h"
17 #include "qgsdxfexport.h"
18 #include "qgssymbollayerv2utils.h"
19 #include "qgsexpression.h"
20 #include "qgsrendercontext.h"
21 #include "qgslogger.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsgeometrysimplifier.h"
24 
25 #include <QPainter>
26 #include <QDomDocument>
27 #include <QDomElement>
28 
29 #include <cmath>
30 
31 QgsSimpleLineSymbolLayerV2::QgsSimpleLineSymbolLayerV2( QColor color, double width, Qt::PenStyle penStyle )
32  : mPenStyle( penStyle )
33  , mPenJoinStyle( DEFAULT_SIMPLELINE_JOINSTYLE )
34  , mPenCapStyle( DEFAULT_SIMPLELINE_CAPSTYLE )
35  , mUseCustomDashPattern( false )
36  , mCustomDashPatternUnit( QgsSymbolV2::MM )
37  , mDrawInsidePolygon( false )
38 {
39  mColor = color;
40  mWidth = width;
41  mCustomDashVector << 5 << 2;
42 }
43 
45 {
47  mWidthUnit = unit;
48  mOffsetUnit = unit;
50 }
51 
53 {
55  if ( mWidthUnit != unit || mOffsetUnit != unit || mCustomDashPatternUnit != unit )
56  {
57  return QgsSymbolV2::Mixed;
58  }
59  return unit;
60 }
61 
63 {
65  mWidthMapUnitScale = scale;
66  mOffsetMapUnitScale = scale;
68 }
69 
71 {
75  {
76  return mWidthMapUnitScale;
77  }
78  return QgsMapUnitScale();
79 }
80 
82 {
86 
87  if ( props.contains( "line_color" ) )
88  {
89  color = QgsSymbolLayerV2Utils::decodeColor( props["line_color"] );
90  }
91  else if ( props.contains( "outline_color" ) )
92  {
93  color = QgsSymbolLayerV2Utils::decodeColor( props["outline_color"] );
94  }
95  else if ( props.contains( "color" ) )
96  {
97  //pre 2.5 projects used "color"
98  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
99  }
100  if ( props.contains( "line_width" ) )
101  {
102  width = props["line_width"].toDouble();
103  }
104  else if ( props.contains( "outline_width" ) )
105  {
106  width = props["outline_width"].toDouble();
107  }
108  else if ( props.contains( "width" ) )
109  {
110  //pre 2.5 projects used "width"
111  width = props["width"].toDouble();
112  }
113  if ( props.contains( "line_style" ) )
114  {
115  penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["line_style"] );
116  }
117  else if ( props.contains( "outline_style" ) )
118  {
119  penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["outline_style"] );
120  }
121  else if ( props.contains( "penstyle" ) )
122  {
123  penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["penstyle"] );
124  }
125 
126  QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
127  if ( props.contains( "line_width_unit" ) )
128  {
129  l->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["line_width_unit"] ) );
130  }
131  else if ( props.contains( "outline_width_unit" ) )
132  {
133  l->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["outline_width_unit"] ) );
134  }
135  else if ( props.contains( "width_unit" ) )
136  {
137  //pre 2.5 projects used "width_unit"
138  l->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["width_unit"] ) );
139  }
140  if ( props.contains( "width_map_unit_scale" ) )
141  l->setWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["width_map_unit_scale"] ) );
142  if ( props.contains( "offset" ) )
143  l->setOffset( props["offset"].toDouble() );
144  if ( props.contains( "offset_unit" ) )
145  l->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
146  if ( props.contains( "offset_map_unit_scale" ) )
147  l->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
148  if ( props.contains( "joinstyle" ) )
150  if ( props.contains( "capstyle" ) )
151  l->setPenCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( props["capstyle"] ) );
152 
153  if ( props.contains( "use_custom_dash" ) )
154  {
155  l->setUseCustomDashPattern( props["use_custom_dash"].toInt() );
156  }
157  if ( props.contains( "customdash" ) )
158  {
160  }
161  if ( props.contains( "customdash_unit" ) )
162  {
163  l->setCustomDashPatternUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["customdash_unit"] ) );
164  }
165  if ( props.contains( "customdash_map_unit_scale" ) )
166  {
167  l->setCustomDashPatternMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["customdash_map_unit_scale"] ) );
168  }
169 
170  if ( props.contains( "draw_inside_polygon" ) )
171  {
172  l->setDrawInsidePolygon( props["draw_inside_polygon"].toInt() );
173  }
174 
175  //data defined properties
176  if ( props.contains( "color_expression" ) )
177  l->setDataDefinedProperty( "color", props["color_expression"] );
178  if ( props.contains( "width_expression" ) )
179  l->setDataDefinedProperty( "width", props["width_expression"] );
180  if ( props.contains( "offset_expression" ) )
181  l->setDataDefinedProperty( "offset", props["offset_expression"] );
182  if ( props.contains( "customdash_expression" ) )
183  l->setDataDefinedProperty( "customdash", props["customdash_expression"] );
184  if ( props.contains( "joinstyle_expression" ) )
185  l->setDataDefinedProperty( "joinstyle", props["joinstyle_expression"] );
186  if ( props.contains( "capstyle_expression" ) )
187  l->setDataDefinedProperty( "capstyle", props["capstyle_expression"] );
188  if ( props.contains( "line_style_expression" ) )
189  l->setDataDefinedProperty( "line_style", props["line_style_expression"] );
190 
191  return l;
192 }
193 
194 
196 {
197  return "SimpleLine";
198 }
199 
201 {
202  QColor penColor = mColor;
203  penColor.setAlphaF( mColor.alphaF() * context.alpha() );
204  mPen.setColor( penColor );
206  mPen.setWidthF( scaledWidth );
207  if ( mUseCustomDashPattern && scaledWidth != 0 )
208  {
209  mPen.setStyle( Qt::CustomDashLine );
210 
211  //scale pattern vector
212  double dashWidthDiv = scaledWidth;
213  //fix dash pattern width in Qt 4.8
214  QStringList versionSplit = QString( qVersion() ).split( "." );
215  if ( versionSplit.size() > 1
216  && versionSplit.at( 1 ).toInt() >= 8
217  && ( scaledWidth * context.renderContext().rasterScaleFactor() ) < 1.0 )
218  {
219  dashWidthDiv = 1.0;
220  }
221  QVector<qreal> scaledVector;
222  QVector<qreal>::const_iterator it = mCustomDashVector.constBegin();
223  for ( ; it != mCustomDashVector.constEnd(); ++it )
224  {
225  //the dash is specified in terms of pen widths, therefore the division
227  }
228  mPen.setDashPattern( scaledVector );
229  }
230  else
231  {
232  mPen.setStyle( mPenStyle );
233  }
234  mPen.setJoinStyle( mPenJoinStyle );
235  mPen.setCapStyle( mPenCapStyle );
236 
237  mSelPen = mPen;
238  QColor selColor = context.renderContext().selectionColor();
239  if ( ! selectionIsOpaque )
240  selColor.setAlphaF( context.alpha() );
241  mSelPen.setColor( selColor );
242 
243  //prepare expressions for data defined properties
244  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
245 }
246 
248 {
249  Q_UNUSED( context );
250 }
251 
252 void QgsSimpleLineSymbolLayerV2::renderPolygonOutline( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
253 {
254  QPainter* p = context.renderContext().painter();
255  if ( !p )
256  {
257  return;
258  }
259 
260  if ( mDrawInsidePolygon )
261  {
262  //only drawing the line on the interior of the polygon, so set clip path for painter
263  p->save();
264  QPainterPath clipPath;
265  clipPath.addPolygon( points );
266 
267  if ( rings != NULL )
268  {
269  //add polygon rings
270  QList<QPolygonF>::const_iterator it = rings->constBegin();
271  for ( ; it != rings->constEnd(); ++it )
272  {
273  QPolygonF ring = *it;
274  clipPath.addPolygon( ring );
275  }
276  }
277 
278  //use intersect mode, as a clip path may already exist (eg, for composer maps)
279  p->setClipPath( clipPath, Qt::IntersectClip );
280  }
281 
282  renderPolyline( points, context );
283  if ( rings )
284  {
285  mOffset = -mOffset; // invert the offset for rings!
286  foreach ( const QPolygonF& ring, *rings )
287  renderPolyline( ring, context );
288  mOffset = -mOffset;
289  }
290 
291  if ( mDrawInsidePolygon )
292  {
293  //restore painter to reset clip path
294  p->restore();
295  }
296 
297 }
298 
300 {
301  QPainter* p = context.renderContext().painter();
302  if ( !p )
303  {
304  return;
305  }
306 
307  //size scaling by field
309  {
310  applySizeScale( context, mPen, mSelPen );
311  }
312 
313  double offset = mOffset;
314  applyDataDefinedSymbology( context, mPen, mSelPen, offset );
315 
316  p->setPen( context.selected() ? mSelPen : mPen );
317 
318  // Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #2 points).
319  if ( points.size() <= 2 &&
322  ( p->renderHints() & QPainter::Antialiasing ) )
323  {
324  p->setRenderHint( QPainter::Antialiasing, false );
325  p->drawPolyline( points );
326  p->setRenderHint( QPainter::Antialiasing, true );
327  return;
328  }
329 
330  if ( qgsDoubleNear( offset, 0 ) )
331  {
332  p->drawPolyline( points );
333  }
334  else
335  {
337  QList<QPolygonF> mline = ::offsetLine( points, scaledOffset, context.feature() ? context.feature()->geometry()->type() : QGis::Line );
338  for ( int part = 0; part < mline.count(); ++part )
339  p->drawPolyline( mline[ part ] );
340  }
341 }
342 
344 {
345  QgsStringMap map;
346  map["line_color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
347  map["line_width"] = QString::number( mWidth );
348  map["line_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mWidthUnit );
349  map["width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mWidthMapUnitScale );
350  map["line_style"] = QgsSymbolLayerV2Utils::encodePenStyle( mPenStyle );
353  map["offset"] = QString::number( mOffset );
355  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
356  map["use_custom_dash"] = ( mUseCustomDashPattern ? "1" : "0" );
360  map["draw_inside_polygon"] = ( mDrawInsidePolygon ? "1" : "0" );
362  return map;
363 }
364 
366 {
368  l->setWidthUnit( mWidthUnit );
374  l->setOffset( mOffset );
381  copyPaintEffect( l );
382  return l;
383 }
384 
385 void QgsSimpleLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
386 {
387  if ( mPenStyle == Qt::NoPen )
388  return;
389 
390  QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
391  if ( !props.value( "uom", "" ).isEmpty() )
392  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
393  element.appendChild( symbolizerElem );
394 
395  // <Geometry>
396  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
397 
398  // <Stroke>
399  QDomElement strokeElem = doc.createElement( "se:Stroke" );
400  symbolizerElem.appendChild( strokeElem );
401 
402  Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
403  QgsSymbolLayerV2Utils::lineToSld( doc, strokeElem, penStyle, mColor, mWidth,
405 
406  // <se:PerpendicularOffset>
407  if ( mOffset != 0 )
408  {
409  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
410  perpOffsetElem.appendChild( doc.createTextNode( QString::number( mOffset ) ) );
411  symbolizerElem.appendChild( perpOffsetElem );
412  }
413 }
414 
415 QString QgsSimpleLineSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
416 {
417  if ( mUseCustomDashPattern )
418  {
419  return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor,
420  mPen.color(), mPenJoinStyle,
422  }
423  else
424  {
425  return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor, mPen.color(), mPenJoinStyle,
427  }
428 }
429 
431 {
432  QgsDebugMsg( "Entered." );
433 
434  QDomElement strokeElem = element.firstChildElement( "Stroke" );
435  if ( strokeElem.isNull() )
436  return NULL;
437 
438  Qt::PenStyle penStyle;
439  QColor color;
440  double width;
441  Qt::PenJoinStyle penJoinStyle;
442  Qt::PenCapStyle penCapStyle;
443  QVector<qreal> customDashVector;
444 
445  if ( !QgsSymbolLayerV2Utils::lineFromSld( strokeElem, penStyle,
446  color, width,
447  &penJoinStyle, &penCapStyle,
448  &customDashVector ) )
449  return NULL;
450 
451  double offset = 0.0;
452  QDomElement perpOffsetElem = element.firstChildElement( "PerpendicularOffset" );
453  if ( !perpOffsetElem.isNull() )
454  {
455  bool ok;
456  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
457  if ( ok )
458  offset = d;
459  }
460 
461  QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
462  l->setOffset( offset );
463  l->setPenJoinStyle( penJoinStyle );
464  l->setPenCapStyle( penCapStyle );
465  l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine );
466  l->setCustomDashVector( customDashVector );
467  return l;
468 }
469 
470 void QgsSimpleLineSymbolLayerV2::applySizeScale( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen )
471 {
473  pen.setWidthF( scaledWidth );
474  selPen.setWidthF( scaledWidth );
475 }
476 
477 void QgsSimpleLineSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen, double& offset )
478 {
479  if ( mDataDefinedProperties.isEmpty() )
480  return; // shortcut
481 
482  //data defined properties
483  QgsExpression* strokeWidthExpression = expression( "width" );
484  if ( strokeWidthExpression )
485  {
486  double scaledWidth = strokeWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble()
488  pen.setWidthF( scaledWidth );
489  selPen.setWidthF( scaledWidth );
490  }
491 
492  //color
493  QgsExpression* strokeColorExpression = expression( "color" );
494  if ( strokeColorExpression )
495  {
496  pen.setColor( QgsSymbolLayerV2Utils::decodeColor( strokeColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
497  }
498 
499  //offset
500  QgsExpression* lineOffsetExpression = expression( "offset" );
501  if ( lineOffsetExpression )
502  {
503  offset = lineOffsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
504  }
505 
506  //dash dot vector
507  QgsExpression* dashPatternExpression = expression( "customdash" );
508  if ( dashPatternExpression )
509  {
511  double dashWidthDiv = mPen.widthF();
512 
513  if ( strokeWidthExpression )
514  {
515  dashWidthDiv = pen.widthF();
516  scaledWidth = pen.widthF();
517  }
518 
519  //fix dash pattern width in Qt 4.8
520  QStringList versionSplit = QString( qVersion() ).split( "." );
521  if ( versionSplit.size() > 1
522  && versionSplit.at( 1 ).toInt() >= 8
523  && ( scaledWidth * context.renderContext().rasterScaleFactor() ) < 1.0 )
524  {
525  dashWidthDiv = 1.0;
526  }
527 
528  QVector<qreal> dashVector;
529  QStringList dashList = dashPatternExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString().split( ";" );
530  QStringList::const_iterator dashIt = dashList.constBegin();
531  for ( ; dashIt != dashList.constEnd(); ++dashIt )
532  {
533  dashVector.push_back( dashIt->toDouble() * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mCustomDashPatternUnit, mCustomDashPatternMapUnitScale ) / dashWidthDiv );
534  }
535  pen.setDashPattern( dashVector );
536  }
537 
538  //line style
539  QgsExpression* lineStyleExpression = expression( "line_style" );
540  if ( lineStyleExpression )
541  {
542  QString lineStyleString = lineStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
543  pen.setStyle( QgsSymbolLayerV2Utils::decodePenStyle( lineStyleString ) );
544  }
545 
546  //join style
547  QgsExpression* joinStyleExpression = expression( "joinstyle" );
548  if ( joinStyleExpression )
549  {
550  QString joinStyleString = joinStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
551  pen.setJoinStyle( QgsSymbolLayerV2Utils::decodePenJoinStyle( joinStyleString ) );
552  }
553 
554  //cap style
555  QgsExpression* capStyleExpression = expression( "capstyle" );
556  if ( capStyleExpression )
557  {
558  QString capStyleString = capStyleExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
559  pen.setCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( capStyleString ) );
560  }
561 }
562 
564 {
565  if ( mDrawInsidePolygon )
566  {
567  //set to clip line to the interior of polygon, so we expect no bleed
568  return 0;
569  }
570  else
571  {
572  return ( mWidth / 2.0 ) + mOffset;
573  }
574 }
575 
577 {
578  unit = mCustomDashPatternUnit;
579  return mUseCustomDashPattern ? mCustomDashVector : QVector<qreal>();
580 }
581 
583 {
584  return mPenStyle;
585 }
586 
588 {
589  double width = mWidth;
590  QgsExpression* strokeWidthExpression = expression( "width" );
591  if ( strokeWidthExpression )
592  {
593  width = strokeWidthExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble() * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), widthUnit(), e.mapUnits() );
594  }
595  else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
596  {
598  }
599 
600  return width * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), widthUnit(), e.mapUnits() );
601 }
602 
604 {
605  QgsExpression* strokeColorExpression = expression( "color" );
606  if ( strokeColorExpression )
607  {
608  return ( QgsSymbolLayerV2Utils::decodeColor( strokeColorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() ) );
609  }
610  return mColor;
611 }
612 
614 {
615  Q_UNUSED( e );
616  double offset = mOffset;
617  QgsExpression* offsetExpression = expression( "offset" );
618  if ( offsetExpression )
619  {
620  offset = offsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
621  }
622  return offset;
623 }
624 
626 
627 
628 class MyLine
629 {
630  public:
631  MyLine( QPointF p1, QPointF p2 ) : mVertical( false ), mIncreasing( false ), mT( 0.0 ), mLength( 0.0 )
632  {
633  if ( p1 == p2 )
634  return; // invalid
635 
636  // tangent and direction
637  if ( p1.x() == p2.x() )
638  {
639  // vertical line - tangent undefined
640  mVertical = true;
641  mIncreasing = ( p2.y() > p1.y() );
642  }
643  else
644  {
645  mVertical = false;
646  mT = float( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
647  mIncreasing = ( p2.x() > p1.x() );
648  }
649 
650  // length
651  double x = ( p2.x() - p1.x() );
652  double y = ( p2.y() - p1.y() );
653  mLength = sqrt( x * x + y * y );
654  }
655 
656  // return angle in radians
657  double angle()
658  {
659  double a = ( mVertical ? M_PI / 2 : atan( mT ) );
660 
661  if ( !mIncreasing )
662  a += M_PI;
663  return a;
664  }
665 
666  // return difference for x,y when going along the line with specified interval
667  QPointF diffForInterval( double interval )
668  {
669  if ( mVertical )
670  return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
671 
672  double alpha = atan( mT );
673  double dx = cos( alpha ) * interval;
674  double dy = sin( alpha ) * interval;
675  return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
676  }
677 
678  double length() { return mLength; }
679 
680  protected:
681  bool mVertical;
683  double mT;
684  double mLength;
685 };
686 
687 
688 QgsMarkerLineSymbolLayerV2::QgsMarkerLineSymbolLayerV2( bool rotateMarker, double interval )
689 {
693  mMarker = NULL;
695  mOffsetAlongLine = 0;
697 
699 }
700 
702 {
703  delete mMarker;
704 }
705 
707 {
708  bool rotate = DEFAULT_MARKERLINE_ROTATE;
710 
711 
712  if ( props.contains( "interval" ) )
713  interval = props["interval"].toDouble();
714  if ( props.contains( "rotate" ) )
715  rotate = ( props["rotate"] == "1" );
716 
717  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotate, interval );
718  if ( props.contains( "offset" ) )
719  {
720  x->setOffset( props["offset"].toDouble() );
721  }
722  if ( props.contains( "offset_unit" ) )
723  {
724  x->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
725  }
726  if ( props.contains( "interval_unit" ) )
727  {
728  x->setIntervalUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["interval_unit"] ) );
729  }
730  if ( props.contains( "offset_along_line" ) )
731  {
732  x->setOffsetAlongLine( props["offset_along_line"].toDouble() );
733  }
734  if ( props.contains( "offset_along_line_unit" ) )
735  {
736  x->setOffsetAlongLineUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_along_line_unit"] ) );
737  }
738  if ( props.contains(( "offset_along_line_map_unit_scale" ) ) )
739  {
740  x->setOffsetAlongLineMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_along_line_map_unit_scale"] ) );
741  }
742 
743  if ( props.contains( "offset_map_unit_scale" ) )
744  {
745  x->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
746  }
747  if ( props.contains( "interval_map_unit_scale" ) )
748  {
749  x->setIntervalMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["interval_map_unit_scale"] ) );
750  }
751 
752  if ( props.contains( "placement" ) )
753  {
754  if ( props["placement"] == "vertex" )
755  x->setPlacement( Vertex );
756  else if ( props["placement"] == "lastvertex" )
757  x->setPlacement( LastVertex );
758  else if ( props["placement"] == "firstvertex" )
760  else if ( props["placement"] == "centralpoint" )
762  else
763  x->setPlacement( Interval );
764  }
765 
766  //data defined properties
767  if ( props.contains( "interval_expression" ) )
768  {
769  x->setDataDefinedProperty( "interval", props["interval_expression"] );
770  }
771  if ( props.contains( "offset_expression" ) )
772  {
773  x->setDataDefinedProperty( "offset", props["offset_expression"] );
774  }
775  if ( props.contains( "placement_expression" ) )
776  {
777  x->setDataDefinedProperty( "placement", props["placement_expression"] );
778  }
779  if ( props.contains( "offset_along_line_expression" ) )
780  {
781  x->setDataDefinedProperty( "offset_along_line", props["offset_along_line_expression"] );
782  }
783 
784  return x;
785 }
786 
788 {
789  return "MarkerLine";
790 }
791 
792 void QgsMarkerLineSymbolLayerV2::setColor( const QColor& color )
793 {
794  mMarker->setColor( color );
795  mColor = color;
796 }
797 
799 {
800  mMarker->setAlpha( context.alpha() );
801 
802  // if being rotated, it gets initialized with every line segment
803  int hints = 0;
804  if ( mRotateMarker )
808  mMarker->setRenderHints( hints );
809 
810  mMarker->startRender( context.renderContext(), context.fields() );
811 
812  //prepare expressions for data defined properties
813  prepareExpressions( context.fields(), context.renderContext().rendererScale() );
814 }
815 
817 {
818  mMarker->stopRender( context.renderContext() );
819 }
820 
822 {
823  double offset = mOffset;
824  QgsExpression* offsetExpression = expression( "offset" );
825  if ( offsetExpression )
826  {
827  offset = offsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
828  }
829 
831  QgsExpression* placementExpression = expression( "placement" );
832  if ( placementExpression )
833  {
834  QString placementString = placementExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString();
835  if ( placementString.compare( "vertex", Qt::CaseInsensitive ) == 0 )
836  {
837  placement = Vertex;
838  }
839  else if ( placementString.compare( "lastvertex", Qt::CaseInsensitive ) == 0 )
840  {
841  placement = LastVertex;
842  }
843  else if ( placementString.compare( "firstvertex", Qt::CaseInsensitive ) == 0 )
844  {
845  placement = FirstVertex;
846  }
847  else if ( placementString.compare( "centerpoint", Qt::CaseInsensitive ) == 0 )
848  {
849  placement = CentralPoint;
850  }
851  else
852  {
853  placement = Interval;
854  }
855  }
856 
857  if ( offset == 0 )
858  {
859  if ( placement == Interval )
860  renderPolylineInterval( points, context );
861  else if ( placement == CentralPoint )
862  renderPolylineCentral( points, context );
863  else
864  renderPolylineVertex( points, context, placement );
865  }
866  else
867  {
868  QList<QPolygonF> mline = ::offsetLine( points, offset * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), mOffsetUnit, mOffsetMapUnitScale ), context.feature() ? context.feature()->geometry()->type() : QGis::Line );
869 
870  for ( int part = 0; part < mline.count(); ++part )
871  {
872  const QPolygonF &points2 = mline[ part ];
873 
874  if ( placement == Interval )
875  renderPolylineInterval( points2, context );
876  else if ( placement == CentralPoint )
877  renderPolylineCentral( points2, context );
878  else
879  renderPolylineVertex( points2, context, placement );
880  }
881  }
882 }
883 
884 void QgsMarkerLineSymbolLayerV2::renderPolygonOutline( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
885 {
886  renderPolyline( points, context );
887  if ( rings )
888  {
889  mOffset = -mOffset; // invert the offset for rings!
890  foreach ( const QPolygonF& ring, *rings )
891  renderPolyline( ring, context );
892  mOffset = -mOffset;
893  }
894 }
895 
897 {
898  if ( points.isEmpty() )
899  return;
900 
901  QPointF lastPt = points[0];
902  double lengthLeft = 0; // how much is left until next marker
903  bool first = mOffsetAlongLine ? false : true; //only draw marker at first vertex when no offset along line is set
904  double origAngle = mMarker->angle();
905 
906  QgsRenderContext& rc = context.renderContext();
907  double interval = mInterval;
908 
909  QgsExpression* intervalExpression = expression( "interval" );
910  if ( intervalExpression )
911  {
912  interval = intervalExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
913  }
914  if ( interval <= 0 )
915  {
916  interval = 0.1;
917  }
919  QgsExpression* offsetAlongLineExpression = expression( "offset_along_line" );
920  if ( offsetAlongLineExpression )
921  {
922  offsetAlongLine = offsetAlongLineExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
923  }
924 
925  double painterUnitInterval = interval * QgsSymbolLayerV2Utils::lineWidthScaleFactor( rc, mIntervalUnit, mIntervalMapUnitScale );
926  lengthLeft = painterUnitInterval - offsetAlongLine * QgsSymbolLayerV2Utils::lineWidthScaleFactor( rc, mIntervalUnit, mIntervalMapUnitScale );
927 
928  for ( int i = 1; i < points.count(); ++i )
929  {
930  const QPointF& pt = points[i];
931 
932  if ( lastPt == pt ) // must not be equal!
933  continue;
934 
935  // for each line, find out dx and dy, and length
936  MyLine l( lastPt, pt );
937  QPointF diff = l.diffForInterval( painterUnitInterval );
938 
939  // if there's some length left from previous line
940  // use only the rest for the first point in new line segment
941  double c = 1 - lengthLeft / painterUnitInterval;
942 
943  lengthLeft += l.length();
944 
945  // rotate marker (if desired)
946  if ( mRotateMarker )
947  {
948  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
949  }
950 
951  // draw first marker
952  if ( first )
953  {
954  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
955  first = false;
956  }
957 
958  // while we're not at the end of line segment, draw!
959  while ( lengthLeft > painterUnitInterval )
960  {
961  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
962  lastPt += c * diff;
963  lengthLeft -= painterUnitInterval;
964  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
965  c = 1; // reset c (if wasn't 1 already)
966  }
967 
968  lastPt = pt;
969  }
970 
971  // restore original rotation
972  mMarker->setAngle( origAngle );
973 
974 }
975 
976 static double _averageAngle( const QPointF& prevPt, const QPointF& pt, const QPointF& nextPt )
977 {
978  // calc average angle between the previous and next point
979  double a1 = MyLine( prevPt, pt ).angle();
980  double a2 = MyLine( pt, nextPt ).angle();
981  double unitX = cos( a1 ) + cos( a2 ), unitY = sin( a1 ) + sin( a2 );
982 
983  return atan2( unitY, unitX );
984 }
985 
986 void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points, QgsSymbolV2RenderContext& context, Placement placement )
987 {
988  if ( points.isEmpty() )
989  return;
990 
991  QgsRenderContext& rc = context.renderContext();
992 
993  double origAngle = mMarker->angle();
994  int i, maxCount;
995  bool isRing = false;
996 
998  QgsExpression* offsetAlongLineExpression = expression( "offset_along_line" );
999  if ( offsetAlongLineExpression )
1000  {
1001  offsetAlongLine = offsetAlongLineExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble();
1002  }
1003  if ( offsetAlongLine != 0 )
1004  {
1005  //scale offset along line
1007  }
1008 
1009  if ( placement == FirstVertex )
1010  {
1011  i = 0;
1012  maxCount = 1;
1013  }
1014  else if ( placement == LastVertex )
1015  {
1016  i = points.count() - 1;
1017  maxCount = points.count();
1018  }
1019  else
1020  {
1021  i = 0;
1022  maxCount = points.count();
1023  if ( points.first() == points.last() )
1024  isRing = true;
1025  }
1026 
1027  if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
1028  {
1029  double distance;
1030  distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine;
1031  renderOffsetVertexAlongLine( points, i, distance, context );
1032  // restore original rotation
1033  mMarker->setAngle( origAngle );
1034  return;
1035  }
1036 
1037  for ( ; i < maxCount; ++i )
1038  {
1039  if ( isRing && placement == Vertex && i == points.count() - 1 )
1040  {
1041  continue; // don't draw the last marker - it has been drawn already
1042  }
1043  // rotate marker (if desired)
1044  if ( mRotateMarker )
1045  {
1046  double angle = markerAngle( points, isRing, i );
1047  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1048  }
1049 
1050  mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() );
1051  }
1052 
1053  // restore original rotation
1054  mMarker->setAngle( origAngle );
1055 }
1056 
1057 double QgsMarkerLineSymbolLayerV2::markerAngle( const QPolygonF& points, bool isRing, int vertex )
1058 {
1059  double angle = 0;
1060  const QPointF& pt = points[vertex];
1061 
1062  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1063  {
1064  int prevIndex = vertex - 1;
1065  int nextIndex = vertex + 1;
1066 
1067  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1068  {
1069  prevIndex = points.count() - 2;
1070  nextIndex = 1;
1071  }
1072 
1073  QPointF prevPoint, nextPoint;
1074  while ( prevIndex >= 0 )
1075  {
1076  prevPoint = points[ prevIndex ];
1077  if ( prevPoint != pt )
1078  {
1079  break;
1080  }
1081  --prevIndex;
1082  }
1083 
1084  while ( nextIndex < points.count() )
1085  {
1086  nextPoint = points[ nextIndex ];
1087  if ( nextPoint != pt )
1088  {
1089  break;
1090  }
1091  ++nextIndex;
1092  }
1093 
1094  if ( prevIndex >= 0 && nextIndex < points.count() )
1095  {
1096  angle = _averageAngle( prevPoint, pt, nextPoint );
1097  }
1098  }
1099  else //no ring and vertex is at start / at end
1100  {
1101  if ( vertex == 0 )
1102  {
1103  while ( vertex < points.size() - 1 )
1104  {
1105  const QPointF& nextPt = points[vertex+1];
1106  if ( pt != nextPt )
1107  {
1108  angle = MyLine( pt, nextPt ).angle();
1109  return angle;
1110  }
1111  ++vertex;
1112  }
1113  }
1114  else
1115  {
1116  // use last segment's angle
1117  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1118  {
1119  const QPointF& prevPt = points[vertex-1];
1120  if ( pt != prevPt )
1121  {
1122  angle = MyLine( prevPt, pt ).angle();
1123  return angle;
1124  }
1125  --vertex;
1126  }
1127  }
1128  }
1129  return angle;
1130 }
1131 
1132 void QgsMarkerLineSymbolLayerV2::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolV2RenderContext& context )
1133 {
1134  if ( points.isEmpty() )
1135  return;
1136 
1137  QgsRenderContext& rc = context.renderContext();
1138  double origAngle = mMarker->angle();
1139  if ( distance == 0 )
1140  {
1141  // rotate marker (if desired)
1142  if ( mRotateMarker )
1143  {
1144  bool isRing = false;
1145  if ( points.first() == points.last() )
1146  isRing = true;
1147  double angle = markerAngle( points, isRing, vertex );
1148  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1149  }
1150  mMarker->renderPoint( points[vertex], context.feature(), rc, -1, context.selected() );
1151  return;
1152  }
1153 
1154  int pointIncrement = distance > 0 ? 1 : -1;
1155  QPointF previousPoint = points[vertex];
1156  int startPoint = distance > 0 ? qMin( vertex + 1, points.count() - 1 ) : qMax( vertex - 1, 0 );
1157  int endPoint = distance > 0 ? points.count() - 1 : 0;
1158  double distanceLeft = qAbs( distance );
1159 
1160  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1161  {
1162  const QPointF& pt = points[i];
1163 
1164  if ( previousPoint == pt ) // must not be equal!
1165  continue;
1166 
1167  // create line segment
1168  MyLine l( previousPoint, pt );
1169 
1170  if ( distanceLeft < l.length() )
1171  {
1172  //destination point is in current segment
1173  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1174  // rotate marker (if desired)
1175  if ( mRotateMarker )
1176  {
1177  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1178  }
1179  mMarker->renderPoint( markerPoint, context.feature(), rc, -1, context.selected() );
1180  return;
1181  }
1182 
1183  distanceLeft -= l.length();
1184  previousPoint = pt;
1185  }
1186 
1187  //didn't find point
1188  return;
1189 }
1190 
1192 {
1193  if ( points.size() > 0 )
1194  {
1195  // calc length
1196  qreal length = 0;
1197  QPolygonF::const_iterator it = points.constBegin();
1198  QPointF last = *it;
1199  for ( ++it; it != points.constEnd(); ++it )
1200  {
1201  length += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1202  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1203  last = *it;
1204  }
1205 
1206  // find the segment where the central point lies
1207  it = points.constBegin();
1208  last = *it;
1209  qreal last_at = 0, next_at = 0;
1210  QPointF next;
1211  int segment = 0;
1212  for ( ++it; it != points.constEnd(); ++it )
1213  {
1214  next = *it;
1215  next_at += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1216  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1217  if ( next_at >= length / 2 )
1218  break; // we have reached the center
1219  last = *it;
1220  last_at = next_at;
1221  segment++;
1222  }
1223 
1224  // find out the central point on segment
1225  MyLine l( last, next ); // for line angle
1226  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1227  QPointF pt = last + ( next - last ) * k;
1228 
1229  // draw the marker
1230  double origAngle = mMarker->angle();
1231  if ( mRotateMarker )
1232  mMarker->setAngle( origAngle + l.angle() * 180 / M_PI );
1233  mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() );
1234  if ( mRotateMarker )
1235  mMarker->setAngle( origAngle );
1236  }
1237 }
1238 
1239 
1241 {
1242  QgsStringMap map;
1243  map["rotate"] = ( mRotateMarker ? "1" : "0" );
1244  map["interval"] = QString::number( mInterval );
1245  map["offset"] = QString::number( mOffset );
1246  map["offset_along_line"] = QString::number( mOffsetAlongLine );
1247  map["offset_along_line_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetAlongLineUnit );
1248  map["offset_along_line_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetAlongLineMapUnitScale );
1249  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1250  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1251  map["interval_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mIntervalUnit );
1252  map["interval_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mIntervalMapUnitScale );
1253  if ( mPlacement == Vertex )
1254  map["placement"] = "vertex";
1255  else if ( mPlacement == LastVertex )
1256  map["placement"] = "lastvertex";
1257  else if ( mPlacement == FirstVertex )
1258  map["placement"] = "firstvertex";
1259  else if ( mPlacement == CentralPoint )
1260  map["placement"] = "centralpoint";
1261  else
1262  map["placement"] = "interval";
1263 
1265  return map;
1266 }
1267 
1269 {
1270  return mMarker;
1271 }
1272 
1274 {
1275  if ( symbol == NULL || symbol->type() != QgsSymbolV2::Marker )
1276  {
1277  delete symbol;
1278  return false;
1279  }
1280 
1281  delete mMarker;
1282  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
1283  mColor = mMarker->color();
1284  return true;
1285 }
1286 
1288 {
1290  x->setSubSymbol( mMarker->clone() );
1291  x->setOffset( mOffset );
1292  x->setPlacement( mPlacement );
1293  x->setOffsetUnit( mOffsetUnit );
1301  copyPaintEffect( x );
1302  return x;
1303 }
1304 
1305 void QgsMarkerLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
1306 {
1307  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1308  {
1309  QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
1310  if ( !props.value( "uom", "" ).isEmpty() )
1311  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
1312  element.appendChild( symbolizerElem );
1313 
1314  // <Geometry>
1315  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
1316 
1317  QString gap;
1318  switch ( mPlacement )
1319  {
1320  case FirstVertex:
1321  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "firstPoint" ) );
1322  break;
1323  case LastVertex:
1324  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) );
1325  break;
1326  case CentralPoint:
1327  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "centralPoint" ) );
1328  break;
1329  case Vertex:
1330  // no way to get line/polygon's vertices, use a VendorOption
1331  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "points" ) );
1332  break;
1333  default:
1334  gap = QString::number( mInterval );
1335  break;
1336  }
1337 
1338  if ( !mRotateMarker )
1339  {
1340  // markers in LineSymbolizer must be drawn following the line orientation,
1341  // use a VendorOption when no marker rotation
1342  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "rotateMarker", "0" ) );
1343  }
1344 
1345  // <Stroke>
1346  QDomElement strokeElem = doc.createElement( "se:Stroke" );
1347  symbolizerElem.appendChild( strokeElem );
1348 
1349  // <GraphicStroke>
1350  QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" );
1351  strokeElem.appendChild( graphicStrokeElem );
1352 
1353  QgsSymbolLayerV2 *layer = mMarker->symbolLayer( i );
1354  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
1355  if ( !markerLayer )
1356  {
1357  graphicStrokeElem.appendChild( doc.createComment( QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() ) ) );
1358  }
1359  else
1360  {
1361  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1362  }
1363 
1364  if ( !gap.isEmpty() )
1365  {
1366  QDomElement gapElem = doc.createElement( "se:Gap" );
1367  QgsSymbolLayerV2Utils::createFunctionElement( doc, gapElem, gap );
1368  graphicStrokeElem.appendChild( gapElem );
1369  }
1370 
1371  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1372  {
1373  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
1374  perpOffsetElem.appendChild( doc.createTextNode( QString::number( mOffset ) ) );
1375  symbolizerElem.appendChild( perpOffsetElem );
1376  }
1377  }
1378 }
1379 
1381 {
1382  QgsDebugMsg( "Entered." );
1383 
1384  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1385  if ( strokeElem.isNull() )
1386  return NULL;
1387 
1388  QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
1389  if ( graphicStrokeElem.isNull() )
1390  return NULL;
1391 
1392  // retrieve vendor options
1393  bool rotateMarker = true;
1395 
1396  QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( element );
1397  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1398  {
1399  if ( it.key() == "placement" )
1400  {
1401  if ( it.value() == "points" ) placement = Vertex;
1402  else if ( it.value() == "firstPoint" ) placement = FirstVertex;
1403  else if ( it.value() == "lastPoint" ) placement = LastVertex;
1404  else if ( it.value() == "centralPoint" ) placement = CentralPoint;
1405  }
1406  else if ( it.value() == "rotateMarker" )
1407  {
1408  rotateMarker = it.value() == "0";
1409  }
1410  }
1411 
1412  QgsMarkerSymbolV2 *marker = 0;
1413 
1415  if ( l )
1416  {
1417  QgsSymbolLayerV2List layers;
1418  layers.append( l );
1419  marker = new QgsMarkerSymbolV2( layers );
1420  }
1421 
1422  if ( !marker )
1423  return NULL;
1424 
1425  double interval = 0.0;
1426  QDomElement gapElem = graphicStrokeElem.firstChildElement( "Gap" );
1427  if ( !gapElem.isNull() )
1428  {
1429  bool ok;
1430  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1431  if ( ok )
1432  interval = d;
1433  }
1434 
1435  double offset = 0.0;
1436  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( "PerpendicularOffset" );
1437  if ( !perpOffsetElem.isNull() )
1438  {
1439  bool ok;
1440  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1441  if ( ok )
1442  offset = d;
1443  }
1444 
1445  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotateMarker );
1446  x->setPlacement( placement );
1447  x->setInterval( interval );
1448  x->setSubSymbol( marker );
1449  x->setOffset( offset );
1450  return x;
1451 }
1452 
1454 {
1455  mMarker->setSize( width );
1456 }
1457 
1459 {
1460  return mMarker->size();
1461 }
1462 
1464 {
1466  mIntervalUnit = unit;
1467  mOffsetUnit = unit;
1468  mOffsetAlongLineUnit = unit;
1469 }
1470 
1472 {
1474  if ( mIntervalUnit != unit || mOffsetUnit != unit || mOffsetAlongLineUnit != unit )
1475  {
1476  return QgsSymbolV2::Mixed;
1477  }
1478  return unit;
1479 }
1480 
1482 {
1484  mIntervalMapUnitScale = scale;
1485  mOffsetMapUnitScale = scale;
1487 }
1488 
1490 {
1494  {
1495  return mOffsetMapUnitScale;
1496  }
1497  return QgsMapUnitScale();
1498 }
1499 
1501 {
1502  return ( mMarker->size() / 2.0 ) + mOffset;
1503 }
1504 
1505 
1506 
static double _averageAngle(const QPointF &prevPt, const QPointF &pt, const QPointF &nextPt)
static double mapUnitScaleFactor(double scaleDenominator, QgsSymbolV2::OutputUnit symbolUnits, QGis::UnitType mapUnits)
void setIntervalUnit(QgsSymbolV2::OutputUnit unit)
static QString encodeOutputUnit(QgsSymbolV2::OutputUnit unit)
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:86
#define DEFAULT_SIMPLELINE_PENSTYLE
#define DEFAULT_MARKERLINE_ROTATE
double estimateMaxBleed() const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
QMap< QString, QgsExpression * > mDataDefinedProperties
float threshold() const
Gets the simplification threshold of the vector layer managed.
int renderHints() const
Definition: qgssymbolv2.h:236
QVector< qreal > dxfCustomDashPattern(QgsSymbolV2::OutputUnit &unit) const override
virtual double width() const
double markerAngle(const QPolygonF &points, bool isRing, int vertex)
void renderPolyline(const QPolygonF &points, QgsSymbolV2RenderContext &context) override
#define DEFAULT_MARKERLINE_INTERVAL
QgsStringMap properties() const override
SymbolType type() const
Definition: qgssymbolv2.h:85
QVariant evaluate(const QgsFeature *f=NULL)
Evaluate the feature and return the result.
QColor selectionColor() const
const QgsVectorSimplifyMethod & vectorSimplifyMethod() const
Added in QGIS v2.4.
virtual void setWidth(double width) override
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setCustomDashPatternUnit(QgsSymbolV2::OutputUnit unit)
QgsMapUnitScale mCustomDashPatternMapUnitScale
double rendererScale() const
void renderPolyline(const QPolygonF &points, QgsSymbolV2RenderContext &context) override
static QgsStringMap getVendorOptionList(QDomElement &element)
QgsGeometry * geometry() const
Get the geometry object associated with this feature.
Definition: qgsfeature.cpp:112
QGis::UnitType mapUnits() const
Definition: qgsdxfexport.h:54
void startRender(QgsSymbolV2RenderContext &context) override
static QVector< qreal > decodeRealVector(const QString &s)
void renderPolylineInterval(const QPolygonF &points, QgsSymbolV2RenderContext &context)
QgsSymbolV2::OutputUnit outputUnit() const override
QVector< qreal > customDashVector() const
bool setSubSymbol(QgsSymbolV2 *symbol) override
QGis::GeometryType type() const
Returns type of the vector.
void setPenJoinStyle(Qt::PenJoinStyle style)
static QDomElement createVendorOptionElement(QDomDocument &doc, QString name, QString value)
static QColor decodeColor(QString str)
void copyPaintEffect(QgsSymbolLayerV2 *destLayer) const
Copies paint effect of this layer to another symbol layer.
static const bool selectionIsOpaque
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
QgsMapUnitScale mWidthMapUnitScale
void setIntervalMapUnitScale(const QgsMapUnitScale &scale)
QString layerType() const override
void setMapUnitScale(const QgsMapUnitScale &scale) override
QgsMapUnitScale mOffsetMapUnitScale
QMap< QString, QString > QgsStringMap
Definition: qgis.h:438
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:347
Qt::PenStyle penStyle() const
QgsSymbolV2::OutputUnit mOffsetUnit
void setWidthUnit(QgsSymbolV2::OutputUnit unit)
QgsMapUnitScale mOffsetAlongLineMapUnitScale
double dxfWidth(const QgsDxfExport &e, const QgsSymbolV2RenderContext &context) const override
void setOffsetAlongLine(double offsetAlongLine)
Sets the the offset along the line for the marker placement.
static QString encodeColor(QColor color)
void setInterval(double interval)
virtual QgsExpression * expression(const QString &property) const
QgsMapUnitScale mapUnitScale() const override
qreal alpha() const
Get alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:229
void setDrawInsidePolygon(bool drawInsidePolygon)
static QString encodePenStyle(Qt::PenStyle style)
QPointF diffForInterval(double interval)
void setColor(const QColor &color)
QgsSymbolV2::OutputUnit mIntervalUnit
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
QgsSymbolLayerV2 * clone() const override
QgsSymbolLayerV2 * clone() const override
static QgsSymbolLayerV2 * createMarkerLayerFromSld(QDomElement &element)
double offsetAlongLine() const
Returns the offset along the line for the marker placement.
static QString ogrFeatureStylePen(double width, double mmScaleFactor, double mapUnitsScaleFactor, const QColor &c, Qt::PenJoinStyle joinStyle=Qt::MiterJoin, Qt::PenCapStyle capStyle=Qt::FlatCap, double offset=0.0, const QVector< qreal > *dashPattern=0)
Create ogr feature style string for pen.
void setOffset(double offset)
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
void setMapUnitScale(const QgsMapUnitScale &scale) override
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbolv2.h:241
void renderPolygonOutline(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context) override
QgsSymbolV2::OutputUnit outputUnit() const override
void startRender(QgsRenderContext &context, const QgsFields *fields=0)
The geometries can be rendered with 'AntiAliasing' disabled because of it is '1-pixel size'...
static Qt::PenCapStyle decodePenCapStyle(QString str)
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
static Qt::PenStyle decodePenStyle(QString str)
QgsSymbolV2::OutputUnit mWidthUnit
void setWidthMapUnitScale(const QgsMapUnitScale &scale)
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, QString function)
QVector< qreal > mCustomDashVector
Vector with an even number of entries for the.
#define DEFAULT_SIMPLELINE_WIDTH
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
void renderPolygonOutline(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context) override
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const override
#define M_PI
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
void setAngle(double angle)
#define DEFAULT_SIMPLELINE_CAPSTYLE
virtual QgsSymbolV2 * clone() const override
#define DEFAULT_SIMPLELINE_JOINSTYLE
int symbolLayerCount()
Returns total number of symbol layers contained in the symbol.
Definition: qgssymbolv2.h:112
QString layerType() const override
void setSize(double size)
double rasterScaleFactor() const
void stopRender(QgsSymbolV2RenderContext &context) override
virtual QColor color() const
void setOffsetAlongLineMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale used for calculating the offset in map units along line for markers...
void setOffsetAlongLineUnit(QgsSymbolV2::OutputUnit unit)
Sets the unit used for calculating the offset along line for markers.
void renderPoint(const QPointF &point, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
virtual void prepareExpressions(const QgsFields *fields, double scale=-1.0)
double estimateMaxBleed() const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
QgsSymbolV2 * subSymbol() override
void setPenCapStyle(Qt::PenCapStyle style)
void setCustomDashVector(const QVector< qreal > &vector)
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
double dxfOffset(const QgsDxfExport &e, const QgsSymbolV2RenderContext &context) const override
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
void setOffsetUnit(QgsSymbolV2::OutputUnit unit)
static QString encodeRealVector(const QVector< qreal > &v)
QgsSymbolV2::OutputUnit outputUnit() const override
QgsSymbolV2::OutputUnit mCustomDashPatternUnit
virtual QString layerType() const =0
QColor dxfColor(const QgsSymbolV2RenderContext &context) const override
double symbologyScaleDenominator() const
Definition: qgsdxfexport.h:51
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const override
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
static void lineToSld(QDomDocument &doc, QDomElement &element, Qt::PenStyle penStyle, QColor color, double width=-1, const Qt::PenJoinStyle *penJoinStyle=0, const Qt::PenCapStyle *penCapStyle=0, const QVector< qreal > *customDashPattern=0, double dashOffset=0.0)
#define DEFAULT_SIMPLELINE_COLOR
static void createGeometryElement(QDomDocument &doc, QDomElement &element, QString geomFunc)
Contains information about the context of a rendering operation.
QgsMapUnitScale mapUnitScale() const override
QgsSymbolV2::OutputUnit mOffsetAlongLineUnit
QPainter * painter()
void stopRender(QgsRenderContext &context)
QgsSimpleLineSymbolLayerV2(QColor color=DEFAULT_SIMPLELINE_COLOR, double width=DEFAULT_SIMPLELINE_WIDTH, Qt::PenStyle penStyle=DEFAULT_SIMPLELINE_PENSTYLE)
void setMapUnitScale(const QgsMapUnitScale &scale) override
static double lineWidthScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u, const QgsMapUnitScale &scale=QgsMapUnitScale())
Returns the line width scale factor depending on the unit and the paint device.
Qt::PenStyle dxfPenStyle() const override
QList< QPolygonF > offsetLine(QPolygonF polyline, double dist, QGis::GeometryType geometryType)
calculate geometry shifted by a specified distance
QList< QgsSymbolLayerV2 * > QgsSymbolLayerV2List
Definition: qgssymbolv2.h:43
void setCustomDashPatternMapUnitScale(const QgsMapUnitScale &scale)
double offset() const
void stopRender(QgsSymbolV2RenderContext &context) override
QgsRenderContext & renderContext()
Definition: qgssymbolv2.h:219
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
QgsMapUnitScale mapUnitScale() const override
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
SimplifyHints simplifyHints() const
Gets the simplification hints of the vector layer managed.
void setColor(const QColor &color) override
const QgsFields * fields() const
Fields of the layer.
Definition: qgssymbolv2.h:247
void setRenderHints(int hints)
Definition: qgssymbolv2.h:161
static Qt::PenJoinStyle decodePenJoinStyle(QString str)
MyLine(QPointF p1, QPointF p2)
bool selected() const
Definition: qgssymbolv2.h:233
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
QgsSymbolLayerV2 * symbolLayer(int layer)
Returns a specific symbol layers contained in the symbol.
static bool lineFromSld(QDomElement &element, Qt::PenStyle &penStyle, QColor &color, double &width, Qt::PenJoinStyle *penJoinStyle=0, Qt::PenCapStyle *penCapStyle=0, QVector< qreal > *customDashPattern=0, double *dashOffset=0)
void startRender(QgsSymbolV2RenderContext &context) override
QgsMarkerLineSymbolLayerV2(bool rotateMarker=DEFAULT_MARKERLINE_ROTATE, double interval=DEFAULT_MARKERLINE_INTERVAL)
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
static bool isGeneralizableByDeviceBoundingBox(const QgsRectangle &envelope, float mapToPixelTol=1.0f)
Returns whether the device-envelope can be replaced by its BBOX when is applied the specified toleran...
void saveDataDefinedProperties(QgsStringMap &stringMap) const
Saves data defined properties to string map.
void setAlpha(qreal alpha)
Set alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:159
void renderPolylineCentral(const QPolygonF &points, QgsSymbolV2RenderContext &context)
virtual double width() const override
QgsSymbolV2::OutputUnit widthUnit() const
static QgsSymbolV2::OutputUnit decodeOutputUnit(QString str)
Qt::PenJoinStyle penJoinStyle() const
void renderPolylineVertex(const QPolygonF &points, QgsSymbolV2RenderContext &context, Placement placement=Vertex)
void copyDataDefinedProperties(QgsSymbolLayerV2 *destLayer) const
Copies data defined properties of this layer to another symbol layer.
Qt::PenCapStyle penCapStyle() const
QColor color() const
static QString encodePenCapStyle(Qt::PenCapStyle style)
QgsStringMap properties() const override
virtual void setDataDefinedProperty(const QString &property, const QString &expressionString)