QGIS API Documentation  2.17.0-Master (00653d2)
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 
893  context.renderContext().painter()->save();
894 
895  if ( qgsDoubleNear( offset, 0.0 ) )
896  {
897  if ( placement == Interval )
898  renderPolylineInterval( points, context );
899  else if ( placement == CentralPoint )
900  renderPolylineCentral( points, context );
901  else
902  renderPolylineVertex( points, context, placement );
903  }
904  else
905  {
906  context.renderContext().setGeometry( nullptr ); //always use segmented geometry with offset
908 
909  for ( int part = 0; part < mline.count(); ++part )
910  {
911  const QPolygonF &points2 = mline[ part ];
912 
913  if ( placement == Interval )
914  renderPolylineInterval( points2, context );
915  else if ( placement == CentralPoint )
916  renderPolylineCentral( points2, context );
917  else
918  renderPolylineVertex( points2, context, placement );
919  }
920  }
921 
922  context.renderContext().painter()->restore();
923 }
924 
926 {
927  const QgsCurvePolygonV2* curvePolygon = dynamic_cast<const QgsCurvePolygonV2*>( context.renderContext().geometry() );
928 
929  if ( curvePolygon )
930  {
931  context.renderContext().setGeometry( curvePolygon->exteriorRing() );
932  }
933  renderPolyline( points, context );
934  if ( rings )
935  {
936  mOffset = -mOffset; // invert the offset for rings!
937  for ( int i = 0; i < rings->size(); ++i )
938  {
939  if ( curvePolygon )
940  {
941  context.renderContext().setGeometry( curvePolygon->interiorRing( i ) );
942  }
943  renderPolyline( rings->at( i ), context );
944  }
945  mOffset = -mOffset;
946  }
947 }
948 
950 {
951  if ( points.isEmpty() )
952  return;
953 
954  QPointF lastPt = points[0];
955  double lengthLeft = 0; // how much is left until next marker
956 
957  QgsRenderContext& rc = context.renderContext();
958  double interval = mInterval;
959 
961  context.renderContext().expressionContext().appendScope( scope );
962 
964  {
965  context.setOriginalValueVariable( mInterval );
966  interval = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_INTERVAL, context, mInterval ).toDouble();
967  }
968  if ( interval <= 0 )
969  {
970  interval = 0.1;
971  }
972  double offsetAlongLine = mOffsetAlongLine;
974  {
975  context.setOriginalValueVariable( mOffsetAlongLine );
976  offsetAlongLine = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET_ALONG_LINE, context, mOffsetAlongLine ).toDouble();
977  }
978 
979  double painterUnitInterval = QgsSymbolLayerV2Utils::convertToPainterUnits( rc, interval, mIntervalUnit, mIntervalMapUnitScale );
980  lengthLeft = painterUnitInterval - QgsSymbolLayerV2Utils::convertToPainterUnits( rc, offsetAlongLine, mIntervalUnit, mIntervalMapUnitScale );
981 
982  int pointNum = 0;
983  for ( int i = 1; i < points.count(); ++i )
984  {
985  const QPointF& pt = points[i];
986 
987  if ( lastPt == pt ) // must not be equal!
988  continue;
989 
990  // for each line, find out dx and dy, and length
991  MyLine l( lastPt, pt );
992  QPointF diff = l.diffForInterval( painterUnitInterval );
993 
994  // if there's some length left from previous line
995  // use only the rest for the first point in new line segment
996  double c = 1 - lengthLeft / painterUnitInterval;
997 
998  lengthLeft += l.length();
999 
1000  // rotate marker (if desired)
1001  if ( mRotateMarker )
1002  {
1003  mMarker->setLineAngle( l.angle() * 180 / M_PI );
1004  }
1005 
1006 
1007  // while we're not at the end of line segment, draw!
1008  while ( lengthLeft > painterUnitInterval )
1009  {
1010  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
1011  lastPt += c * diff;
1012  lengthLeft -= painterUnitInterval;
1014  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
1015  c = 1; // reset c (if wasn't 1 already)
1016  }
1017 
1018  lastPt = pt;
1019  }
1020 
1021  delete context.renderContext().expressionContext().popScope();
1022 }
1023 
1024 static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt )
1025 {
1026  // calc average angle between the previous and next point
1027  double a1 = MyLine( prevPt, pt ).angle();
1028  double a2 = MyLine( pt, nextPt ).angle();
1029  double unitX = cos( a1 ) + cos( a2 ), unitY = sin( a1 ) + sin( a2 );
1030 
1031  return atan2( unitY, unitX );
1032 }
1033 
1035 {
1036  if ( points.isEmpty() )
1037  return;
1038 
1039  QgsRenderContext& rc = context.renderContext();
1040 
1041  double origAngle = mMarker->angle();
1042  int i, maxCount;
1043  bool isRing = false;
1044 
1046  context.renderContext().expressionContext().appendScope( scope );
1048 
1049  double offsetAlongLine = mOffsetAlongLine;
1051  {
1052  context.setOriginalValueVariable( mOffsetAlongLine );
1053  offsetAlongLine = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET_ALONG_LINE, context, mOffsetAlongLine ).toDouble();
1054  }
1055  if ( !qgsDoubleNear( offsetAlongLine, 0.0 ) )
1056  {
1057  //scale offset along line
1058  offsetAlongLine = QgsSymbolLayerV2Utils::convertToPainterUnits( rc, offsetAlongLine, mOffsetAlongLineUnit, mOffsetAlongLineMapUnitScale );
1059  }
1060 
1061  if ( qgsDoubleNear( offsetAlongLine, 0.0 ) && context.renderContext().geometry()
1062  && context.renderContext().geometry()->hasCurvedSegments() && ( placement == Vertex || placement == CurvePoint ) )
1063  {
1065  const QgsMapToPixel& mtp = context.renderContext().mapToPixel();
1066 
1067  QgsVertexId vId;
1068  QgsPointV2 vPoint;
1069  double x, y, z;
1070  QPointF mapPoint;
1071  int pointNum = 0;
1072  while ( context.renderContext().geometry()->nextVertex( vId, vPoint ) )
1073  {
1075 
1076  if (( placement == Vertex && vId.type == QgsVertexId::SegmentVertex )
1077  || ( placement == CurvePoint && vId.type == QgsVertexId::CurveVertex ) )
1078  {
1079  //transform
1080  x = vPoint.x(), y = vPoint.y();
1081  z = 0.0;
1082  if ( ct )
1083  {
1084  ct->transformInPlace( x, y, z );
1085  }
1086  mapPoint.setX( x );
1087  mapPoint.setY( y );
1088  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1089  if ( mRotateMarker )
1090  {
1091  double angle = context.renderContext().geometry()->vertexAngle( vId );
1092  mMarker->setAngle( angle * 180 / M_PI );
1093  }
1094  mMarker->renderPoint( mapPoint, context.feature(), rc, -1, context.selected() );
1095  }
1096  }
1097 
1098  delete context.renderContext().expressionContext().popScope();
1099  return;
1100  }
1101 
1102  if ( placement == FirstVertex )
1103  {
1104  i = 0;
1105  maxCount = 1;
1106  }
1107  else if ( placement == LastVertex )
1108  {
1109  i = points.count() - 1;
1110  maxCount = points.count();
1111  }
1112  else if ( placement == Vertex )
1113  {
1114  i = 0;
1115  maxCount = points.count();
1116  if ( points.first() == points.last() )
1117  isRing = true;
1118  }
1119  else
1120  {
1121  delete context.renderContext().expressionContext().popScope();
1122  return;
1123  }
1124 
1125  if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
1126  {
1127  double distance;
1128  distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine;
1129  renderOffsetVertexAlongLine( points, i, distance, context );
1130  // restore original rotation
1131  mMarker->setAngle( origAngle );
1132 
1133  delete context.renderContext().expressionContext().popScope();
1134  return;
1135  }
1136 
1137  int pointNum = 0;
1138  for ( ; i < maxCount; ++i )
1139  {
1141 
1142  if ( isRing && placement == Vertex && i == points.count() - 1 )
1143  {
1144  continue; // don't draw the last marker - it has been drawn already
1145  }
1146  // rotate marker (if desired)
1147  if ( mRotateMarker )
1148  {
1149  double angle = markerAngle( points, isRing, i );
1150  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1151  }
1152 
1153  mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() );
1154  }
1155 
1156  // restore original rotation
1157  mMarker->setAngle( origAngle );
1158 
1159  delete context.renderContext().expressionContext().popScope();
1160 }
1161 
1162 double QgsMarkerLineSymbolLayerV2::markerAngle( const QPolygonF& points, bool isRing, int vertex )
1163 {
1164  double angle = 0;
1165  const QPointF& pt = points[vertex];
1166 
1167  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1168  {
1169  int prevIndex = vertex - 1;
1170  int nextIndex = vertex + 1;
1171 
1172  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1173  {
1174  prevIndex = points.count() - 2;
1175  nextIndex = 1;
1176  }
1177 
1178  QPointF prevPoint, nextPoint;
1179  while ( prevIndex >= 0 )
1180  {
1181  prevPoint = points[ prevIndex ];
1182  if ( prevPoint != pt )
1183  {
1184  break;
1185  }
1186  --prevIndex;
1187  }
1188 
1189  while ( nextIndex < points.count() )
1190  {
1191  nextPoint = points[ nextIndex ];
1192  if ( nextPoint != pt )
1193  {
1194  break;
1195  }
1196  ++nextIndex;
1197  }
1198 
1199  if ( prevIndex >= 0 && nextIndex < points.count() )
1200  {
1201  angle = _averageAngle( prevPoint, pt, nextPoint );
1202  }
1203  }
1204  else //no ring and vertex is at start / at end
1205  {
1206  if ( vertex == 0 )
1207  {
1208  while ( vertex < points.size() - 1 )
1209  {
1210  const QPointF& nextPt = points[vertex+1];
1211  if ( pt != nextPt )
1212  {
1213  angle = MyLine( pt, nextPt ).angle();
1214  return angle;
1215  }
1216  ++vertex;
1217  }
1218  }
1219  else
1220  {
1221  // use last segment's angle
1222  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1223  {
1224  const QPointF& prevPt = points[vertex-1];
1225  if ( pt != prevPt )
1226  {
1227  angle = MyLine( prevPt, pt ).angle();
1228  return angle;
1229  }
1230  --vertex;
1231  }
1232  }
1233  }
1234  return angle;
1235 }
1236 
1237 void QgsMarkerLineSymbolLayerV2::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolV2RenderContext& context )
1238 {
1239  if ( points.isEmpty() )
1240  return;
1241 
1242  QgsRenderContext& rc = context.renderContext();
1243  double origAngle = mMarker->angle();
1244  if ( qgsDoubleNear( distance, 0.0 ) )
1245  {
1246  // rotate marker (if desired)
1247  if ( mRotateMarker )
1248  {
1249  bool isRing = false;
1250  if ( points.first() == points.last() )
1251  isRing = true;
1252  double angle = markerAngle( points, isRing, vertex );
1253  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1254  }
1255  mMarker->renderPoint( points[vertex], context.feature(), rc, -1, context.selected() );
1256  return;
1257  }
1258 
1259  int pointIncrement = distance > 0 ? 1 : -1;
1260  QPointF previousPoint = points[vertex];
1261  int startPoint = distance > 0 ? qMin( vertex + 1, points.count() - 1 ) : qMax( vertex - 1, 0 );
1262  int endPoint = distance > 0 ? points.count() - 1 : 0;
1263  double distanceLeft = qAbs( distance );
1264 
1265  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1266  {
1267  const QPointF& pt = points[i];
1268 
1269  if ( previousPoint == pt ) // must not be equal!
1270  continue;
1271 
1272  // create line segment
1273  MyLine l( previousPoint, pt );
1274 
1275  if ( distanceLeft < l.length() )
1276  {
1277  //destination point is in current segment
1278  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1279  // rotate marker (if desired)
1280  if ( mRotateMarker )
1281  {
1282  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1283  }
1284  mMarker->renderPoint( markerPoint, context.feature(), rc, -1, context.selected() );
1285  return;
1286  }
1287 
1288  distanceLeft -= l.length();
1289  previousPoint = pt;
1290  }
1291 
1292  //didn't find point
1293  return;
1294 }
1295 
1297 {
1298  if ( !points.isEmpty() )
1299  {
1300  // calc length
1301  qreal length = 0;
1302  QPolygonF::const_iterator it = points.constBegin();
1303  QPointF last = *it;
1304  for ( ++it; it != points.constEnd(); ++it )
1305  {
1306  length += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1307  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1308  last = *it;
1309  }
1310 
1311  // find the segment where the central point lies
1312  it = points.constBegin();
1313  last = *it;
1314  qreal last_at = 0, next_at = 0;
1315  QPointF next;
1316  int segment = 0;
1317  for ( ++it; it != points.constEnd(); ++it )
1318  {
1319  next = *it;
1320  next_at += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1321  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1322  if ( next_at >= length / 2 )
1323  break; // we have reached the center
1324  last = *it;
1325  last_at = next_at;
1326  segment++;
1327  }
1328 
1329  // find out the central point on segment
1330  MyLine l( last, next ); // for line angle
1331  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1332  QPointF pt = last + ( next - last ) * k;
1333 
1334  // draw the marker
1335  double origAngle = mMarker->angle();
1336  if ( mRotateMarker )
1337  mMarker->setAngle( origAngle + l.angle() * 180 / M_PI );
1338  mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() );
1339  if ( mRotateMarker )
1340  mMarker->setAngle( origAngle );
1341  }
1342 }
1343 
1344 
1346 {
1347  QgsStringMap map;
1348  map["rotate"] = ( mRotateMarker ? "1" : "0" );
1349  map["interval"] = QString::number( mInterval );
1350  map["offset"] = QString::number( mOffset );
1351  map["offset_along_line"] = QString::number( mOffsetAlongLine );
1352  map["offset_along_line_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetAlongLineUnit );
1353  map["offset_along_line_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetAlongLineMapUnitScale );
1354  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1355  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1356  map["interval_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mIntervalUnit );
1357  map["interval_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mIntervalMapUnitScale );
1358  if ( mPlacement == Vertex )
1359  map["placement"] = "vertex";
1360  else if ( mPlacement == LastVertex )
1361  map["placement"] = "lastvertex";
1362  else if ( mPlacement == FirstVertex )
1363  map["placement"] = "firstvertex";
1364  else if ( mPlacement == CentralPoint )
1365  map["placement"] = "centralpoint";
1366  else if ( mPlacement == CurvePoint )
1367  map["placement"] = "curvepoint";
1368  else
1369  map["placement"] = "interval";
1370 
1372  return map;
1373 }
1374 
1376 {
1377  return mMarker;
1378 }
1379 
1381 {
1382  if ( !symbol || symbol->type() != QgsSymbolV2::Marker )
1383  {
1384  delete symbol;
1385  return false;
1386  }
1387 
1388  delete mMarker;
1389  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
1390  mColor = mMarker->color();
1391  return true;
1392 }
1393 
1395 {
1396  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( mRotateMarker, mInterval );
1397  x->setSubSymbol( mMarker->clone() );
1398  x->setOffset( mOffset );
1399  x->setPlacement( mPlacement );
1400  x->setOffsetUnit( mOffsetUnit );
1402  x->setIntervalUnit( mIntervalUnit );
1403  x->setIntervalMapUnitScale( mIntervalMapUnitScale );
1404  x->setOffsetAlongLine( mOffsetAlongLine );
1405  x->setOffsetAlongLineMapUnitScale( mOffsetAlongLineMapUnitScale );
1406  x->setOffsetAlongLineUnit( mOffsetAlongLineUnit );
1408  copyPaintEffect( x );
1409  return x;
1410 }
1411 
1413 {
1414  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1415  {
1416  QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
1417  if ( !props.value( "uom", "" ).isEmpty() )
1418  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
1419  element.appendChild( symbolizerElem );
1420 
1421  // <Geometry>
1422  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
1423 
1424  QString gap;
1425  switch ( mPlacement )
1426  {
1427  case FirstVertex:
1428  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "firstPoint" ) );
1429  break;
1430  case LastVertex:
1431  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) );
1432  break;
1433  case CentralPoint:
1434  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "centralPoint" ) );
1435  break;
1436  case Vertex:
1437  // no way to get line/polygon's vertices, use a VendorOption
1438  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "points" ) );
1439  break;
1440  default:
1441  double interval = QgsSymbolLayerV2Utils::rescaleUom( mInterval, mIntervalUnit, props );
1442  gap = qgsDoubleToString( interval );
1443  break;
1444  }
1445 
1446  if ( !mRotateMarker )
1447  {
1448  // markers in LineSymbolizer must be drawn following the line orientation,
1449  // use a VendorOption when no marker rotation
1450  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "rotateMarker", "0" ) );
1451  }
1452 
1453  // <Stroke>
1454  QDomElement strokeElem = doc.createElement( "se:Stroke" );
1455  symbolizerElem.appendChild( strokeElem );
1456 
1457  // <GraphicStroke>
1458  QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" );
1459  strokeElem.appendChild( graphicStrokeElem );
1460 
1461  QgsSymbolLayerV2 *layer = mMarker->symbolLayer( i );
1462  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
1463  if ( !markerLayer )
1464  {
1465  graphicStrokeElem.appendChild( doc.createComment( QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() ) ) );
1466  }
1467  else
1468  {
1469  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1470  }
1471 
1472  if ( !gap.isEmpty() )
1473  {
1474  QDomElement gapElem = doc.createElement( "se:Gap" );
1476  graphicStrokeElem.appendChild( gapElem );
1477  }
1478 
1479  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1480  {
1481  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
1483  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
1484  symbolizerElem.appendChild( perpOffsetElem );
1485  }
1486  }
1487 }
1488 
1490 {
1491  QgsDebugMsg( "Entered." );
1492 
1493  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1494  if ( strokeElem.isNull() )
1495  return nullptr;
1496 
1497  QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
1498  if ( graphicStrokeElem.isNull() )
1499  return nullptr;
1500 
1501  // retrieve vendor options
1502  bool rotateMarker = true;
1503  Placement placement = Interval;
1504 
1505  QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( element );
1506  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1507  {
1508  if ( it.key() == "placement" )
1509  {
1510  if ( it.value() == "points" ) placement = Vertex;
1511  else if ( it.value() == "firstPoint" ) placement = FirstVertex;
1512  else if ( it.value() == "lastPoint" ) placement = LastVertex;
1513  else if ( it.value() == "centralPoint" ) placement = CentralPoint;
1514  }
1515  else if ( it.value() == "rotateMarker" )
1516  {
1517  rotateMarker = it.value() == "0";
1518  }
1519  }
1520 
1521  QgsMarkerSymbolV2 *marker = nullptr;
1522 
1524  if ( l )
1525  {
1526  QgsSymbolLayerV2List layers;
1527  layers.append( l );
1528  marker = new QgsMarkerSymbolV2( layers );
1529  }
1530 
1531  if ( !marker )
1532  return nullptr;
1533 
1534  double interval = 0.0;
1535  QDomElement gapElem = graphicStrokeElem.firstChildElement( "Gap" );
1536  if ( !gapElem.isNull() )
1537  {
1538  bool ok;
1539  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1540  if ( ok )
1541  interval = d;
1542  }
1543 
1544  double offset = 0.0;
1545  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( "PerpendicularOffset" );
1546  if ( !perpOffsetElem.isNull() )
1547  {
1548  bool ok;
1549  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1550  if ( ok )
1551  offset = d;
1552  }
1553 
1554  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotateMarker );
1555  x->setPlacement( placement );
1556  x->setInterval( interval );
1557  x->setSubSymbol( marker );
1558  x->setOffset( offset );
1559  return x;
1560 }
1561 
1563 {
1564  mMarker->setSize( width );
1565 }
1566 
1568 {
1569  if ( property == QgsSymbolLayerV2::EXPR_WIDTH && mMarker && dataDefined )
1570  {
1571  mMarker->setDataDefinedSize( *dataDefined );
1572  }
1573  QgsLineSymbolLayerV2::setDataDefinedProperty( property, dataDefined );
1574 }
1575 
1577 {
1578  return mMarker->size();
1579 }
1580 
1582 {
1584  mMarker->setOutputUnit( unit );
1585  mIntervalUnit = unit;
1586  mOffsetUnit = unit;
1587  mOffsetAlongLineUnit = unit;
1588 }
1589 
1591 {
1593  if ( mIntervalUnit != unit || mOffsetUnit != unit || mOffsetAlongLineUnit != unit )
1594  {
1595  return QgsSymbolV2::Mixed;
1596  }
1597  return unit;
1598 }
1599 
1601 {
1603  mIntervalMapUnitScale = scale;
1604  mOffsetMapUnitScale = scale;
1605  mOffsetAlongLineMapUnitScale = scale;
1606 }
1607 
1609 {
1610  if ( QgsLineSymbolLayerV2::mapUnitScale() == mIntervalMapUnitScale &&
1611  mIntervalMapUnitScale == mOffsetMapUnitScale &&
1612  mOffsetMapUnitScale == mOffsetAlongLineMapUnitScale )
1613  {
1614  return mOffsetMapUnitScale;
1615  }
1616  return QgsMapUnitScale();
1617 }
1618 
1620 {
1622  if ( mMarker )
1623  attr.unite( mMarker->usedAttributes() );
1624  return attr;
1625 }
1626 
1628 {
1629  return ( mMarker->size() / 2.0 ) + mOffset;
1630 }
1631 
1632 
1633 
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.
static const QString EXPR_GEOMETRY_POINT_COUNT
Inbuilt variable name for point count variable.
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
void setVariable(const QString &name, const QVariant &value)
Convenience method for setting a variable in the context scope by name and value. ...
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)
Single scope for storing variables and functions for use within a QgsExpressionContext.
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.
QgsExpressionContext & expressionContext()
Gets the expression context.
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 appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
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)
static const QString EXPR_GEOMETRY_POINT_NUM
Inbuilt variable name for point number variable.
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
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
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.