QGIS API Documentation  2.17.0-Master (bf77d09)
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 "qgscurvev2.h"
18 #include "qgscurvepolygonv2.h"
19 #include "qgsdxfexport.h"
20 #include "qgssymbollayerv2utils.h"
21 #include "qgsexpression.h"
22 #include "qgsrendercontext.h"
23 #include "qgslogger.h"
24 #include "qgsvectorlayer.h"
25 #include "qgsgeometrysimplifier.h"
26 #include "qgsunittypes.h"
27 
28 #include <QPainter>
29 #include <QDomDocument>
30 #include <QDomElement>
31 
32 #include <cmath>
33 
34 QgsSimpleLineSymbolLayerV2::QgsSimpleLineSymbolLayerV2( const QColor& color, double width, Qt::PenStyle penStyle )
35  : mPenStyle( penStyle )
36  , mPenJoinStyle( DEFAULT_SIMPLELINE_JOINSTYLE )
37  , mPenCapStyle( DEFAULT_SIMPLELINE_CAPSTYLE )
38  , mUseCustomDashPattern( false )
39  , mCustomDashPatternUnit( QgsSymbolV2::MM )
40  , mDrawInsidePolygon( false )
41 {
42  mColor = color;
43  mWidth = width;
44  mCustomDashVector << 5 << 2;
45 }
46 
48 {
50  mWidthUnit = unit;
51  mOffsetUnit = unit;
53 }
54 
56 {
58  if ( mWidthUnit != unit || mOffsetUnit != unit || mCustomDashPatternUnit != unit )
59  {
60  return QgsSymbolV2::Mixed;
61  }
62  return unit;
63 }
64 
66 {
68  mWidthMapUnitScale = scale;
69  mOffsetMapUnitScale = scale;
71 }
72 
74 {
78  {
79  return mWidthMapUnitScale;
80  }
81  return QgsMapUnitScale();
82 }
83 
85 {
89 
90  if ( props.contains( "line_color" ) )
91  {
92  color = QgsSymbolLayerV2Utils::decodeColor( props["line_color"] );
93  }
94  else if ( props.contains( "outline_color" ) )
95  {
96  color = QgsSymbolLayerV2Utils::decodeColor( props["outline_color"] );
97  }
98  else if ( props.contains( "color" ) )
99  {
100  //pre 2.5 projects used "color"
101  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
102  }
103  if ( props.contains( "line_width" ) )
104  {
105  width = props["line_width"].toDouble();
106  }
107  else if ( props.contains( "outline_width" ) )
108  {
109  width = props["outline_width"].toDouble();
110  }
111  else if ( props.contains( "width" ) )
112  {
113  //pre 2.5 projects used "width"
114  width = props["width"].toDouble();
115  }
116  if ( props.contains( "line_style" ) )
117  {
118  penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["line_style"] );
119  }
120  else if ( props.contains( "outline_style" ) )
121  {
122  penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["outline_style"] );
123  }
124  else if ( props.contains( "penstyle" ) )
125  {
126  penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["penstyle"] );
127  }
128 
129  QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
130  if ( props.contains( "line_width_unit" ) )
131  {
132  l->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["line_width_unit"] ) );
133  }
134  else if ( props.contains( "outline_width_unit" ) )
135  {
136  l->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["outline_width_unit"] ) );
137  }
138  else if ( props.contains( "width_unit" ) )
139  {
140  //pre 2.5 projects used "width_unit"
141  l->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["width_unit"] ) );
142  }
143  if ( props.contains( "width_map_unit_scale" ) )
144  l->setWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["width_map_unit_scale"] ) );
145  if ( props.contains( "offset" ) )
146  l->setOffset( props["offset"].toDouble() );
147  if ( props.contains( "offset_unit" ) )
148  l->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
149  if ( props.contains( "offset_map_unit_scale" ) )
150  l->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
151  if ( props.contains( "joinstyle" ) )
153  if ( props.contains( "capstyle" ) )
154  l->setPenCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( props["capstyle"] ) );
155 
156  if ( props.contains( "use_custom_dash" ) )
157  {
158  l->setUseCustomDashPattern( props["use_custom_dash"].toInt() );
159  }
160  if ( props.contains( "customdash" ) )
161  {
163  }
164  if ( props.contains( "customdash_unit" ) )
165  {
166  l->setCustomDashPatternUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["customdash_unit"] ) );
167  }
168  if ( props.contains( "customdash_map_unit_scale" ) )
169  {
170  l->setCustomDashPatternMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["customdash_map_unit_scale"] ) );
171  }
172 
173  if ( props.contains( "draw_inside_polygon" ) )
174  {
175  l->setDrawInsidePolygon( props["draw_inside_polygon"].toInt() );
176  }
177 
178  l->restoreDataDefinedProperties( props );
179 
180  return l;
181 }
182 
183 
185 {
186  return "SimpleLine";
187 }
188 
190 {
191  QColor penColor = mColor;
192  penColor.setAlphaF( mColor.alphaF() * context.alpha() );
193  mPen.setColor( penColor );
195  mPen.setWidthF( scaledWidth );
196  if ( mUseCustomDashPattern && !qgsDoubleNear( scaledWidth, 0 ) )
197  {
198  mPen.setStyle( Qt::CustomDashLine );
199 
200  //scale pattern vector
201  double dashWidthDiv = scaledWidth;
202  //fix dash pattern width in Qt 4.8
203  QStringList versionSplit = QString( qVersion() ).split( '.' );
204  if ( versionSplit.size() > 1
205  && versionSplit.at( 1 ).toInt() >= 8
206  && ( scaledWidth * context.renderContext().rasterScaleFactor() ) < 1.0 )
207  {
208  dashWidthDiv = 1.0;
209  }
210  QVector<qreal> scaledVector;
212  for ( ; it != mCustomDashVector.constEnd(); ++it )
213  {
214  //the dash is specified in terms of pen widths, therefore the division
216  }
217  mPen.setDashPattern( scaledVector );
218  }
219  else
220  {
222  }
225 
226  mSelPen = mPen;
227  QColor selColor = context.renderContext().selectionColor();
228  if ( ! selectionIsOpaque )
229  selColor.setAlphaF( context.alpha() );
230  mSelPen.setColor( selColor );
231 
232  //prepare expressions for data defined properties
233  prepareExpressions( context );
234 }
235 
237 {
238  Q_UNUSED( context );
239 }
240 
242 {
243  QPainter* p = context.renderContext().painter();
244  if ( !p )
245  {
246  return;
247  }
248 
249  if ( mDrawInsidePolygon )
250  {
251  //only drawing the line on the interior of the polygon, so set clip path for painter
252  p->save();
253  QPainterPath clipPath;
254  clipPath.addPolygon( points );
255 
256  if ( rings )
257  {
258  //add polygon rings
260  for ( ; it != rings->constEnd(); ++it )
261  {
262  QPolygonF ring = *it;
263  clipPath.addPolygon( ring );
264  }
265  }
266 
267  //use intersect mode, as a clip path may already exist (eg, for composer maps)
268  p->setClipPath( clipPath, Qt::IntersectClip );
269  }
270 
271  renderPolyline( points, context );
272  if ( rings )
273  {
274  mOffset = -mOffset; // invert the offset for rings!
275  Q_FOREACH ( const QPolygonF& ring, *rings )
276  renderPolyline( ring, context );
277  mOffset = -mOffset;
278  }
279 
280  if ( mDrawInsidePolygon )
281  {
282  //restore painter to reset clip path
283  p->restore();
284  }
285 
286 }
287 
289 {
290  QPainter* p = context.renderContext().painter();
291  if ( !p )
292  {
293  return;
294  }
295 
296  //size scaling by field
298  {
299  applySizeScale( context, mPen, mSelPen );
300  }
301 
302  double offset = mOffset;
303  applyDataDefinedSymbology( context, mPen, mSelPen, offset );
304 
305  p->setPen( context.selected() ? mSelPen : mPen );
306  p->setBrush( Qt::NoBrush );
307 
308  // Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #2 points).
309  if ( points.size() <= 2 &&
312  ( p->renderHints() & QPainter::Antialiasing ) )
313  {
314  p->setRenderHint( QPainter::Antialiasing, false );
315 #if 0
316  p->drawPolyline( points );
317 #else
318  QPainterPath path;
319  path.addPolygon( points );
320  p->drawPath( path );
321 #endif
322  p->setRenderHint( QPainter::Antialiasing, true );
323  return;
324  }
325 
326  if ( qgsDoubleNear( offset, 0 ) )
327  {
328 #if 0
329  p->drawPolyline( points );
330 #else
331  QPainterPath path;
332  path.addPolygon( points );
333  p->drawPath( path );
334 #endif
335  }
336  else
337  {
339  QList<QPolygonF> mline = ::offsetLine( points, scaledOffset, context.feature() ? context.feature()->constGeometry()->type() : QGis::Line );
340  for ( int part = 0; part < mline.count(); ++part )
341  {
342 #if 0
343  p->drawPolyline( mline );
344 #else
345  QPainterPath path;
346  path.addPolygon( mline[ part ] );
347  p->drawPath( path );
348 #endif
349  }
350  }
351 }
352 
354 {
355  QgsStringMap map;
356  map["line_color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
357  map["line_width"] = QString::number( mWidth );
358  map["line_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mWidthUnit );
359  map["width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mWidthMapUnitScale );
360  map["line_style"] = QgsSymbolLayerV2Utils::encodePenStyle( mPenStyle );
363  map["offset"] = QString::number( mOffset );
365  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
366  map["use_custom_dash"] = ( mUseCustomDashPattern ? "1" : "0" );
370  map["draw_inside_polygon"] = ( mDrawInsidePolygon ? "1" : "0" );
372  return map;
373 }
374 
376 {
378  l->setWidthUnit( mWidthUnit );
384  l->setOffset( mOffset );
391  copyPaintEffect( l );
392  return l;
393 }
394 
395 void QgsSimpleLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap& props ) const
396 {
397  if ( mPenStyle == Qt::NoPen )
398  return;
399 
400  QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
401  if ( !props.value( "uom", "" ).isEmpty() )
402  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
403  element.appendChild( symbolizerElem );
404 
405  // <Geometry>
406  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
407 
408  // <Stroke>
409  QDomElement strokeElem = doc.createElement( "se:Stroke" );
410  symbolizerElem.appendChild( strokeElem );
411 
412  Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
413  QgsSymbolLayerV2Utils::lineToSld( doc, strokeElem, penStyle, mColor, mWidth,
415 
416  // <se:PerpendicularOffset>
417  if ( !qgsDoubleNear( mOffset, 0.0 ) )
418  {
419  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
420  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( mOffset ) ) );
421  symbolizerElem.appendChild( perpOffsetElem );
422  }
423 }
424 
425 QString QgsSimpleLineSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
426 {
427  if ( mUseCustomDashPattern )
428  {
429  return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor,
432  }
433  else
434  {
435  return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor, mPen.color(), mPenJoinStyle,
437  }
438 }
439 
441 {
442  QgsDebugMsg( "Entered." );
443 
444  QDomElement strokeElem = element.firstChildElement( "Stroke" );
445  if ( strokeElem.isNull() )
446  return nullptr;
447 
448  Qt::PenStyle penStyle;
449  QColor color;
450  double width;
451  Qt::PenJoinStyle penJoinStyle;
452  Qt::PenCapStyle penCapStyle;
454 
455  if ( !QgsSymbolLayerV2Utils::lineFromSld( strokeElem, penStyle,
456  color, width,
457  &penJoinStyle, &penCapStyle,
458  &customDashVector ) )
459  return nullptr;
460 
461  double offset = 0.0;
462  QDomElement perpOffsetElem = element.firstChildElement( "PerpendicularOffset" );
463  if ( !perpOffsetElem.isNull() )
464  {
465  bool ok;
466  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
467  if ( ok )
468  offset = d;
469  }
470 
471  QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
472  l->setOffset( offset );
473  l->setPenJoinStyle( penJoinStyle );
474  l->setPenCapStyle( penCapStyle );
475  l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine );
476  l->setCustomDashVector( customDashVector );
477  return l;
478 }
479 
480 void QgsSimpleLineSymbolLayerV2::applySizeScale( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen )
481 {
483  pen.setWidthF( scaledWidth );
484  selPen.setWidthF( scaledWidth );
485 }
486 
487 void QgsSimpleLineSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen, double& offset )
488 {
489  if ( !hasDataDefinedProperties() )
490  return; // shortcut
491 
492  //data defined properties
493  bool hasStrokeWidthExpression = false;
495  {
496  context.setOriginalValueVariable( mWidth );
497  double scaledWidth = QgsSymbolLayerV2Utils::convertToPainterUnits( context.renderContext(),
500  pen.setWidthF( scaledWidth );
501  selPen.setWidthF( scaledWidth );
502  hasStrokeWidthExpression = true;
503  }
504 
505  //color
506  bool ok;
508  {
511  if ( ok )
512  pen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorString ) );
513  }
514 
515  //offset
517  {
520  }
521 
522  //dash dot vector
524  {
526  double dashWidthDiv = mPen.widthF();
527 
528  if ( hasStrokeWidthExpression )
529  {
530  dashWidthDiv = pen.widthF();
531  scaledWidth = pen.widthF();
532  }
533 
534  //fix dash pattern width in Qt 4.8
535  QStringList versionSplit = QString( qVersion() ).split( '.' );
536  if ( versionSplit.size() > 1
537  && versionSplit.at( 1 ).toInt() >= 8
538  && ( scaledWidth * context.renderContext().rasterScaleFactor() ) < 1.0 )
539  {
540  dashWidthDiv = 1.0;
541  }
542 
543  QVector<qreal> dashVector;
545  if ( ok )
546  {
547  QStringList::const_iterator dashIt = dashList.constBegin();
548  for ( ; dashIt != dashList.constEnd(); ++dashIt )
549  {
551  }
552  pen.setDashPattern( dashVector );
553  }
554  }
555 
556  //line style
558  {
561  if ( ok )
562  pen.setStyle( QgsSymbolLayerV2Utils::decodePenStyle( lineStyleString ) );
563  }
564 
565  //join style
567  {
570  if ( ok )
572  }
573 
574  //cap style
576  {
579  if ( ok )
580  pen.setCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( capStyleString ) );
581  }
582 }
583 
585 {
586  if ( mDrawInsidePolygon )
587  {
588  //set to clip line to the interior of polygon, so we expect no bleed
589  return 0;
590  }
591  else
592  {
593  return ( mWidth / 2.0 ) + mOffset;
594  }
595 }
596 
598 {
599  unit = mCustomDashPatternUnit;
601 }
602 
604 {
605  return mPenStyle;
606 }
607 
609 {
610  double width = mWidth;
611 
613  {
614  context.setOriginalValueVariable( mWidth );
616  }
617  else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
618  {
620  }
621 
622  return width * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), widthUnit(), e.mapUnits() );
623 }
624 
626 {
628  {
629  bool ok;
632  if ( ok )
633  return ( QgsSymbolLayerV2Utils::decodeColor( colorString ) );
634  }
635  return mColor;
636 }
637 
639 {
640  Q_UNUSED( e );
641  double offset = mOffset;
642 
644  {
647  }
648  return offset;
649 }
650 
652 
654 
655 class MyLine
656 {
657  public:
658  MyLine( QPointF p1, QPointF p2 )
659  : mVertical( false )
660  , mIncreasing( false )
661  , mT( 0.0 )
662  , mLength( 0.0 )
663  {
664  if ( p1 == p2 )
665  return; // invalid
666 
667  // tangent and direction
668  if ( qgsDoubleNear( p1.x(), p2.x() ) )
669  {
670  // vertical line - tangent undefined
671  mVertical = true;
672  mIncreasing = ( p2.y() > p1.y() );
673  }
674  else
675  {
676  mVertical = false;
677  mT = float( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
678  mIncreasing = ( p2.x() > p1.x() );
679  }
680 
681  // length
682  double x = ( p2.x() - p1.x() );
683  double y = ( p2.y() - p1.y() );
684  mLength = sqrt( x * x + y * y );
685  }
686 
687  // return angle in radians
688  double angle()
689  {
690  double a = ( mVertical ? M_PI / 2 : atan( mT ) );
691 
692  if ( !mIncreasing )
693  a += M_PI;
694  return a;
695  }
696 
697  // return difference for x,y when going along the line with specified interval
698  QPointF diffForInterval( double interval )
699  {
700  if ( mVertical )
701  return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
702 
703  double alpha = atan( mT );
704  double dx = cos( alpha ) * interval;
705  double dy = sin( alpha ) * interval;
706  return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
707  }
708 
709  double length() { return mLength; }
710 
711  protected:
712  bool mVertical;
713  bool mIncreasing;
714  double mT;
715  double mLength;
716 };
717 
719 
720 QgsMarkerLineSymbolLayerV2::QgsMarkerLineSymbolLayerV2( bool rotateMarker, double interval )
721 {
722  mRotateMarker = rotateMarker;
723  mInterval = interval;
724  mIntervalUnit = QgsSymbolV2::MM;
725  mMarker = nullptr;
726  mPlacement = Interval;
727  mOffsetAlongLine = 0;
728  mOffsetAlongLineUnit = QgsSymbolV2::MM;
729 
731 }
732 
734 {
735  delete mMarker;
736 }
737 
739 {
740  bool rotate = DEFAULT_MARKERLINE_ROTATE;
741  double interval = DEFAULT_MARKERLINE_INTERVAL;
742 
743 
744  if ( props.contains( "interval" ) )
745  interval = props["interval"].toDouble();
746  if ( props.contains( "rotate" ) )
747  rotate = ( props["rotate"] == "1" );
748 
749  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotate, interval );
750  if ( props.contains( "offset" ) )
751  {
752  x->setOffset( props["offset"].toDouble() );
753  }
754  if ( props.contains( "offset_unit" ) )
755  {
756  x->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
757  }
758  if ( props.contains( "interval_unit" ) )
759  {
760  x->setIntervalUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["interval_unit"] ) );
761  }
762  if ( props.contains( "offset_along_line" ) )
763  {
764  x->setOffsetAlongLine( props["offset_along_line"].toDouble() );
765  }
766  if ( props.contains( "offset_along_line_unit" ) )
767  {
768  x->setOffsetAlongLineUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_along_line_unit"] ) );
769  }
770  if ( props.contains(( "offset_along_line_map_unit_scale" ) ) )
771  {
772  x->setOffsetAlongLineMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_along_line_map_unit_scale"] ) );
773  }
774 
775  if ( props.contains( "offset_map_unit_scale" ) )
776  {
777  x->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
778  }
779  if ( props.contains( "interval_map_unit_scale" ) )
780  {
781  x->setIntervalMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["interval_map_unit_scale"] ) );
782  }
783 
784  if ( props.contains( "placement" ) )
785  {
786  if ( props["placement"] == "vertex" )
787  x->setPlacement( Vertex );
788  else if ( props["placement"] == "lastvertex" )
789  x->setPlacement( LastVertex );
790  else if ( props["placement"] == "firstvertex" )
791  x->setPlacement( FirstVertex );
792  else if ( props["placement"] == "centralpoint" )
793  x->setPlacement( CentralPoint );
794  else if ( props["placement"] == "curvepoint" )
795  x->setPlacement( CurvePoint );
796  else
797  x->setPlacement( Interval );
798  }
799 
800  x->restoreDataDefinedProperties( props );
801 
802  return x;
803 }
804 
806 {
807  return "MarkerLine";
808 }
809 
811 {
812  mMarker->setColor( color );
813  mColor = color;
814 }
815 
817 {
818  return mMarker ? mMarker->color() : mColor;
819 }
820 
822 {
823  mMarker->setAlpha( context.alpha() );
824 
825  // if being rotated, it gets initialized with every line segment
826  int hints = 0;
827  if ( mRotateMarker )
831  mMarker->setRenderHints( hints );
832 
833  mMarker->startRender( context.renderContext(), context.fields() );
834 
835  //prepare expressions for data defined properties
836  prepareExpressions( context );
837 }
838 
840 {
841  mMarker->stopRender( context.renderContext() );
842 }
843 
845 {
846  double offset = mOffset;
847 
849  {
852  }
853 
854  Placement placement = mPlacement;
855 
856  bool ok;
858  {
860  if ( ok )
861  {
862  if ( placementString.compare( "vertex", Qt::CaseInsensitive ) == 0 )
863  {
864  placement = Vertex;
865  }
866  else if ( placementString.compare( "lastvertex", Qt::CaseInsensitive ) == 0 )
867  {
868  placement = LastVertex;
869  }
870  else if ( placementString.compare( "firstvertex", Qt::CaseInsensitive ) == 0 )
871  {
872  placement = FirstVertex;
873  }
874  else if ( placementString.compare( "centerpoint", Qt::CaseInsensitive ) == 0 )
875  {
876  placement = CentralPoint;
877  }
878  else if ( placementString.compare( "curvepoint", Qt::CaseInsensitive ) == 0 )
879  {
880  placement = CurvePoint;
881  }
882  else
883  {
884  placement = Interval;
885  }
886  }
887  }
888 
889  if ( qgsDoubleNear( offset, 0.0 ) )
890  {
891  if ( placement == Interval )
892  renderPolylineInterval( points, context );
893  else if ( placement == CentralPoint )
894  renderPolylineCentral( points, context );
895  else
896  renderPolylineVertex( points, context, placement );
897  }
898  else
899  {
900  context.renderContext().setGeometry( nullptr ); //always use segmented geometry with offset
902 
903  for ( int part = 0; part < mline.count(); ++part )
904  {
905  const QPolygonF &points2 = mline[ part ];
906 
907  if ( placement == Interval )
908  renderPolylineInterval( points2, context );
909  else if ( placement == CentralPoint )
910  renderPolylineCentral( points2, context );
911  else
912  renderPolylineVertex( points2, context, placement );
913  }
914  }
915 }
916 
918 {
919  const QgsCurvePolygonV2* curvePolygon = dynamic_cast<const QgsCurvePolygonV2*>( context.renderContext().geometry() );
920 
921  if ( curvePolygon )
922  {
923  context.renderContext().setGeometry( curvePolygon->exteriorRing() );
924  }
925  renderPolyline( points, context );
926  if ( rings )
927  {
928  mOffset = -mOffset; // invert the offset for rings!
929  for ( int i = 0; i < rings->size(); ++i )
930  {
931  if ( curvePolygon )
932  {
933  context.renderContext().setGeometry( curvePolygon->interiorRing( i ) );
934  }
935  renderPolyline( rings->at( i ), context );
936  }
937  mOffset = -mOffset;
938  }
939 }
940 
942 {
943  if ( points.isEmpty() )
944  return;
945 
946  QPointF lastPt = points[0];
947  double lengthLeft = 0; // how much is left until next marker
948  bool first = mOffsetAlongLine ? false : true; //only draw marker at first vertex when no offset along line is set
949 
950  QgsRenderContext& rc = context.renderContext();
951  double interval = mInterval;
952 
954  {
955  context.setOriginalValueVariable( mInterval );
956  interval = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_INTERVAL, context, mInterval ).toDouble();
957  }
958  if ( interval <= 0 )
959  {
960  interval = 0.1;
961  }
962  double offsetAlongLine = mOffsetAlongLine;
964  {
965  context.setOriginalValueVariable( mOffsetAlongLine );
966  offsetAlongLine = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET_ALONG_LINE, context, mOffsetAlongLine ).toDouble();
967  }
968 
969  double painterUnitInterval = QgsSymbolLayerV2Utils::convertToPainterUnits( rc, interval, mIntervalUnit, mIntervalMapUnitScale );
970  lengthLeft = painterUnitInterval - QgsSymbolLayerV2Utils::convertToPainterUnits( rc, offsetAlongLine, mIntervalUnit, mIntervalMapUnitScale );
971 
972  for ( int i = 1; i < points.count(); ++i )
973  {
974  const QPointF& pt = points[i];
975 
976  if ( lastPt == pt ) // must not be equal!
977  continue;
978 
979  // for each line, find out dx and dy, and length
980  MyLine l( lastPt, pt );
981  QPointF diff = l.diffForInterval( painterUnitInterval );
982 
983  // if there's some length left from previous line
984  // use only the rest for the first point in new line segment
985  double c = 1 - lengthLeft / painterUnitInterval;
986 
987  lengthLeft += l.length();
988 
989  // rotate marker (if desired)
990  if ( mRotateMarker )
991  {
992  mMarker->setLineAngle( l.angle() * 180 / M_PI );
993  }
994 
995  // draw first marker
996  if ( first )
997  {
998  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
999  first = false;
1000  }
1001 
1002  // while we're not at the end of line segment, draw!
1003  while ( lengthLeft > painterUnitInterval )
1004  {
1005  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
1006  lastPt += c * diff;
1007  lengthLeft -= painterUnitInterval;
1008  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
1009  c = 1; // reset c (if wasn't 1 already)
1010  }
1011 
1012  lastPt = pt;
1013  }
1014 }
1015 
1016 static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt )
1017 {
1018  // calc average angle between the previous and next point
1019  double a1 = MyLine( prevPt, pt ).angle();
1020  double a2 = MyLine( pt, nextPt ).angle();
1021  double unitX = cos( a1 ) + cos( a2 ), unitY = sin( a1 ) + sin( a2 );
1022 
1023  return atan2( unitY, unitX );
1024 }
1025 
1027 {
1028  if ( points.isEmpty() )
1029  return;
1030 
1031  QgsRenderContext& rc = context.renderContext();
1032 
1033  double origAngle = mMarker->angle();
1034  int i, maxCount;
1035  bool isRing = false;
1036 
1037  double offsetAlongLine = mOffsetAlongLine;
1039  {
1040  context.setOriginalValueVariable( mOffsetAlongLine );
1041  offsetAlongLine = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET_ALONG_LINE, context, mOffsetAlongLine ).toDouble();
1042  }
1043  if ( !qgsDoubleNear( offsetAlongLine, 0.0 ) )
1044  {
1045  //scale offset along line
1046  offsetAlongLine = QgsSymbolLayerV2Utils::convertToPainterUnits( rc, offsetAlongLine, mOffsetAlongLineUnit, mOffsetAlongLineMapUnitScale );
1047  }
1048 
1049  if ( qgsDoubleNear( offsetAlongLine, 0.0 ) && context.renderContext().geometry()
1050  && context.renderContext().geometry()->hasCurvedSegments() && ( placement == Vertex || placement == CurvePoint ) )
1051  {
1053  const QgsMapToPixel& mtp = context.renderContext().mapToPixel();
1054 
1055  QgsVertexId vId;
1056  QgsPointV2 vPoint;
1057  double x, y, z;
1058  QPointF mapPoint;
1059  while ( context.renderContext().geometry()->nextVertex( vId, vPoint ) )
1060  {
1061  if (( placement == Vertex && vId.type == QgsVertexId::SegmentVertex )
1062  || ( placement == CurvePoint && vId.type == QgsVertexId::CurveVertex ) )
1063  {
1064  //transform
1065  x = vPoint.x(), y = vPoint.y();
1066  z = 0.0;
1067  if ( ct )
1068  {
1069  ct->transformInPlace( x, y, z );
1070  }
1071  mapPoint.setX( x );
1072  mapPoint.setY( y );
1073  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1074  if ( mRotateMarker )
1075  {
1076  double angle = context.renderContext().geometry()->vertexAngle( vId );
1077  mMarker->setAngle( angle * 180 / M_PI );
1078  }
1079  mMarker->renderPoint( mapPoint, context.feature(), rc, -1, context.selected() );
1080  }
1081  }
1082  return;
1083  }
1084 
1085  if ( placement == FirstVertex )
1086  {
1087  i = 0;
1088  maxCount = 1;
1089  }
1090  else if ( placement == LastVertex )
1091  {
1092  i = points.count() - 1;
1093  maxCount = points.count();
1094  }
1095  else if ( placement == Vertex )
1096  {
1097  i = 0;
1098  maxCount = points.count();
1099  if ( points.first() == points.last() )
1100  isRing = true;
1101  }
1102  else
1103  {
1104  return;
1105  }
1106 
1107  if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
1108  {
1109  double distance;
1110  distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine;
1111  renderOffsetVertexAlongLine( points, i, distance, context );
1112  // restore original rotation
1113  mMarker->setAngle( origAngle );
1114  return;
1115  }
1116 
1117  for ( ; i < maxCount; ++i )
1118  {
1119  if ( isRing && placement == Vertex && i == points.count() - 1 )
1120  {
1121  continue; // don't draw the last marker - it has been drawn already
1122  }
1123  // rotate marker (if desired)
1124  if ( mRotateMarker )
1125  {
1126  double angle = markerAngle( points, isRing, i );
1127  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1128  }
1129 
1130  mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() );
1131  }
1132 
1133  // restore original rotation
1134  mMarker->setAngle( origAngle );
1135 }
1136 
1137 double QgsMarkerLineSymbolLayerV2::markerAngle( const QPolygonF& points, bool isRing, int vertex )
1138 {
1139  double angle = 0;
1140  const QPointF& pt = points[vertex];
1141 
1142  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1143  {
1144  int prevIndex = vertex - 1;
1145  int nextIndex = vertex + 1;
1146 
1147  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1148  {
1149  prevIndex = points.count() - 2;
1150  nextIndex = 1;
1151  }
1152 
1153  QPointF prevPoint, nextPoint;
1154  while ( prevIndex >= 0 )
1155  {
1156  prevPoint = points[ prevIndex ];
1157  if ( prevPoint != pt )
1158  {
1159  break;
1160  }
1161  --prevIndex;
1162  }
1163 
1164  while ( nextIndex < points.count() )
1165  {
1166  nextPoint = points[ nextIndex ];
1167  if ( nextPoint != pt )
1168  {
1169  break;
1170  }
1171  ++nextIndex;
1172  }
1173 
1174  if ( prevIndex >= 0 && nextIndex < points.count() )
1175  {
1176  angle = _averageAngle( prevPoint, pt, nextPoint );
1177  }
1178  }
1179  else //no ring and vertex is at start / at end
1180  {
1181  if ( vertex == 0 )
1182  {
1183  while ( vertex < points.size() - 1 )
1184  {
1185  const QPointF& nextPt = points[vertex+1];
1186  if ( pt != nextPt )
1187  {
1188  angle = MyLine( pt, nextPt ).angle();
1189  return angle;
1190  }
1191  ++vertex;
1192  }
1193  }
1194  else
1195  {
1196  // use last segment's angle
1197  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1198  {
1199  const QPointF& prevPt = points[vertex-1];
1200  if ( pt != prevPt )
1201  {
1202  angle = MyLine( prevPt, pt ).angle();
1203  return angle;
1204  }
1205  --vertex;
1206  }
1207  }
1208  }
1209  return angle;
1210 }
1211 
1212 void QgsMarkerLineSymbolLayerV2::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolV2RenderContext& context )
1213 {
1214  if ( points.isEmpty() )
1215  return;
1216 
1217  QgsRenderContext& rc = context.renderContext();
1218  double origAngle = mMarker->angle();
1219  if ( qgsDoubleNear( distance, 0.0 ) )
1220  {
1221  // rotate marker (if desired)
1222  if ( mRotateMarker )
1223  {
1224  bool isRing = false;
1225  if ( points.first() == points.last() )
1226  isRing = true;
1227  double angle = markerAngle( points, isRing, vertex );
1228  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1229  }
1230  mMarker->renderPoint( points[vertex], context.feature(), rc, -1, context.selected() );
1231  return;
1232  }
1233 
1234  int pointIncrement = distance > 0 ? 1 : -1;
1235  QPointF previousPoint = points[vertex];
1236  int startPoint = distance > 0 ? qMin( vertex + 1, points.count() - 1 ) : qMax( vertex - 1, 0 );
1237  int endPoint = distance > 0 ? points.count() - 1 : 0;
1238  double distanceLeft = qAbs( distance );
1239 
1240  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1241  {
1242  const QPointF& pt = points[i];
1243 
1244  if ( previousPoint == pt ) // must not be equal!
1245  continue;
1246 
1247  // create line segment
1248  MyLine l( previousPoint, pt );
1249 
1250  if ( distanceLeft < l.length() )
1251  {
1252  //destination point is in current segment
1253  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1254  // rotate marker (if desired)
1255  if ( mRotateMarker )
1256  {
1257  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1258  }
1259  mMarker->renderPoint( markerPoint, context.feature(), rc, -1, context.selected() );
1260  return;
1261  }
1262 
1263  distanceLeft -= l.length();
1264  previousPoint = pt;
1265  }
1266 
1267  //didn't find point
1268  return;
1269 }
1270 
1272 {
1273  if ( !points.isEmpty() )
1274  {
1275  // calc length
1276  qreal length = 0;
1277  QPolygonF::const_iterator it = points.constBegin();
1278  QPointF last = *it;
1279  for ( ++it; it != points.constEnd(); ++it )
1280  {
1281  length += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1282  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1283  last = *it;
1284  }
1285 
1286  // find the segment where the central point lies
1287  it = points.constBegin();
1288  last = *it;
1289  qreal last_at = 0, next_at = 0;
1290  QPointF next;
1291  int segment = 0;
1292  for ( ++it; it != points.constEnd(); ++it )
1293  {
1294  next = *it;
1295  next_at += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1296  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1297  if ( next_at >= length / 2 )
1298  break; // we have reached the center
1299  last = *it;
1300  last_at = next_at;
1301  segment++;
1302  }
1303 
1304  // find out the central point on segment
1305  MyLine l( last, next ); // for line angle
1306  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1307  QPointF pt = last + ( next - last ) * k;
1308 
1309  // draw the marker
1310  double origAngle = mMarker->angle();
1311  if ( mRotateMarker )
1312  mMarker->setAngle( origAngle + l.angle() * 180 / M_PI );
1313  mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() );
1314  if ( mRotateMarker )
1315  mMarker->setAngle( origAngle );
1316  }
1317 }
1318 
1319 
1321 {
1322  QgsStringMap map;
1323  map["rotate"] = ( mRotateMarker ? "1" : "0" );
1324  map["interval"] = QString::number( mInterval );
1325  map["offset"] = QString::number( mOffset );
1326  map["offset_along_line"] = QString::number( mOffsetAlongLine );
1327  map["offset_along_line_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetAlongLineUnit );
1328  map["offset_along_line_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetAlongLineMapUnitScale );
1329  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1330  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1331  map["interval_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mIntervalUnit );
1332  map["interval_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mIntervalMapUnitScale );
1333  if ( mPlacement == Vertex )
1334  map["placement"] = "vertex";
1335  else if ( mPlacement == LastVertex )
1336  map["placement"] = "lastvertex";
1337  else if ( mPlacement == FirstVertex )
1338  map["placement"] = "firstvertex";
1339  else if ( mPlacement == CentralPoint )
1340  map["placement"] = "centralpoint";
1341  else if ( mPlacement == CurvePoint )
1342  map["placement"] = "curvepoint";
1343  else
1344  map["placement"] = "interval";
1345 
1347  return map;
1348 }
1349 
1351 {
1352  return mMarker;
1353 }
1354 
1356 {
1357  if ( !symbol || symbol->type() != QgsSymbolV2::Marker )
1358  {
1359  delete symbol;
1360  return false;
1361  }
1362 
1363  delete mMarker;
1364  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
1365  mColor = mMarker->color();
1366  return true;
1367 }
1368 
1370 {
1371  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( mRotateMarker, mInterval );
1372  x->setSubSymbol( mMarker->clone() );
1373  x->setOffset( mOffset );
1374  x->setPlacement( mPlacement );
1375  x->setOffsetUnit( mOffsetUnit );
1377  x->setIntervalUnit( mIntervalUnit );
1378  x->setIntervalMapUnitScale( mIntervalMapUnitScale );
1379  x->setOffsetAlongLine( mOffsetAlongLine );
1380  x->setOffsetAlongLineMapUnitScale( mOffsetAlongLineMapUnitScale );
1381  x->setOffsetAlongLineUnit( mOffsetAlongLineUnit );
1383  copyPaintEffect( x );
1384  return x;
1385 }
1386 
1388 {
1389  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1390  {
1391  QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
1392  if ( !props.value( "uom", "" ).isEmpty() )
1393  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
1394  element.appendChild( symbolizerElem );
1395 
1396  // <Geometry>
1397  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
1398 
1399  QString gap;
1400  switch ( mPlacement )
1401  {
1402  case FirstVertex:
1403  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "firstPoint" ) );
1404  break;
1405  case LastVertex:
1406  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) );
1407  break;
1408  case CentralPoint:
1409  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "centralPoint" ) );
1410  break;
1411  case Vertex:
1412  // no way to get line/polygon's vertices, use a VendorOption
1413  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "points" ) );
1414  break;
1415  default:
1416  gap = qgsDoubleToString( mInterval );
1417  break;
1418  }
1419 
1420  if ( !mRotateMarker )
1421  {
1422  // markers in LineSymbolizer must be drawn following the line orientation,
1423  // use a VendorOption when no marker rotation
1424  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "rotateMarker", "0" ) );
1425  }
1426 
1427  // <Stroke>
1428  QDomElement strokeElem = doc.createElement( "se:Stroke" );
1429  symbolizerElem.appendChild( strokeElem );
1430 
1431  // <GraphicStroke>
1432  QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" );
1433  strokeElem.appendChild( graphicStrokeElem );
1434 
1435  QgsSymbolLayerV2 *layer = mMarker->symbolLayer( i );
1436  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
1437  if ( !markerLayer )
1438  {
1439  graphicStrokeElem.appendChild( doc.createComment( QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() ) ) );
1440  }
1441  else
1442  {
1443  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1444  }
1445 
1446  if ( !gap.isEmpty() )
1447  {
1448  QDomElement gapElem = doc.createElement( "se:Gap" );
1449  QgsSymbolLayerV2Utils::createFunctionElement( doc, gapElem, gap );
1450  graphicStrokeElem.appendChild( gapElem );
1451  }
1452 
1453  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1454  {
1455  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
1456  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( mOffset ) ) );
1457  symbolizerElem.appendChild( perpOffsetElem );
1458  }
1459  }
1460 }
1461 
1463 {
1464  QgsDebugMsg( "Entered." );
1465 
1466  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1467  if ( strokeElem.isNull() )
1468  return nullptr;
1469 
1470  QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
1471  if ( graphicStrokeElem.isNull() )
1472  return nullptr;
1473 
1474  // retrieve vendor options
1475  bool rotateMarker = true;
1476  Placement placement = Interval;
1477 
1478  QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( element );
1479  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1480  {
1481  if ( it.key() == "placement" )
1482  {
1483  if ( it.value() == "points" ) placement = Vertex;
1484  else if ( it.value() == "firstPoint" ) placement = FirstVertex;
1485  else if ( it.value() == "lastPoint" ) placement = LastVertex;
1486  else if ( it.value() == "centralPoint" ) placement = CentralPoint;
1487  }
1488  else if ( it.value() == "rotateMarker" )
1489  {
1490  rotateMarker = it.value() == "0";
1491  }
1492  }
1493 
1494  QgsMarkerSymbolV2 *marker = nullptr;
1495 
1497  if ( l )
1498  {
1499  QgsSymbolLayerV2List layers;
1500  layers.append( l );
1501  marker = new QgsMarkerSymbolV2( layers );
1502  }
1503 
1504  if ( !marker )
1505  return nullptr;
1506 
1507  double interval = 0.0;
1508  QDomElement gapElem = graphicStrokeElem.firstChildElement( "Gap" );
1509  if ( !gapElem.isNull() )
1510  {
1511  bool ok;
1512  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1513  if ( ok )
1514  interval = d;
1515  }
1516 
1517  double offset = 0.0;
1518  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( "PerpendicularOffset" );
1519  if ( !perpOffsetElem.isNull() )
1520  {
1521  bool ok;
1522  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1523  if ( ok )
1524  offset = d;
1525  }
1526 
1527  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotateMarker );
1528  x->setPlacement( placement );
1529  x->setInterval( interval );
1530  x->setSubSymbol( marker );
1531  x->setOffset( offset );
1532  return x;
1533 }
1534 
1536 {
1537  mMarker->setSize( width );
1538 }
1539 
1541 {
1542  if ( property == QgsSymbolLayerV2::EXPR_WIDTH && mMarker && dataDefined )
1543  {
1544  mMarker->setDataDefinedSize( *dataDefined );
1545  }
1546  QgsLineSymbolLayerV2::setDataDefinedProperty( property, dataDefined );
1547 }
1548 
1550 {
1551  return mMarker->size();
1552 }
1553 
1555 {
1557  mIntervalUnit = unit;
1558  mOffsetUnit = unit;
1559  mOffsetAlongLineUnit = unit;
1560 }
1561 
1563 {
1565  if ( mIntervalUnit != unit || mOffsetUnit != unit || mOffsetAlongLineUnit != unit )
1566  {
1567  return QgsSymbolV2::Mixed;
1568  }
1569  return unit;
1570 }
1571 
1573 {
1575  mIntervalMapUnitScale = scale;
1576  mOffsetMapUnitScale = scale;
1577  mOffsetAlongLineMapUnitScale = scale;
1578 }
1579 
1581 {
1582  if ( QgsLineSymbolLayerV2::mapUnitScale() == mIntervalMapUnitScale &&
1583  mIntervalMapUnitScale == mOffsetMapUnitScale &&
1584  mOffsetMapUnitScale == mOffsetAlongLineMapUnitScale )
1585  {
1586  return mOffsetMapUnitScale;
1587  }
1588  return QgsMapUnitScale();
1589 }
1590 
1592 {
1594  if ( mMarker )
1595  attr.unite( mMarker->usedAttributes() );
1596  return attr;
1597 }
1598 
1600 {
1601  return ( mMarker->size() / 2.0 ) + mOffset;
1602 }
1603 
1604 
1605 
virtual QSet< QString > usedAttributes() const
Returns the set of attributes referenced by the layer.
static double mapUnitScaleFactor(double scaleDenominator, QgsSymbolV2::OutputUnit symbolUnits, QGis::UnitType mapUnits)
void setIntervalUnit(QgsSymbolV2::OutputUnit unit)
static QString encodeOutputUnit(QgsSymbolV2::OutputUnit unit)
const QgsCurveV2 * exteriorRing() const
#define DEFAULT_SIMPLELINE_PENSTYLE
#define DEFAULT_MARKERLINE_ROTATE
static const QString EXPR_JOINSTYLE
double estimateMaxBleed() const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
void setStyle(Qt::PenStyle style)
double dxfWidth(const QgsDxfExport &e, QgsSymbolV2RenderContext &context) const override
get line width
OutputUnit
The unit of the output.
Definition: qgssymbolv2.h:65
float threshold() const
Gets the simplification threshold of the vector layer managed.
int renderHints() const
Definition: qgssymbolv2.h:379
QVector< qreal > dxfCustomDashPattern(QgsSymbolV2::OutputUnit &unit) const override
get dash pattern
static bool lineFromSld(QDomElement &element, Qt::PenStyle &penStyle, QColor &color, double &width, Qt::PenJoinStyle *penJoinStyle=nullptr, Qt::PenCapStyle *penCapStyle=nullptr, QVector< qreal > *customDashPattern=nullptr, double *dashOffset=nullptr)
A container class for data source field mapping or expression.
bool contains(const Key &key) const
virtual double width() const
void setClipPath(const QPainterPath &path, Qt::ClipOperation operation)
double markerAngle(const QPolygonF &points, bool isRing, int vertex)
qreal alphaF() const
void renderPolyline(const QPolygonF &points, QgsSymbolV2RenderContext &context) override
static double _averageAngle(QPointF prevPt, QPointF pt, QPointF nextPt)
void setRenderHint(RenderHint hint, bool on)
QDomNode appendChild(const QDomNode &newChild)
#define DEFAULT_MARKERLINE_INTERVAL
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
double x() const
Returns the point&#39;s x-coordinate.
Definition: qgspointv2.h:68
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
SymbolType type() const
Definition: qgssymbolv2.h:107
RenderHints renderHints() const
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
QColor selectionColor() const
const QgsVectorSimplifyMethod & vectorSimplifyMethod() const
Added in QGIS v2.4.
QString nodeValue() const
virtual void setWidth(double width) override
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setCustomDashPatternUnit(QgsSymbolV2::OutputUnit unit)
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
static QString encodeColor(const QColor &color)
Placement
Defines how/where the marker should be placed on the line.
QgsMapUnitScale mCustomDashPatternMapUnitScale
void renderPolyline(const QPolygonF &points, QgsSymbolV2RenderContext &context) override
static const QString EXPR_WIDTH
static const QString EXPR_CUSTOMDASH
void drawPolyline(const QPointF *points, int pointCount)
static QgsStringMap getVendorOptionList(QDomElement &element)
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Writes the symbol layer definition as a SLD XML element.
QGis::UnitType mapUnits() const
Retrieve map units.
Definition: qgsdxfexport.h:96
const_iterator constEnd() const
void startRender(QgsSymbolV2RenderContext &context) override
const T & at(int i) const
T & last()
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
set layer&#39;s subsymbol. takes ownership of the passed symbol
QGis::GeometryType type() const
Returns type of the geometry as a QGis::GeometryType.
void setPenJoinStyle(Qt::PenJoinStyle style)
void save()
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
double dxfOffset(const QgsDxfExport &e, QgsSymbolV2RenderContext &context) const override
get offset
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=nullptr)
Create ogr feature style string for pen.
void setJoinStyle(Qt::PenJoinStyle style)
static Qt::PenJoinStyle decodePenJoinStyle(const 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)
virtual bool hasDataDefinedProperty(const QString &property) const
Checks whether the layer has a matching data defined property and if that property is currently activ...
T & first()
static const QString EXPR_OFFSET_ALONG_LINE
QgsMapUnitScale mWidthMapUnitScale
void setIntervalMapUnitScale(const QgsMapUnitScale &scale)
const QgsCoordinateTransform * coordinateTransform() const
QString layerType() const override
Returns a string that represents this layer type.
double toDouble(bool *ok) const
void setMapUnitScale(const QgsMapUnitScale &scale) override
QgsMapUnitScale mOffsetMapUnitScale
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:353
static const QString EXPR_OFFSET
Qt::PenStyle penStyle() const
QgsSymbolV2::OutputUnit mOffsetUnit
void setWidthUnit(QgsSymbolV2::OutputUnit unit)
Marker symbol.
Definition: qgssymbolv2.h:81
int size() const
void setOffsetAlongLine(double offsetAlongLine)
Sets the the offset along the line for the marker placement.
void setInterval(double interval)
The interval between individual markers.
double y() const
Returns the point&#39;s y-coordinate.
Definition: qgspointv2.h:74
QgsMapUnitScale mapUnitScale() const override
static QgsSymbolV2::OutputUnit decodeOutputUnit(const QString &str)
qreal alpha() const
Get alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:372
void setDrawInsidePolygon(bool drawInsidePolygon)
static QString encodePenStyle(Qt::PenStyle style)
void setCapStyle(Qt::PenCapStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:34
QgsSimpleLineSymbolLayerV2 * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QColor color() const
Mixed units in symbol layers.
Definition: qgssymbolv2.h:69
void transformInPlace(double &x, double &y, double &z, TransformDirection direction=ForwardTransform) const
void transformInPlace(double &x, double &y) const
Transform device coordinates to map coordinates.
static QgsSymbolLayerV2 * createMarkerLayerFromSld(QDomElement &element)
The output shall be in millimeters.
Definition: qgssymbolv2.h:67
static const QString EXPR_PLACEMENT
QString number(int n, int base)
int count(const T &value) const
qreal x() const
qreal y() const
void append(const T &value)
void setDashPattern(const QVector< qreal > &pattern)
void addPolygon(const QPolygonF &polygon)
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:384
void renderPolygonOutline(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context) override
QgsSymbolV2::OutputUnit outputUnit() const override
static double convertToPainterUnits(const QgsRenderContext &c, double size, QgsSymbolV2::OutputUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale())
Converts a size from the specied units to painter units.
Utility class for identifying a unique vertex within a geometry.
The geometries can be rendered with &#39;AntiAliasing&#39; disabled because of it is &#39;1-pixel size&#39;...
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
void setPen(const QColor &color)
virtual QColor color() const override
The fill color.
QgsSymbolV2::OutputUnit mWidthUnit
void setAttribute(const QString &name, const QString &value)
void setWidthMapUnitScale(const QgsMapUnitScale &scale)
QVector< qreal > mCustomDashVector
Vector with an even number of entries for the.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspointv2.h:34
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:341
#define DEFAULT_SIMPLELINE_WIDTH
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
void renderPolygonOutline(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context) override
#define M_PI
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
Create a new MarkerLineSymbolLayerV2 from SLD.
#define DEFAULT_SIMPLELINE_CAPSTYLE
#define DEFAULT_SIMPLELINE_JOINSTYLE
void setWidthF(qreal width)
virtual bool hasCurvedSegments() const
Returns true if the geometry contains curved segments.
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
void setBrush(const QBrush &brush)
QString layerType() const override
Returns a string that represents this layer type.
double rasterScaleFactor() const
void stopRender(QgsSymbolV2RenderContext &context) override
virtual QColor color() const
The fill color.
void setOffsetAlongLineMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale used for calculating the offset in map units along line for markers...
virtual bool nextVertex(QgsVertexId &id, QgsPointV2 &vertex) const =0
Returns next vertex id and coordinates.
iterator end()
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
void setOffsetAlongLineUnit(QgsSymbolV2::OutputUnit unit)
Sets the unit used for calculating the offset along line for markers.
void setColor(const QColor &color)
virtual Q_DECL_DEPRECATED void prepareExpressions(const QgsFields *fields, double scale=-1.0)
Prepares all data defined property expressions for evaluation.
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)
static void lineToSld(QDomDocument &doc, QDomElement &element, Qt::PenStyle penStyle, const QColor &color, double width=-1, const Qt::PenJoinStyle *penJoinStyle=nullptr, const Qt::PenCapStyle *penCapStyle=nullptr, const QVector< qreal > *customDashPattern=nullptr, double dashOffset=0.0)
virtual bool hasDataDefinedProperties() const
Checks whether the layer has any associated data defined properties.
void setCustomDashVector(const QVector< qreal > &vector)
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
Create a new MarkerLineSymbolLayerV2.
iterator begin()
static Qt::PenStyle decodePenStyle(const QString &str)
static const QString EXPR_CAPSTYLE
QgsMarkerLineSymbolLayerV2 * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
QDomText createTextNode(const QString &value)
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 const QString EXPR_COLOR
void setDataDefinedProperty(const QString &property, QgsDataDefined *dataDefined) override
Sets a data defined property for the layer.
static QString encodeRealVector(const QVector< qreal > &v)
QgsSymbolV2::OutputUnit outputUnit() const override
QgsSymbolV2::OutputUnit mCustomDashPatternUnit
virtual QString layerType() const =0
Returns a string that represents this layer type.
const QgsCurveV2 * interiorRing(int i) const
double symbologyScaleDenominator() const
Retrieve reference scale for output.
Definition: qgsdxfexport.h:83
bool isNull() const
void restore()
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
const T & at(int i) const
#define DEFAULT_SIMPLELINE_COLOR
const_iterator constBegin() const
Contains information about the context of a rendering operation.
QgsMapUnitScale mapUnitScale() const override
QDomNode firstChild() const
QPainter * painter()
void setMapUnitScale(const QgsMapUnitScale &scale) override
void drawPath(const QPainterPath &path)
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
QSet< T > & unite(const QSet< T > &other)
void setPlacement(Placement p)
The placement of the markers.
Struct for storing maximum and minimum scales for measurements in map units.
Qt::PenStyle dxfPenStyle() const override
get pen style
QList< QPolygonF > offsetLine(QPolygonF polyline, double dist, QGis::GeometryType geometryType)
calculate geometry shifted by a specified distance
QgsSimpleLineSymbolLayerV2(const QColor &color=DEFAULT_SIMPLELINE_COLOR, double width=DEFAULT_SIMPLELINE_WIDTH, Qt::PenStyle penStyle=DEFAULT_SIMPLELINE_PENSTYLE)
bool isEmpty() const
void setCustomDashPatternMapUnitScale(const QgsMapUnitScale &scale)
double offset() const
const QgsAbstractGeometryV2 * geometry() const
Returns pointer to the unsegmentized geometry.
void stopRender(QgsSymbolV2RenderContext &context) override
QgsRenderContext & renderContext()
Definition: qgssymbolv2.h:355
void setX(qreal x)
void setY(qreal y)
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
QDomElement firstChildElement(const QString &tagName) const
QColor dxfColor(QgsSymbolV2RenderContext &context) const override
get color
int count(const T &value) const
QgsMapUnitScale mapUnitScale() const override
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
SimplifyHints simplifyHints() const
Gets the simplification hints of the vector layer managed.
const QgsGeometry * constGeometry() const
Gets a const pointer to the geometry object associated with this feature.
Definition: qgsfeature.cpp:82
qreal & rx()
qreal & ry()
QDomComment createComment(const QString &value)
Class for doing transforms between two map coordinate systems.
qreal widthF() const
void setColor(const QColor &color) override
The fill color.
const QgsMapToPixel & mapToPixel() const
const QgsFields * fields() const
Fields of the layer.
Definition: qgssymbolv2.h:390
void push_back(const T &value)
void setAlphaF(qreal alpha)
bool selected() const
Definition: qgssymbolv2.h:376
double toDouble(bool *ok) const
static QColor decodeColor(const QString &str)
Abstract base class for marker symbol layers.
typedef const_iterator
Curve polygon geometry type.
void restoreDataDefinedProperties(const QgsStringMap &stringMap)
Restores all data defined properties from string map.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
const_iterator constEnd() const
QDomElement createElement(const QString &tagName)
const_iterator constBegin() const
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
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...
int size() const
int compare(const QString &other) const
static const QString EXPR_LINE_STYLE
QString toString() const
virtual bool setSubSymbol(QgsSymbolV2 *symbol)
set layer&#39;s subsymbol. takes ownership of the passed symbol
void saveDataDefinedProperties(QgsStringMap &stringMap) const
Saves all data defined properties to a string map.
static const QString EXPR_INTERVAL
void renderPolylineCentral(const QPolygonF &points, QgsSymbolV2RenderContext &context)
void setGeometry(const QgsAbstractGeometryV2 *geometry)
Sets pointer to original (unsegmentized) geometry.
virtual double width() const override
QgsSymbolV2::OutputUnit widthUnit() const
QSet< QString > usedAttributes() const override
Returns the set of attributes referenced by the layer.
Qt::PenJoinStyle penJoinStyle() const
void renderPolylineVertex(const QPolygonF &points, QgsSymbolV2RenderContext &context, Placement placement=Vertex)
void copyDataDefinedProperties(QgsSymbolLayerV2 *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
Qt::PenCapStyle penCapStyle() const
const T value(const Key &key) const
static QString encodePenCapStyle(Qt::PenCapStyle style)
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
virtual Q_DECL_DEPRECATED void setDataDefinedProperty(const QString &property, const QString &expressionString)
Sets a data defined expression for a property.
virtual Q_DECL_DEPRECATED QVariant evaluateDataDefinedProperty(const QString &property, const QgsFeature *feature, const QVariant &defaultVal=QVariant(), bool *ok=nullptr) const
Evaluates the matching data defined property and returns the calculated value.