QGIS API Documentation  2.17.0-Master (eef6f05)
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;
415  QgsSymbolLayerV2Utils::lineToSld( doc, strokeElem, penStyle, mColor, width,
416  &mPenJoinStyle, &mPenCapStyle, &customDashVector );
417 
418  // <se:PerpendicularOffset>
419  if ( !qgsDoubleNear( mOffset, 0.0 ) )
420  {
421  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
423  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
424  symbolizerElem.appendChild( perpOffsetElem );
425  }
426 }
427 
428 QString QgsSimpleLineSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
429 {
430  if ( mUseCustomDashPattern )
431  {
432  return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor,
435  }
436  else
437  {
438  return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor, mPen.color(), mPenJoinStyle,
440  }
441 }
442 
444 {
445  QgsDebugMsg( "Entered." );
446 
447  QDomElement strokeElem = element.firstChildElement( "Stroke" );
448  if ( strokeElem.isNull() )
449  return nullptr;
450 
451  Qt::PenStyle penStyle;
452  QColor color;
453  double width;
454  Qt::PenJoinStyle penJoinStyle;
455  Qt::PenCapStyle penCapStyle;
457 
458  if ( !QgsSymbolLayerV2Utils::lineFromSld( strokeElem, penStyle,
459  color, width,
460  &penJoinStyle, &penCapStyle,
461  &customDashVector ) )
462  return nullptr;
463 
464  double offset = 0.0;
465  QDomElement perpOffsetElem = element.firstChildElement( "PerpendicularOffset" );
466  if ( !perpOffsetElem.isNull() )
467  {
468  bool ok;
469  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
470  if ( ok )
471  offset = d;
472  }
473 
474  QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
475  l->setOffset( offset );
476  l->setPenJoinStyle( penJoinStyle );
477  l->setPenCapStyle( penCapStyle );
478  l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine );
479  l->setCustomDashVector( customDashVector );
480  return l;
481 }
482 
483 void QgsSimpleLineSymbolLayerV2::applySizeScale( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen )
484 {
486  pen.setWidthF( scaledWidth );
487  selPen.setWidthF( scaledWidth );
488 }
489 
490 void QgsSimpleLineSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen, double& offset )
491 {
492  if ( !hasDataDefinedProperties() )
493  return; // shortcut
494 
495  //data defined properties
496  bool hasStrokeWidthExpression = false;
498  {
499  context.setOriginalValueVariable( mWidth );
500  double scaledWidth = QgsSymbolLayerV2Utils::convertToPainterUnits( context.renderContext(),
503  pen.setWidthF( scaledWidth );
504  selPen.setWidthF( scaledWidth );
505  hasStrokeWidthExpression = true;
506  }
507 
508  //color
509  bool ok;
511  {
514  if ( ok )
515  pen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorString ) );
516  }
517 
518  //offset
520  {
523  }
524 
525  //dash dot vector
527  {
529  double dashWidthDiv = mPen.widthF();
530 
531  if ( hasStrokeWidthExpression )
532  {
533  dashWidthDiv = pen.widthF();
534  scaledWidth = pen.widthF();
535  }
536 
537  //fix dash pattern width in Qt 4.8
538  QStringList versionSplit = QString( qVersion() ).split( '.' );
539  if ( versionSplit.size() > 1
540  && versionSplit.at( 1 ).toInt() >= 8
541  && ( scaledWidth * context.renderContext().rasterScaleFactor() ) < 1.0 )
542  {
543  dashWidthDiv = 1.0;
544  }
545 
546  QVector<qreal> dashVector;
548  if ( ok )
549  {
550  QStringList::const_iterator dashIt = dashList.constBegin();
551  for ( ; dashIt != dashList.constEnd(); ++dashIt )
552  {
554  }
555  pen.setDashPattern( dashVector );
556  }
557  }
558 
559  //line style
561  {
564  if ( ok )
565  pen.setStyle( QgsSymbolLayerV2Utils::decodePenStyle( lineStyleString ) );
566  }
567 
568  //join style
570  {
573  if ( ok )
575  }
576 
577  //cap style
579  {
582  if ( ok )
583  pen.setCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( capStyleString ) );
584  }
585 }
586 
588 {
589  if ( mDrawInsidePolygon )
590  {
591  //set to clip line to the interior of polygon, so we expect no bleed
592  return 0;
593  }
594  else
595  {
596  return ( mWidth / 2.0 ) + mOffset;
597  }
598 }
599 
601 {
602  unit = mCustomDashPatternUnit;
604 }
605 
607 {
608  return mPenStyle;
609 }
610 
612 {
613  double width = mWidth;
614 
616  {
617  context.setOriginalValueVariable( mWidth );
619  }
620  else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
621  {
623  }
624 
625  return width * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), widthUnit(), e.mapUnits() );
626 }
627 
629 {
631  {
632  bool ok;
635  if ( ok )
636  return ( QgsSymbolLayerV2Utils::decodeColor( colorString ) );
637  }
638  return mColor;
639 }
640 
642 {
643  Q_UNUSED( e );
644  double offset = mOffset;
645 
647  {
650  }
651  return offset;
652 }
653 
655 
657 
658 class MyLine
659 {
660  public:
661  MyLine( QPointF p1, QPointF p2 )
662  : mVertical( false )
663  , mIncreasing( false )
664  , mT( 0.0 )
665  , mLength( 0.0 )
666  {
667  if ( p1 == p2 )
668  return; // invalid
669 
670  // tangent and direction
671  if ( qgsDoubleNear( p1.x(), p2.x() ) )
672  {
673  // vertical line - tangent undefined
674  mVertical = true;
675  mIncreasing = ( p2.y() > p1.y() );
676  }
677  else
678  {
679  mVertical = false;
680  mT = float( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
681  mIncreasing = ( p2.x() > p1.x() );
682  }
683 
684  // length
685  double x = ( p2.x() - p1.x() );
686  double y = ( p2.y() - p1.y() );
687  mLength = sqrt( x * x + y * y );
688  }
689 
690  // return angle in radians
691  double angle()
692  {
693  double a = ( mVertical ? M_PI / 2 : atan( mT ) );
694 
695  if ( !mIncreasing )
696  a += M_PI;
697  return a;
698  }
699 
700  // return difference for x,y when going along the line with specified interval
701  QPointF diffForInterval( double interval )
702  {
703  if ( mVertical )
704  return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
705 
706  double alpha = atan( mT );
707  double dx = cos( alpha ) * interval;
708  double dy = sin( alpha ) * interval;
709  return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
710  }
711 
712  double length() { return mLength; }
713 
714  protected:
715  bool mVertical;
716  bool mIncreasing;
717  double mT;
718  double mLength;
719 };
720 
722 
723 QgsMarkerLineSymbolLayerV2::QgsMarkerLineSymbolLayerV2( bool rotateMarker, double interval )
724 {
725  mRotateMarker = rotateMarker;
726  mInterval = interval;
727  mIntervalUnit = QgsSymbolV2::MM;
728  mMarker = nullptr;
729  mPlacement = Interval;
730  mOffsetAlongLine = 0;
731  mOffsetAlongLineUnit = QgsSymbolV2::MM;
732 
734 }
735 
737 {
738  delete mMarker;
739 }
740 
742 {
743  bool rotate = DEFAULT_MARKERLINE_ROTATE;
744  double interval = DEFAULT_MARKERLINE_INTERVAL;
745 
746 
747  if ( props.contains( "interval" ) )
748  interval = props["interval"].toDouble();
749  if ( props.contains( "rotate" ) )
750  rotate = ( props["rotate"] == "1" );
751 
752  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotate, interval );
753  if ( props.contains( "offset" ) )
754  {
755  x->setOffset( props["offset"].toDouble() );
756  }
757  if ( props.contains( "offset_unit" ) )
758  {
759  x->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
760  }
761  if ( props.contains( "interval_unit" ) )
762  {
763  x->setIntervalUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["interval_unit"] ) );
764  }
765  if ( props.contains( "offset_along_line" ) )
766  {
767  x->setOffsetAlongLine( props["offset_along_line"].toDouble() );
768  }
769  if ( props.contains( "offset_along_line_unit" ) )
770  {
771  x->setOffsetAlongLineUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_along_line_unit"] ) );
772  }
773  if ( props.contains(( "offset_along_line_map_unit_scale" ) ) )
774  {
775  x->setOffsetAlongLineMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_along_line_map_unit_scale"] ) );
776  }
777 
778  if ( props.contains( "offset_map_unit_scale" ) )
779  {
780  x->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
781  }
782  if ( props.contains( "interval_map_unit_scale" ) )
783  {
784  x->setIntervalMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["interval_map_unit_scale"] ) );
785  }
786 
787  if ( props.contains( "placement" ) )
788  {
789  if ( props["placement"] == "vertex" )
790  x->setPlacement( Vertex );
791  else if ( props["placement"] == "lastvertex" )
792  x->setPlacement( LastVertex );
793  else if ( props["placement"] == "firstvertex" )
794  x->setPlacement( FirstVertex );
795  else if ( props["placement"] == "centralpoint" )
796  x->setPlacement( CentralPoint );
797  else if ( props["placement"] == "curvepoint" )
798  x->setPlacement( CurvePoint );
799  else
800  x->setPlacement( Interval );
801  }
802 
803  x->restoreDataDefinedProperties( props );
804 
805  return x;
806 }
807 
809 {
810  return "MarkerLine";
811 }
812 
814 {
815  mMarker->setColor( color );
816  mColor = color;
817 }
818 
820 {
821  return mMarker ? mMarker->color() : mColor;
822 }
823 
825 {
826  mMarker->setAlpha( context.alpha() );
827 
828  // if being rotated, it gets initialized with every line segment
829  int hints = 0;
830  if ( mRotateMarker )
834  mMarker->setRenderHints( hints );
835 
836  mMarker->startRender( context.renderContext(), context.fields() );
837 
838  //prepare expressions for data defined properties
839  prepareExpressions( context );
840 }
841 
843 {
844  mMarker->stopRender( context.renderContext() );
845 }
846 
848 {
849  double offset = mOffset;
850 
852  {
855  }
856 
857  Placement placement = mPlacement;
858 
859  bool ok;
861  {
863  if ( ok )
864  {
865  if ( placementString.compare( "vertex", Qt::CaseInsensitive ) == 0 )
866  {
867  placement = Vertex;
868  }
869  else if ( placementString.compare( "lastvertex", Qt::CaseInsensitive ) == 0 )
870  {
871  placement = LastVertex;
872  }
873  else if ( placementString.compare( "firstvertex", Qt::CaseInsensitive ) == 0 )
874  {
875  placement = FirstVertex;
876  }
877  else if ( placementString.compare( "centerpoint", Qt::CaseInsensitive ) == 0 )
878  {
879  placement = CentralPoint;
880  }
881  else if ( placementString.compare( "curvepoint", Qt::CaseInsensitive ) == 0 )
882  {
883  placement = CurvePoint;
884  }
885  else
886  {
887  placement = Interval;
888  }
889  }
890  }
891 
892  if ( qgsDoubleNear( offset, 0.0 ) )
893  {
894  if ( placement == Interval )
895  renderPolylineInterval( points, context );
896  else if ( placement == CentralPoint )
897  renderPolylineCentral( points, context );
898  else
899  renderPolylineVertex( points, context, placement );
900  }
901  else
902  {
903  context.renderContext().setGeometry( nullptr ); //always use segmented geometry with offset
905 
906  for ( int part = 0; part < mline.count(); ++part )
907  {
908  const QPolygonF &points2 = mline[ part ];
909 
910  if ( placement == Interval )
911  renderPolylineInterval( points2, context );
912  else if ( placement == CentralPoint )
913  renderPolylineCentral( points2, context );
914  else
915  renderPolylineVertex( points2, context, placement );
916  }
917  }
918 }
919 
921 {
922  const QgsCurvePolygonV2* curvePolygon = dynamic_cast<const QgsCurvePolygonV2*>( context.renderContext().geometry() );
923 
924  if ( curvePolygon )
925  {
926  context.renderContext().setGeometry( curvePolygon->exteriorRing() );
927  }
928  renderPolyline( points, context );
929  if ( rings )
930  {
931  mOffset = -mOffset; // invert the offset for rings!
932  for ( int i = 0; i < rings->size(); ++i )
933  {
934  if ( curvePolygon )
935  {
936  context.renderContext().setGeometry( curvePolygon->interiorRing( i ) );
937  }
938  renderPolyline( rings->at( i ), context );
939  }
940  mOffset = -mOffset;
941  }
942 }
943 
945 {
946  if ( points.isEmpty() )
947  return;
948 
949  QPointF lastPt = points[0];
950  double lengthLeft = 0; // how much is left until next marker
951  bool first = mOffsetAlongLine ? false : true; //only draw marker at first vertex when no offset along line is set
952 
953  QgsRenderContext& rc = context.renderContext();
954  double interval = mInterval;
955 
957  {
958  context.setOriginalValueVariable( mInterval );
959  interval = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_INTERVAL, context, mInterval ).toDouble();
960  }
961  if ( interval <= 0 )
962  {
963  interval = 0.1;
964  }
965  double offsetAlongLine = mOffsetAlongLine;
967  {
968  context.setOriginalValueVariable( mOffsetAlongLine );
969  offsetAlongLine = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET_ALONG_LINE, context, mOffsetAlongLine ).toDouble();
970  }
971 
972  double painterUnitInterval = QgsSymbolLayerV2Utils::convertToPainterUnits( rc, interval, mIntervalUnit, mIntervalMapUnitScale );
973  lengthLeft = painterUnitInterval - QgsSymbolLayerV2Utils::convertToPainterUnits( rc, offsetAlongLine, mIntervalUnit, mIntervalMapUnitScale );
974 
975  for ( int i = 1; i < points.count(); ++i )
976  {
977  const QPointF& pt = points[i];
978 
979  if ( lastPt == pt ) // must not be equal!
980  continue;
981 
982  // for each line, find out dx and dy, and length
983  MyLine l( lastPt, pt );
984  QPointF diff = l.diffForInterval( painterUnitInterval );
985 
986  // if there's some length left from previous line
987  // use only the rest for the first point in new line segment
988  double c = 1 - lengthLeft / painterUnitInterval;
989 
990  lengthLeft += l.length();
991 
992  // rotate marker (if desired)
993  if ( mRotateMarker )
994  {
995  mMarker->setLineAngle( l.angle() * 180 / M_PI );
996  }
997 
998  // draw first marker
999  if ( first )
1000  {
1001  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
1002  first = false;
1003  }
1004 
1005  // while we're not at the end of line segment, draw!
1006  while ( lengthLeft > painterUnitInterval )
1007  {
1008  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
1009  lastPt += c * diff;
1010  lengthLeft -= painterUnitInterval;
1011  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
1012  c = 1; // reset c (if wasn't 1 already)
1013  }
1014 
1015  lastPt = pt;
1016  }
1017 }
1018 
1019 static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt )
1020 {
1021  // calc average angle between the previous and next point
1022  double a1 = MyLine( prevPt, pt ).angle();
1023  double a2 = MyLine( pt, nextPt ).angle();
1024  double unitX = cos( a1 ) + cos( a2 ), unitY = sin( a1 ) + sin( a2 );
1025 
1026  return atan2( unitY, unitX );
1027 }
1028 
1030 {
1031  if ( points.isEmpty() )
1032  return;
1033 
1034  QgsRenderContext& rc = context.renderContext();
1035 
1036  double origAngle = mMarker->angle();
1037  int i, maxCount;
1038  bool isRing = false;
1039 
1040  double offsetAlongLine = mOffsetAlongLine;
1042  {
1043  context.setOriginalValueVariable( mOffsetAlongLine );
1044  offsetAlongLine = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET_ALONG_LINE, context, mOffsetAlongLine ).toDouble();
1045  }
1046  if ( !qgsDoubleNear( offsetAlongLine, 0.0 ) )
1047  {
1048  //scale offset along line
1049  offsetAlongLine = QgsSymbolLayerV2Utils::convertToPainterUnits( rc, offsetAlongLine, mOffsetAlongLineUnit, mOffsetAlongLineMapUnitScale );
1050  }
1051 
1052  if ( qgsDoubleNear( offsetAlongLine, 0.0 ) && context.renderContext().geometry()
1053  && context.renderContext().geometry()->hasCurvedSegments() && ( placement == Vertex || placement == CurvePoint ) )
1054  {
1056  const QgsMapToPixel& mtp = context.renderContext().mapToPixel();
1057 
1058  QgsVertexId vId;
1059  QgsPointV2 vPoint;
1060  double x, y, z;
1061  QPointF mapPoint;
1062  while ( context.renderContext().geometry()->nextVertex( vId, vPoint ) )
1063  {
1064  if (( placement == Vertex && vId.type == QgsVertexId::SegmentVertex )
1065  || ( placement == CurvePoint && vId.type == QgsVertexId::CurveVertex ) )
1066  {
1067  //transform
1068  x = vPoint.x(), y = vPoint.y();
1069  z = 0.0;
1070  if ( ct )
1071  {
1072  ct->transformInPlace( x, y, z );
1073  }
1074  mapPoint.setX( x );
1075  mapPoint.setY( y );
1076  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1077  if ( mRotateMarker )
1078  {
1079  double angle = context.renderContext().geometry()->vertexAngle( vId );
1080  mMarker->setAngle( angle * 180 / M_PI );
1081  }
1082  mMarker->renderPoint( mapPoint, context.feature(), rc, -1, context.selected() );
1083  }
1084  }
1085  return;
1086  }
1087 
1088  if ( placement == FirstVertex )
1089  {
1090  i = 0;
1091  maxCount = 1;
1092  }
1093  else if ( placement == LastVertex )
1094  {
1095  i = points.count() - 1;
1096  maxCount = points.count();
1097  }
1098  else if ( placement == Vertex )
1099  {
1100  i = 0;
1101  maxCount = points.count();
1102  if ( points.first() == points.last() )
1103  isRing = true;
1104  }
1105  else
1106  {
1107  return;
1108  }
1109 
1110  if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
1111  {
1112  double distance;
1113  distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine;
1114  renderOffsetVertexAlongLine( points, i, distance, context );
1115  // restore original rotation
1116  mMarker->setAngle( origAngle );
1117  return;
1118  }
1119 
1120  for ( ; i < maxCount; ++i )
1121  {
1122  if ( isRing && placement == Vertex && i == points.count() - 1 )
1123  {
1124  continue; // don't draw the last marker - it has been drawn already
1125  }
1126  // rotate marker (if desired)
1127  if ( mRotateMarker )
1128  {
1129  double angle = markerAngle( points, isRing, i );
1130  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1131  }
1132 
1133  mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() );
1134  }
1135 
1136  // restore original rotation
1137  mMarker->setAngle( origAngle );
1138 }
1139 
1140 double QgsMarkerLineSymbolLayerV2::markerAngle( const QPolygonF& points, bool isRing, int vertex )
1141 {
1142  double angle = 0;
1143  const QPointF& pt = points[vertex];
1144 
1145  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1146  {
1147  int prevIndex = vertex - 1;
1148  int nextIndex = vertex + 1;
1149 
1150  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1151  {
1152  prevIndex = points.count() - 2;
1153  nextIndex = 1;
1154  }
1155 
1156  QPointF prevPoint, nextPoint;
1157  while ( prevIndex >= 0 )
1158  {
1159  prevPoint = points[ prevIndex ];
1160  if ( prevPoint != pt )
1161  {
1162  break;
1163  }
1164  --prevIndex;
1165  }
1166 
1167  while ( nextIndex < points.count() )
1168  {
1169  nextPoint = points[ nextIndex ];
1170  if ( nextPoint != pt )
1171  {
1172  break;
1173  }
1174  ++nextIndex;
1175  }
1176 
1177  if ( prevIndex >= 0 && nextIndex < points.count() )
1178  {
1179  angle = _averageAngle( prevPoint, pt, nextPoint );
1180  }
1181  }
1182  else //no ring and vertex is at start / at end
1183  {
1184  if ( vertex == 0 )
1185  {
1186  while ( vertex < points.size() - 1 )
1187  {
1188  const QPointF& nextPt = points[vertex+1];
1189  if ( pt != nextPt )
1190  {
1191  angle = MyLine( pt, nextPt ).angle();
1192  return angle;
1193  }
1194  ++vertex;
1195  }
1196  }
1197  else
1198  {
1199  // use last segment's angle
1200  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1201  {
1202  const QPointF& prevPt = points[vertex-1];
1203  if ( pt != prevPt )
1204  {
1205  angle = MyLine( prevPt, pt ).angle();
1206  return angle;
1207  }
1208  --vertex;
1209  }
1210  }
1211  }
1212  return angle;
1213 }
1214 
1215 void QgsMarkerLineSymbolLayerV2::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolV2RenderContext& context )
1216 {
1217  if ( points.isEmpty() )
1218  return;
1219 
1220  QgsRenderContext& rc = context.renderContext();
1221  double origAngle = mMarker->angle();
1222  if ( qgsDoubleNear( distance, 0.0 ) )
1223  {
1224  // rotate marker (if desired)
1225  if ( mRotateMarker )
1226  {
1227  bool isRing = false;
1228  if ( points.first() == points.last() )
1229  isRing = true;
1230  double angle = markerAngle( points, isRing, vertex );
1231  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1232  }
1233  mMarker->renderPoint( points[vertex], context.feature(), rc, -1, context.selected() );
1234  return;
1235  }
1236 
1237  int pointIncrement = distance > 0 ? 1 : -1;
1238  QPointF previousPoint = points[vertex];
1239  int startPoint = distance > 0 ? qMin( vertex + 1, points.count() - 1 ) : qMax( vertex - 1, 0 );
1240  int endPoint = distance > 0 ? points.count() - 1 : 0;
1241  double distanceLeft = qAbs( distance );
1242 
1243  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1244  {
1245  const QPointF& pt = points[i];
1246 
1247  if ( previousPoint == pt ) // must not be equal!
1248  continue;
1249 
1250  // create line segment
1251  MyLine l( previousPoint, pt );
1252 
1253  if ( distanceLeft < l.length() )
1254  {
1255  //destination point is in current segment
1256  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1257  // rotate marker (if desired)
1258  if ( mRotateMarker )
1259  {
1260  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1261  }
1262  mMarker->renderPoint( markerPoint, context.feature(), rc, -1, context.selected() );
1263  return;
1264  }
1265 
1266  distanceLeft -= l.length();
1267  previousPoint = pt;
1268  }
1269 
1270  //didn't find point
1271  return;
1272 }
1273 
1275 {
1276  if ( !points.isEmpty() )
1277  {
1278  // calc length
1279  qreal length = 0;
1280  QPolygonF::const_iterator it = points.constBegin();
1281  QPointF last = *it;
1282  for ( ++it; it != points.constEnd(); ++it )
1283  {
1284  length += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1285  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1286  last = *it;
1287  }
1288 
1289  // find the segment where the central point lies
1290  it = points.constBegin();
1291  last = *it;
1292  qreal last_at = 0, next_at = 0;
1293  QPointF next;
1294  int segment = 0;
1295  for ( ++it; it != points.constEnd(); ++it )
1296  {
1297  next = *it;
1298  next_at += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1299  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1300  if ( next_at >= length / 2 )
1301  break; // we have reached the center
1302  last = *it;
1303  last_at = next_at;
1304  segment++;
1305  }
1306 
1307  // find out the central point on segment
1308  MyLine l( last, next ); // for line angle
1309  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1310  QPointF pt = last + ( next - last ) * k;
1311 
1312  // draw the marker
1313  double origAngle = mMarker->angle();
1314  if ( mRotateMarker )
1315  mMarker->setAngle( origAngle + l.angle() * 180 / M_PI );
1316  mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() );
1317  if ( mRotateMarker )
1318  mMarker->setAngle( origAngle );
1319  }
1320 }
1321 
1322 
1324 {
1325  QgsStringMap map;
1326  map["rotate"] = ( mRotateMarker ? "1" : "0" );
1327  map["interval"] = QString::number( mInterval );
1328  map["offset"] = QString::number( mOffset );
1329  map["offset_along_line"] = QString::number( mOffsetAlongLine );
1330  map["offset_along_line_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetAlongLineUnit );
1331  map["offset_along_line_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetAlongLineMapUnitScale );
1332  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1333  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1334  map["interval_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mIntervalUnit );
1335  map["interval_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mIntervalMapUnitScale );
1336  if ( mPlacement == Vertex )
1337  map["placement"] = "vertex";
1338  else if ( mPlacement == LastVertex )
1339  map["placement"] = "lastvertex";
1340  else if ( mPlacement == FirstVertex )
1341  map["placement"] = "firstvertex";
1342  else if ( mPlacement == CentralPoint )
1343  map["placement"] = "centralpoint";
1344  else if ( mPlacement == CurvePoint )
1345  map["placement"] = "curvepoint";
1346  else
1347  map["placement"] = "interval";
1348 
1350  return map;
1351 }
1352 
1354 {
1355  return mMarker;
1356 }
1357 
1359 {
1360  if ( !symbol || symbol->type() != QgsSymbolV2::Marker )
1361  {
1362  delete symbol;
1363  return false;
1364  }
1365 
1366  delete mMarker;
1367  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
1368  mColor = mMarker->color();
1369  return true;
1370 }
1371 
1373 {
1374  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( mRotateMarker, mInterval );
1375  x->setSubSymbol( mMarker->clone() );
1376  x->setOffset( mOffset );
1377  x->setPlacement( mPlacement );
1378  x->setOffsetUnit( mOffsetUnit );
1380  x->setIntervalUnit( mIntervalUnit );
1381  x->setIntervalMapUnitScale( mIntervalMapUnitScale );
1382  x->setOffsetAlongLine( mOffsetAlongLine );
1383  x->setOffsetAlongLineMapUnitScale( mOffsetAlongLineMapUnitScale );
1384  x->setOffsetAlongLineUnit( mOffsetAlongLineUnit );
1386  copyPaintEffect( x );
1387  return x;
1388 }
1389 
1391 {
1392  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1393  {
1394  QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
1395  if ( !props.value( "uom", "" ).isEmpty() )
1396  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
1397  element.appendChild( symbolizerElem );
1398 
1399  // <Geometry>
1400  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
1401 
1402  QString gap;
1403  switch ( mPlacement )
1404  {
1405  case FirstVertex:
1406  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "firstPoint" ) );
1407  break;
1408  case LastVertex:
1409  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) );
1410  break;
1411  case CentralPoint:
1412  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "centralPoint" ) );
1413  break;
1414  case Vertex:
1415  // no way to get line/polygon's vertices, use a VendorOption
1416  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "points" ) );
1417  break;
1418  default:
1419  double interval = QgsSymbolLayerV2Utils::rescaleUom( mInterval, mIntervalUnit, props );
1420  gap = qgsDoubleToString( interval );
1421  break;
1422  }
1423 
1424  if ( !mRotateMarker )
1425  {
1426  // markers in LineSymbolizer must be drawn following the line orientation,
1427  // use a VendorOption when no marker rotation
1428  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "rotateMarker", "0" ) );
1429  }
1430 
1431  // <Stroke>
1432  QDomElement strokeElem = doc.createElement( "se:Stroke" );
1433  symbolizerElem.appendChild( strokeElem );
1434 
1435  // <GraphicStroke>
1436  QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" );
1437  strokeElem.appendChild( graphicStrokeElem );
1438 
1439  QgsSymbolLayerV2 *layer = mMarker->symbolLayer( i );
1440  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
1441  if ( !markerLayer )
1442  {
1443  graphicStrokeElem.appendChild( doc.createComment( QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() ) ) );
1444  }
1445  else
1446  {
1447  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1448  }
1449 
1450  if ( !gap.isEmpty() )
1451  {
1452  QDomElement gapElem = doc.createElement( "se:Gap" );
1454  graphicStrokeElem.appendChild( gapElem );
1455  }
1456 
1457  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1458  {
1459  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
1461  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
1462  symbolizerElem.appendChild( perpOffsetElem );
1463  }
1464  }
1465 }
1466 
1468 {
1469  QgsDebugMsg( "Entered." );
1470 
1471  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1472  if ( strokeElem.isNull() )
1473  return nullptr;
1474 
1475  QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
1476  if ( graphicStrokeElem.isNull() )
1477  return nullptr;
1478 
1479  // retrieve vendor options
1480  bool rotateMarker = true;
1481  Placement placement = Interval;
1482 
1483  QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( element );
1484  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1485  {
1486  if ( it.key() == "placement" )
1487  {
1488  if ( it.value() == "points" ) placement = Vertex;
1489  else if ( it.value() == "firstPoint" ) placement = FirstVertex;
1490  else if ( it.value() == "lastPoint" ) placement = LastVertex;
1491  else if ( it.value() == "centralPoint" ) placement = CentralPoint;
1492  }
1493  else if ( it.value() == "rotateMarker" )
1494  {
1495  rotateMarker = it.value() == "0";
1496  }
1497  }
1498 
1499  QgsMarkerSymbolV2 *marker = nullptr;
1500 
1502  if ( l )
1503  {
1504  QgsSymbolLayerV2List layers;
1505  layers.append( l );
1506  marker = new QgsMarkerSymbolV2( layers );
1507  }
1508 
1509  if ( !marker )
1510  return nullptr;
1511 
1512  double interval = 0.0;
1513  QDomElement gapElem = graphicStrokeElem.firstChildElement( "Gap" );
1514  if ( !gapElem.isNull() )
1515  {
1516  bool ok;
1517  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1518  if ( ok )
1519  interval = d;
1520  }
1521 
1522  double offset = 0.0;
1523  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( "PerpendicularOffset" );
1524  if ( !perpOffsetElem.isNull() )
1525  {
1526  bool ok;
1527  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1528  if ( ok )
1529  offset = d;
1530  }
1531 
1532  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotateMarker );
1533  x->setPlacement( placement );
1534  x->setInterval( interval );
1535  x->setSubSymbol( marker );
1536  x->setOffset( offset );
1537  return x;
1538 }
1539 
1541 {
1542  mMarker->setSize( width );
1543 }
1544 
1546 {
1547  if ( property == QgsSymbolLayerV2::EXPR_WIDTH && mMarker && dataDefined )
1548  {
1549  mMarker->setDataDefinedSize( *dataDefined );
1550  }
1551  QgsLineSymbolLayerV2::setDataDefinedProperty( property, dataDefined );
1552 }
1553 
1555 {
1556  return mMarker->size();
1557 }
1558 
1560 {
1562  mMarker->setOutputUnit( unit );
1563  mIntervalUnit = unit;
1564  mOffsetUnit = unit;
1565  mOffsetAlongLineUnit = unit;
1566 }
1567 
1569 {
1571  if ( mIntervalUnit != unit || mOffsetUnit != unit || mOffsetAlongLineUnit != unit )
1572  {
1573  return QgsSymbolV2::Mixed;
1574  }
1575  return unit;
1576 }
1577 
1579 {
1581  mIntervalMapUnitScale = scale;
1582  mOffsetMapUnitScale = scale;
1583  mOffsetAlongLineMapUnitScale = scale;
1584 }
1585 
1587 {
1588  if ( QgsLineSymbolLayerV2::mapUnitScale() == mIntervalMapUnitScale &&
1589  mIntervalMapUnitScale == mOffsetMapUnitScale &&
1590  mOffsetMapUnitScale == mOffsetAlongLineMapUnitScale )
1591  {
1592  return mOffsetMapUnitScale;
1593  }
1594  return QgsMapUnitScale();
1595 }
1596 
1598 {
1600  if ( mMarker )
1601  attr.unite( mMarker->usedAttributes() );
1602  return attr;
1603 }
1604 
1606 {
1607  return ( mMarker->size() / 2.0 ) + mOffset;
1608 }
1609 
1610 
1611 
static double mapUnitScaleFactor(double scaleDenominator, QgsSymbolV2::OutputUnit symbolUnits, QGis::UnitType mapUnits)
void setIntervalUnit(QgsSymbolV2::OutputUnit unit)
static QString encodeOutputUnit(QgsSymbolV2::OutputUnit unit)
Qt::PenCapStyle penCapStyle() const
#define DEFAULT_SIMPLELINE_PENSTYLE
#define DEFAULT_MARKERLINE_ROTATE
const QgsCurveV2 * exteriorRing() const
static const QString EXPR_JOINSTYLE
double estimateMaxBleed() const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
const QgsFields * fields() const
Fields of the layer.
Definition: qgssymbolv2.h:391
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
QVector< qreal > dxfCustomDashPattern(QgsSymbolV2::OutputUnit &unit) const override
get dash pattern
const QgsCurveV2 * interiorRing(int i) const
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.
const QgsVectorSimplifyMethod & vectorSimplifyMethod() const
Added in QGIS v2.4.
bool contains(const Key &key) 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)
Qt::PenJoinStyle penJoinStyle() const
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...
static double rescaleUom(double size, QgsSymbolV2::OutputUnit unit, const QgsStringMap &props)
Rescales the given size based on the uomScale found in the props, if any is found, otherwise returns the value un-modified.
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
RenderHints renderHints() const
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
QString nodeValue() const
virtual QSet< QString > usedAttributes() const
Returns the set of attributes referenced by the layer.
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
Qt::PenStyle penStyle() const
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)
const_iterator constEnd() const
void startRender(QgsSymbolV2RenderContext &context) override
const T & at(int i) const
T & last()
virtual bool hasCurvedSegments() const
Returns true if the geometry contains curved segments.
static QVector< qreal > decodeRealVector(const QString &s)
void renderPolylineInterval(const QPolygonF &points, QgsSymbolV2RenderContext &context)
QgsSymbolV2::OutputUnit outputUnit() const override
bool setSubSymbol(QgsSymbolV2 *symbol) override
set layer&#39;s subsymbol. takes ownership of the passed symbol
void setPenJoinStyle(Qt::PenJoinStyle style)
virtual bool hasDataDefinedProperties() const
Checks whether the layer has any associated data defined properties.
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)
SymbolType type() const
Definition: qgssymbolv2.h:107
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
const QgsGeometry * constGeometry() const
Gets a const pointer to the geometry object associated with this feature.
Definition: qgsfeature.cpp:82
static const bool selectionIsOpaque
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbolv2.h:385
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
QGis::UnitType mapUnits() const
Retrieve map units.
Definition: qgsdxfexport.h:97
T & first()
static const QString EXPR_OFFSET_ALONG_LINE
QgsMapUnitScale mWidthMapUnitScale
void setIntervalMapUnitScale(const QgsMapUnitScale &scale)
double y() const
Returns the point&#39;s y-coordinate.
Definition: qgspointv2.h:74
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
QgsSymbolV2::OutputUnit mOffsetUnit
void setWidthUnit(QgsSymbolV2::OutputUnit unit)
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.
Marker symbol.
Definition: qgssymbolv2.h:81
int size() const
void setOffsetAlongLine(double offsetAlongLine)
Sets the the offset along the line for the marker placement.
double symbologyScaleDenominator() const
Retrieve reference scale for output.
Definition: qgsdxfexport.h:84
void setInterval(double interval)
The interval between individual markers.
QgsMapUnitScale mapUnitScale() const override
static QgsSymbolV2::OutputUnit decodeOutputUnit(const QString &str)
void setDrawInsidePolygon(bool drawInsidePolygon)
void transformInPlace(double &x, double &y) const
Transform device coordinates to map coordinates.
static QString encodePenStyle(Qt::PenStyle style)
void setCapStyle(Qt::PenCapStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:34
virtual double width() const
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
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
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())
static bool createExpressionElement(QDomDocument &doc, QDomElement &element, const QString &function)
Creates a OGC Expression element based on the provided function expression.
void setPen(const QColor &color)
virtual QColor color() const override
The fill color.
QgsSymbolV2::OutputUnit mWidthUnit
qreal alpha() const
Get alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:373
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.
const QgsCoordinateTransform * coordinateTransform() const
#define DEFAULT_SIMPLELINE_CAPSTYLE
QColor selectionColor() const
#define DEFAULT_SIMPLELINE_JOINSTYLE
void setWidthF(qreal width)
float threshold() const
Gets the simplification threshold of the vector layer managed.
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 x() const
Returns the point&#39;s x-coordinate.
Definition: qgspointv2.h:68
QVector< qreal > customDashVector() const
void stopRender(QgsSymbolV2RenderContext &context) override
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
Writes the SLD element following the SLD v1.1 specs.
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.
QGis::GeometryType type() const
Returns type of the geometry as a QGis::GeometryType.
virtual QColor color() const
The fill color.
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)
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)
void copyDataDefinedProperties(QgsSymbolLayerV2 *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
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 saveDataDefinedProperties(QgsStringMap &stringMap) const
Saves all data defined properties to a string map.
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 QgsAbstractGeometryV2 * geometry() const
Returns pointer to the unsegmentized geometry.
bool isNull() const
void restore()
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
void copyPaintEffect(QgsSymbolLayerV2 *destLayer) const
Copies paint effect of this layer to another symbol layer.
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()
const QgsMapToPixel & mapToPixel() const
void setMapUnitScale(const QgsMapUnitScale &scale) override
void drawPath(const QPainterPath &path)
QSet< T > & unite(const QSet< T > &other)
void setPlacement(Placement p)
The placement of the markers.
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Writes the symbol layer definition as a SLD XML element.
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
virtual bool hasDataDefinedProperty(const QString &property) const
Checks whether the layer has a matching data defined property and if that property is currently activ...
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)
void stopRender(QgsSymbolV2RenderContext &context) override
QgsRenderContext & renderContext()
Definition: qgssymbolv2.h:356
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.
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.
void push_back(const T &value)
void setAlphaF(qreal alpha)
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)
double rasterScaleFactor() const
const_iterator constEnd() const
QDomElement createElement(const QString &tagName)
const_iterator constBegin() const
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the SLD element following the SLD v1.1 specs.
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 transformInPlace(double &x, double &y, double &z, TransformDirection direction=ForwardTransform) const
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
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
QSet< QString > usedAttributes() const override
Returns the set of attributes referenced by the layer.
void renderPolylineVertex(const QPolygonF &points, QgsSymbolV2RenderContext &context, Placement placement=Vertex)
QgsSymbolV2::OutputUnit widthUnit() 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.