QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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  QString uom = element.attribute( QString( "uom" ), "" );
475  width = QgsSymbolLayerV2Utils::sizeInPixelsFromSldUom( uom, width );
476  offset = QgsSymbolLayerV2Utils::sizeInPixelsFromSldUom( uom, offset );
477 
478  QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
480  l->setOffset( offset );
481  l->setPenJoinStyle( penJoinStyle );
482  l->setPenCapStyle( penCapStyle );
483  l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine );
484  l->setCustomDashVector( customDashVector );
485  return l;
486 }
487 
488 void QgsSimpleLineSymbolLayerV2::applySizeScale( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen )
489 {
491  pen.setWidthF( scaledWidth );
492  selPen.setWidthF( scaledWidth );
493 }
494 
495 void QgsSimpleLineSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen, double& offset )
496 {
497  if ( !hasDataDefinedProperties() )
498  return; // shortcut
499 
500  //data defined properties
501  bool hasStrokeWidthExpression = false;
503  {
504  context.setOriginalValueVariable( mWidth );
505  double scaledWidth = QgsSymbolLayerV2Utils::convertToPainterUnits( context.renderContext(),
508  pen.setWidthF( scaledWidth );
509  selPen.setWidthF( scaledWidth );
510  hasStrokeWidthExpression = true;
511  }
512 
513  //color
514  bool ok;
516  {
519  if ( ok )
520  pen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorString ) );
521  }
522 
523  //offset
525  {
528  }
529 
530  //dash dot vector
532  {
534  double dashWidthDiv = mPen.widthF();
535 
536  if ( hasStrokeWidthExpression )
537  {
538  dashWidthDiv = pen.widthF();
539  scaledWidth = pen.widthF();
540  }
541 
542  //fix dash pattern width in Qt 4.8
543  QStringList versionSplit = QString( qVersion() ).split( '.' );
544  if ( versionSplit.size() > 1
545  && versionSplit.at( 1 ).toInt() >= 8
546  && ( scaledWidth * context.renderContext().rasterScaleFactor() ) < 1.0 )
547  {
548  dashWidthDiv = 1.0;
549  }
550 
551  QVector<qreal> dashVector;
553  if ( ok )
554  {
555  QStringList::const_iterator dashIt = dashList.constBegin();
556  for ( ; dashIt != dashList.constEnd(); ++dashIt )
557  {
559  }
560  pen.setDashPattern( dashVector );
561  }
562  }
563 
564  //line style
566  {
569  if ( ok )
570  pen.setStyle( QgsSymbolLayerV2Utils::decodePenStyle( lineStyleString ) );
571  }
572 
573  //join style
575  {
578  if ( ok )
580  }
581 
582  //cap style
584  {
587  if ( ok )
588  pen.setCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( capStyleString ) );
589  }
590 }
591 
593 {
594  if ( mDrawInsidePolygon )
595  {
596  //set to clip line to the interior of polygon, so we expect no bleed
597  return 0;
598  }
599  else
600  {
601  return ( mWidth / 2.0 ) + mOffset;
602  }
603 }
604 
606 {
607  unit = mCustomDashPatternUnit;
609 }
610 
612 {
613  return mPenStyle;
614 }
615 
617 {
618  double width = mWidth;
619 
621  {
622  context.setOriginalValueVariable( mWidth );
624  }
625 
628  {
630  }
631  return width;
632 }
633 
635 {
637  {
638  bool ok;
641  if ( ok )
642  return ( QgsSymbolLayerV2Utils::decodeColor( colorString ) );
643  }
644  return mColor;
645 }
646 
648 {
649  Q_UNUSED( e );
650  double offset = mOffset;
651 
653  {
656  }
657 
660  {
662  }
663  return -offset; //direction seems to be inverse to symbology offset
664 }
665 
667 
669 
670 class MyLine
671 {
672  public:
673  MyLine( QPointF p1, QPointF p2 )
674  : mVertical( false )
675  , mIncreasing( false )
676  , mT( 0.0 )
677  , mLength( 0.0 )
678  {
679  if ( p1 == p2 )
680  return; // invalid
681 
682  // tangent and direction
683  if ( qgsDoubleNear( p1.x(), p2.x() ) )
684  {
685  // vertical line - tangent undefined
686  mVertical = true;
687  mIncreasing = ( p2.y() > p1.y() );
688  }
689  else
690  {
691  mVertical = false;
692  mT = float( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
693  mIncreasing = ( p2.x() > p1.x() );
694  }
695 
696  // length
697  double x = ( p2.x() - p1.x() );
698  double y = ( p2.y() - p1.y() );
699  mLength = sqrt( x * x + y * y );
700  }
701 
702  // return angle in radians
703  double angle()
704  {
705  double a = ( mVertical ? M_PI / 2 : atan( mT ) );
706 
707  if ( !mIncreasing )
708  a += M_PI;
709  return a;
710  }
711 
712  // return difference for x,y when going along the line with specified interval
713  QPointF diffForInterval( double interval )
714  {
715  if ( mVertical )
716  return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
717 
718  double alpha = atan( mT );
719  double dx = cos( alpha ) * interval;
720  double dy = sin( alpha ) * interval;
721  return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
722  }
723 
724  double length() { return mLength; }
725 
726  protected:
727  bool mVertical;
728  bool mIncreasing;
729  double mT;
730  double mLength;
731 };
732 
734 
735 QgsMarkerLineSymbolLayerV2::QgsMarkerLineSymbolLayerV2( bool rotateMarker, double interval )
736 {
737  mRotateMarker = rotateMarker;
738  mInterval = interval;
739  mIntervalUnit = QgsSymbolV2::MM;
740  mMarker = nullptr;
741  mPlacement = Interval;
742  mOffsetAlongLine = 0;
743  mOffsetAlongLineUnit = QgsSymbolV2::MM;
744 
746 }
747 
749 {
750  delete mMarker;
751 }
752 
754 {
755  bool rotate = DEFAULT_MARKERLINE_ROTATE;
756  double interval = DEFAULT_MARKERLINE_INTERVAL;
757 
758 
759  if ( props.contains( "interval" ) )
760  interval = props["interval"].toDouble();
761  if ( props.contains( "rotate" ) )
762  rotate = ( props["rotate"] == "1" );
763 
764  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotate, interval );
765  if ( props.contains( "offset" ) )
766  {
767  x->setOffset( props["offset"].toDouble() );
768  }
769  if ( props.contains( "offset_unit" ) )
770  {
771  x->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
772  }
773  if ( props.contains( "interval_unit" ) )
774  {
775  x->setIntervalUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["interval_unit"] ) );
776  }
777  if ( props.contains( "offset_along_line" ) )
778  {
779  x->setOffsetAlongLine( props["offset_along_line"].toDouble() );
780  }
781  if ( props.contains( "offset_along_line_unit" ) )
782  {
783  x->setOffsetAlongLineUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_along_line_unit"] ) );
784  }
785  if ( props.contains(( "offset_along_line_map_unit_scale" ) ) )
786  {
787  x->setOffsetAlongLineMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_along_line_map_unit_scale"] ) );
788  }
789 
790  if ( props.contains( "offset_map_unit_scale" ) )
791  {
792  x->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
793  }
794  if ( props.contains( "interval_map_unit_scale" ) )
795  {
796  x->setIntervalMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["interval_map_unit_scale"] ) );
797  }
798 
799  if ( props.contains( "placement" ) )
800  {
801  if ( props["placement"] == "vertex" )
802  x->setPlacement( Vertex );
803  else if ( props["placement"] == "lastvertex" )
804  x->setPlacement( LastVertex );
805  else if ( props["placement"] == "firstvertex" )
806  x->setPlacement( FirstVertex );
807  else if ( props["placement"] == "centralpoint" )
808  x->setPlacement( CentralPoint );
809  else if ( props["placement"] == "curvepoint" )
810  x->setPlacement( CurvePoint );
811  else
812  x->setPlacement( Interval );
813  }
814 
815  x->restoreDataDefinedProperties( props );
816 
817  return x;
818 }
819 
821 {
822  return "MarkerLine";
823 }
824 
826 {
827  mMarker->setColor( color );
828  mColor = color;
829 }
830 
832 {
833  return mMarker ? mMarker->color() : mColor;
834 }
835 
837 {
838  mMarker->setAlpha( context.alpha() );
839 
840  // if being rotated, it gets initialized with every line segment
841  int hints = 0;
842  if ( mRotateMarker )
846  mMarker->setRenderHints( hints );
847 
848  mMarker->startRender( context.renderContext(), context.fields() );
849 
850  //prepare expressions for data defined properties
851  prepareExpressions( context );
852 }
853 
855 {
856  mMarker->stopRender( context.renderContext() );
857 }
858 
860 {
861  double offset = mOffset;
862 
864  {
867  }
868 
869  Placement placement = mPlacement;
870 
871  bool ok;
873  {
875  if ( ok )
876  {
877  if ( placementString.compare( "vertex", Qt::CaseInsensitive ) == 0 )
878  {
879  placement = Vertex;
880  }
881  else if ( placementString.compare( "lastvertex", Qt::CaseInsensitive ) == 0 )
882  {
883  placement = LastVertex;
884  }
885  else if ( placementString.compare( "firstvertex", Qt::CaseInsensitive ) == 0 )
886  {
887  placement = FirstVertex;
888  }
889  else if ( placementString.compare( "centerpoint", Qt::CaseInsensitive ) == 0 )
890  {
891  placement = CentralPoint;
892  }
893  else if ( placementString.compare( "curvepoint", Qt::CaseInsensitive ) == 0 )
894  {
895  placement = CurvePoint;
896  }
897  else
898  {
899  placement = Interval;
900  }
901  }
902  }
903 
904 
905  context.renderContext().painter()->save();
906 
907  if ( qgsDoubleNear( offset, 0.0 ) )
908  {
909  if ( placement == Interval )
910  renderPolylineInterval( points, context );
911  else if ( placement == CentralPoint )
912  renderPolylineCentral( points, context );
913  else
914  renderPolylineVertex( points, context, placement );
915  }
916  else
917  {
918  context.renderContext().setGeometry( nullptr ); //always use segmented geometry with offset
920 
921  for ( int part = 0; part < mline.count(); ++part )
922  {
923  const QPolygonF &points2 = mline[ part ];
924 
925  if ( placement == Interval )
926  renderPolylineInterval( points2, context );
927  else if ( placement == CentralPoint )
928  renderPolylineCentral( points2, context );
929  else
930  renderPolylineVertex( points2, context, placement );
931  }
932  }
933 
934  context.renderContext().painter()->restore();
935 }
936 
938 {
939  const QgsCurvePolygonV2* curvePolygon = dynamic_cast<const QgsCurvePolygonV2*>( context.renderContext().geometry() );
940 
941  if ( curvePolygon )
942  {
943  context.renderContext().setGeometry( curvePolygon->exteriorRing() );
944  }
945  renderPolyline( points, context );
946  if ( rings )
947  {
948  mOffset = -mOffset; // invert the offset for rings!
949  for ( int i = 0; i < rings->size(); ++i )
950  {
951  if ( curvePolygon )
952  {
953  context.renderContext().setGeometry( curvePolygon->interiorRing( i ) );
954  }
955  renderPolyline( rings->at( i ), context );
956  }
957  mOffset = -mOffset;
958  }
959 }
960 
962 {
963  if ( points.isEmpty() )
964  return;
965 
966  QPointF lastPt = points[0];
967  double lengthLeft = 0; // how much is left until next marker
968 
969  QgsRenderContext& rc = context.renderContext();
970  double interval = mInterval;
971 
973  context.renderContext().expressionContext().appendScope( scope );
974 
976  {
977  context.setOriginalValueVariable( mInterval );
978  interval = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_INTERVAL, context, mInterval ).toDouble();
979  }
980  if ( interval <= 0 )
981  {
982  interval = 0.1;
983  }
984  double offsetAlongLine = mOffsetAlongLine;
986  {
987  context.setOriginalValueVariable( mOffsetAlongLine );
988  offsetAlongLine = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET_ALONG_LINE, context, mOffsetAlongLine ).toDouble();
989  }
990 
991  double painterUnitInterval = QgsSymbolLayerV2Utils::convertToPainterUnits( rc, interval, mIntervalUnit, mIntervalMapUnitScale );
992  lengthLeft = painterUnitInterval - QgsSymbolLayerV2Utils::convertToPainterUnits( rc, offsetAlongLine, mIntervalUnit, mIntervalMapUnitScale );
993 
994  int pointNum = 0;
995  for ( int i = 1; i < points.count(); ++i )
996  {
997  const QPointF& pt = points[i];
998 
999  if ( lastPt == pt ) // must not be equal!
1000  continue;
1001 
1002  // for each line, find out dx and dy, and length
1003  MyLine l( lastPt, pt );
1004  QPointF diff = l.diffForInterval( painterUnitInterval );
1005 
1006  // if there's some length left from previous line
1007  // use only the rest for the first point in new line segment
1008  double c = 1 - lengthLeft / painterUnitInterval;
1009 
1010  lengthLeft += l.length();
1011 
1012  // rotate marker (if desired)
1013  if ( mRotateMarker )
1014  {
1015  mMarker->setLineAngle( l.angle() * 180 / M_PI );
1016  }
1017 
1018 
1019  // while we're not at the end of line segment, draw!
1020  while ( lengthLeft > painterUnitInterval )
1021  {
1022  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
1023  lastPt += c * diff;
1024  lengthLeft -= painterUnitInterval;
1026  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
1027  c = 1; // reset c (if wasn't 1 already)
1028  }
1029 
1030  lastPt = pt;
1031  }
1032 
1033  delete context.renderContext().expressionContext().popScope();
1034 }
1035 
1036 static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt )
1037 {
1038  // calc average angle between the previous and next point
1039  double a1 = MyLine( prevPt, pt ).angle();
1040  double a2 = MyLine( pt, nextPt ).angle();
1041  double unitX = cos( a1 ) + cos( a2 ), unitY = sin( a1 ) + sin( a2 );
1042 
1043  return atan2( unitY, unitX );
1044 }
1045 
1047 {
1048  if ( points.isEmpty() )
1049  return;
1050 
1051  QgsRenderContext& rc = context.renderContext();
1052 
1053  double origAngle = mMarker->angle();
1054  int i, maxCount;
1055  bool isRing = false;
1056 
1058  context.renderContext().expressionContext().appendScope( scope );
1060 
1061  double offsetAlongLine = mOffsetAlongLine;
1063  {
1064  context.setOriginalValueVariable( mOffsetAlongLine );
1065  offsetAlongLine = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET_ALONG_LINE, context, mOffsetAlongLine ).toDouble();
1066  }
1067  if ( !qgsDoubleNear( offsetAlongLine, 0.0 ) )
1068  {
1069  //scale offset along line
1070  offsetAlongLine = QgsSymbolLayerV2Utils::convertToPainterUnits( rc, offsetAlongLine, mOffsetAlongLineUnit, mOffsetAlongLineMapUnitScale );
1071  }
1072 
1073  if ( qgsDoubleNear( offsetAlongLine, 0.0 ) && context.renderContext().geometry()
1074  && context.renderContext().geometry()->hasCurvedSegments() && ( placement == Vertex || placement == CurvePoint ) )
1075  {
1077  const QgsMapToPixel& mtp = context.renderContext().mapToPixel();
1078 
1079  QgsVertexId vId;
1080  QgsPointV2 vPoint;
1081  double x, y, z;
1082  QPointF mapPoint;
1083  int pointNum = 0;
1084  while ( context.renderContext().geometry()->nextVertex( vId, vPoint ) )
1085  {
1087 
1088  if (( placement == Vertex && vId.type == QgsVertexId::SegmentVertex )
1089  || ( placement == CurvePoint && vId.type == QgsVertexId::CurveVertex ) )
1090  {
1091  //transform
1092  x = vPoint.x(), y = vPoint.y();
1093  z = 0.0;
1094  if ( ct )
1095  {
1096  ct->transformInPlace( x, y, z );
1097  }
1098  mapPoint.setX( x );
1099  mapPoint.setY( y );
1100  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1101  if ( mRotateMarker )
1102  {
1103  double angle = context.renderContext().geometry()->vertexAngle( vId );
1104  mMarker->setAngle( angle * 180 / M_PI );
1105  }
1106  mMarker->renderPoint( mapPoint, context.feature(), rc, -1, context.selected() );
1107  }
1108  }
1109 
1110  delete context.renderContext().expressionContext().popScope();
1111  return;
1112  }
1113 
1114  if ( placement == FirstVertex )
1115  {
1116  i = 0;
1117  maxCount = 1;
1118  }
1119  else if ( placement == LastVertex )
1120  {
1121  i = points.count() - 1;
1122  maxCount = points.count();
1123  }
1124  else if ( placement == Vertex )
1125  {
1126  i = 0;
1127  maxCount = points.count();
1128  if ( points.first() == points.last() )
1129  isRing = true;
1130  }
1131  else
1132  {
1133  delete context.renderContext().expressionContext().popScope();
1134  return;
1135  }
1136 
1137  if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
1138  {
1139  double distance;
1140  distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine;
1141  renderOffsetVertexAlongLine( points, i, distance, context );
1142  // restore original rotation
1143  mMarker->setAngle( origAngle );
1144 
1145  delete context.renderContext().expressionContext().popScope();
1146  return;
1147  }
1148 
1149  int pointNum = 0;
1150  for ( ; i < maxCount; ++i )
1151  {
1153 
1154  if ( isRing && placement == Vertex && i == points.count() - 1 )
1155  {
1156  continue; // don't draw the last marker - it has been drawn already
1157  }
1158  // rotate marker (if desired)
1159  if ( mRotateMarker )
1160  {
1161  double angle = markerAngle( points, isRing, i );
1162  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1163  }
1164 
1165  mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() );
1166  }
1167 
1168  // restore original rotation
1169  mMarker->setAngle( origAngle );
1170 
1171  delete context.renderContext().expressionContext().popScope();
1172 }
1173 
1174 double QgsMarkerLineSymbolLayerV2::markerAngle( const QPolygonF& points, bool isRing, int vertex )
1175 {
1176  double angle = 0;
1177  const QPointF& pt = points[vertex];
1178 
1179  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1180  {
1181  int prevIndex = vertex - 1;
1182  int nextIndex = vertex + 1;
1183 
1184  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1185  {
1186  prevIndex = points.count() - 2;
1187  nextIndex = 1;
1188  }
1189 
1190  QPointF prevPoint, nextPoint;
1191  while ( prevIndex >= 0 )
1192  {
1193  prevPoint = points[ prevIndex ];
1194  if ( prevPoint != pt )
1195  {
1196  break;
1197  }
1198  --prevIndex;
1199  }
1200 
1201  while ( nextIndex < points.count() )
1202  {
1203  nextPoint = points[ nextIndex ];
1204  if ( nextPoint != pt )
1205  {
1206  break;
1207  }
1208  ++nextIndex;
1209  }
1210 
1211  if ( prevIndex >= 0 && nextIndex < points.count() )
1212  {
1213  angle = _averageAngle( prevPoint, pt, nextPoint );
1214  }
1215  }
1216  else //no ring and vertex is at start / at end
1217  {
1218  if ( vertex == 0 )
1219  {
1220  while ( vertex < points.size() - 1 )
1221  {
1222  const QPointF& nextPt = points[vertex+1];
1223  if ( pt != nextPt )
1224  {
1225  angle = MyLine( pt, nextPt ).angle();
1226  return angle;
1227  }
1228  ++vertex;
1229  }
1230  }
1231  else
1232  {
1233  // use last segment's angle
1234  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1235  {
1236  const QPointF& prevPt = points[vertex-1];
1237  if ( pt != prevPt )
1238  {
1239  angle = MyLine( prevPt, pt ).angle();
1240  return angle;
1241  }
1242  --vertex;
1243  }
1244  }
1245  }
1246  return angle;
1247 }
1248 
1249 void QgsMarkerLineSymbolLayerV2::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolV2RenderContext& context )
1250 {
1251  if ( points.isEmpty() )
1252  return;
1253 
1254  QgsRenderContext& rc = context.renderContext();
1255  double origAngle = mMarker->angle();
1256  if ( qgsDoubleNear( distance, 0.0 ) )
1257  {
1258  // rotate marker (if desired)
1259  if ( mRotateMarker )
1260  {
1261  bool isRing = false;
1262  if ( points.first() == points.last() )
1263  isRing = true;
1264  double angle = markerAngle( points, isRing, vertex );
1265  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1266  }
1267  mMarker->renderPoint( points[vertex], context.feature(), rc, -1, context.selected() );
1268  return;
1269  }
1270 
1271  int pointIncrement = distance > 0 ? 1 : -1;
1272  QPointF previousPoint = points[vertex];
1273  int startPoint = distance > 0 ? qMin( vertex + 1, points.count() - 1 ) : qMax( vertex - 1, 0 );
1274  int endPoint = distance > 0 ? points.count() - 1 : 0;
1275  double distanceLeft = qAbs( distance );
1276 
1277  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1278  {
1279  const QPointF& pt = points[i];
1280 
1281  if ( previousPoint == pt ) // must not be equal!
1282  continue;
1283 
1284  // create line segment
1285  MyLine l( previousPoint, pt );
1286 
1287  if ( distanceLeft < l.length() )
1288  {
1289  //destination point is in current segment
1290  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1291  // rotate marker (if desired)
1292  if ( mRotateMarker )
1293  {
1294  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1295  }
1296  mMarker->renderPoint( markerPoint, context.feature(), rc, -1, context.selected() );
1297  return;
1298  }
1299 
1300  distanceLeft -= l.length();
1301  previousPoint = pt;
1302  }
1303 
1304  //didn't find point
1305  return;
1306 }
1307 
1309 {
1310  if ( !points.isEmpty() )
1311  {
1312  // calc length
1313  qreal length = 0;
1314  QPolygonF::const_iterator it = points.constBegin();
1315  QPointF last = *it;
1316  for ( ++it; it != points.constEnd(); ++it )
1317  {
1318  length += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1319  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1320  last = *it;
1321  }
1322 
1323  // find the segment where the central point lies
1324  it = points.constBegin();
1325  last = *it;
1326  qreal last_at = 0, next_at = 0;
1327  QPointF next;
1328  int segment = 0;
1329  for ( ++it; it != points.constEnd(); ++it )
1330  {
1331  next = *it;
1332  next_at += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1333  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1334  if ( next_at >= length / 2 )
1335  break; // we have reached the center
1336  last = *it;
1337  last_at = next_at;
1338  segment++;
1339  }
1340 
1341  // find out the central point on segment
1342  MyLine l( last, next ); // for line angle
1343  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1344  QPointF pt = last + ( next - last ) * k;
1345 
1346  // draw the marker
1347  double origAngle = mMarker->angle();
1348  if ( mRotateMarker )
1349  mMarker->setAngle( origAngle + l.angle() * 180 / M_PI );
1350  mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() );
1351  if ( mRotateMarker )
1352  mMarker->setAngle( origAngle );
1353  }
1354 }
1355 
1356 
1358 {
1359  QgsStringMap map;
1360  map["rotate"] = ( mRotateMarker ? "1" : "0" );
1361  map["interval"] = QString::number( mInterval );
1362  map["offset"] = QString::number( mOffset );
1363  map["offset_along_line"] = QString::number( mOffsetAlongLine );
1364  map["offset_along_line_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetAlongLineUnit );
1365  map["offset_along_line_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetAlongLineMapUnitScale );
1366  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1367  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1368  map["interval_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mIntervalUnit );
1369  map["interval_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mIntervalMapUnitScale );
1370  if ( mPlacement == Vertex )
1371  map["placement"] = "vertex";
1372  else if ( mPlacement == LastVertex )
1373  map["placement"] = "lastvertex";
1374  else if ( mPlacement == FirstVertex )
1375  map["placement"] = "firstvertex";
1376  else if ( mPlacement == CentralPoint )
1377  map["placement"] = "centralpoint";
1378  else if ( mPlacement == CurvePoint )
1379  map["placement"] = "curvepoint";
1380  else
1381  map["placement"] = "interval";
1382 
1384  return map;
1385 }
1386 
1388 {
1389  return mMarker;
1390 }
1391 
1393 {
1394  if ( !symbol || symbol->type() != QgsSymbolV2::Marker )
1395  {
1396  delete symbol;
1397  return false;
1398  }
1399 
1400  delete mMarker;
1401  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
1402  mColor = mMarker->color();
1403  return true;
1404 }
1405 
1407 {
1408  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( mRotateMarker, mInterval );
1409  x->setSubSymbol( mMarker->clone() );
1410  x->setOffset( mOffset );
1411  x->setPlacement( mPlacement );
1412  x->setOffsetUnit( mOffsetUnit );
1414  x->setIntervalUnit( mIntervalUnit );
1415  x->setIntervalMapUnitScale( mIntervalMapUnitScale );
1416  x->setOffsetAlongLine( mOffsetAlongLine );
1417  x->setOffsetAlongLineMapUnitScale( mOffsetAlongLineMapUnitScale );
1418  x->setOffsetAlongLineUnit( mOffsetAlongLineUnit );
1420  copyPaintEffect( x );
1421  return x;
1422 }
1423 
1425 {
1426  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1427  {
1428  QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
1429  if ( !props.value( "uom", "" ).isEmpty() )
1430  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
1431  element.appendChild( symbolizerElem );
1432 
1433  // <Geometry>
1434  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
1435 
1436  QString gap;
1437  switch ( mPlacement )
1438  {
1439  case FirstVertex:
1440  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "firstPoint" ) );
1441  break;
1442  case LastVertex:
1443  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) );
1444  break;
1445  case CentralPoint:
1446  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "centralPoint" ) );
1447  break;
1448  case Vertex:
1449  // no way to get line/polygon's vertices, use a VendorOption
1450  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "points" ) );
1451  break;
1452  default:
1453  double interval = QgsSymbolLayerV2Utils::rescaleUom( mInterval, mIntervalUnit, props );
1454  gap = qgsDoubleToString( interval );
1455  break;
1456  }
1457 
1458  if ( !mRotateMarker )
1459  {
1460  // markers in LineSymbolizer must be drawn following the line orientation,
1461  // use a VendorOption when no marker rotation
1462  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "rotateMarker", "0" ) );
1463  }
1464 
1465  // <Stroke>
1466  QDomElement strokeElem = doc.createElement( "se:Stroke" );
1467  symbolizerElem.appendChild( strokeElem );
1468 
1469  // <GraphicStroke>
1470  QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" );
1471  strokeElem.appendChild( graphicStrokeElem );
1472 
1473  QgsSymbolLayerV2 *layer = mMarker->symbolLayer( i );
1474  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
1475  if ( !markerLayer )
1476  {
1477  graphicStrokeElem.appendChild( doc.createComment( QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() ) ) );
1478  }
1479  else
1480  {
1481  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1482  }
1483 
1484  if ( !gap.isEmpty() )
1485  {
1486  QDomElement gapElem = doc.createElement( "se:Gap" );
1488  graphicStrokeElem.appendChild( gapElem );
1489  }
1490 
1491  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1492  {
1493  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
1495  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
1496  symbolizerElem.appendChild( perpOffsetElem );
1497  }
1498  }
1499 }
1500 
1502 {
1503  QgsDebugMsg( "Entered." );
1504 
1505  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1506  if ( strokeElem.isNull() )
1507  return nullptr;
1508 
1509  QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
1510  if ( graphicStrokeElem.isNull() )
1511  return nullptr;
1512 
1513  // retrieve vendor options
1514  bool rotateMarker = true;
1515  Placement placement = Interval;
1516 
1517  QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( element );
1518  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1519  {
1520  if ( it.key() == "placement" )
1521  {
1522  if ( it.value() == "points" ) placement = Vertex;
1523  else if ( it.value() == "firstPoint" ) placement = FirstVertex;
1524  else if ( it.value() == "lastPoint" ) placement = LastVertex;
1525  else if ( it.value() == "centralPoint" ) placement = CentralPoint;
1526  }
1527  else if ( it.value() == "rotateMarker" )
1528  {
1529  rotateMarker = it.value() == "0";
1530  }
1531  }
1532 
1533  QgsMarkerSymbolV2 *marker = nullptr;
1534 
1536  if ( l )
1537  {
1538  QgsSymbolLayerV2List layers;
1539  layers.append( l );
1540  marker = new QgsMarkerSymbolV2( layers );
1541  }
1542 
1543  if ( !marker )
1544  return nullptr;
1545 
1546  double interval = 0.0;
1547  QDomElement gapElem = graphicStrokeElem.firstChildElement( "Gap" );
1548  if ( !gapElem.isNull() )
1549  {
1550  bool ok;
1551  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1552  if ( ok )
1553  interval = d;
1554  }
1555 
1556  double offset = 0.0;
1557  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( "PerpendicularOffset" );
1558  if ( !perpOffsetElem.isNull() )
1559  {
1560  bool ok;
1561  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1562  if ( ok )
1563  offset = d;
1564  }
1565 
1566  QString uom = element.attribute( QString( "uom" ), "" );
1567  interval = QgsSymbolLayerV2Utils::sizeInPixelsFromSldUom( uom, interval );
1568  offset = QgsSymbolLayerV2Utils::sizeInPixelsFromSldUom( uom, offset );
1569 
1570  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotateMarker );
1572  x->setPlacement( placement );
1573  x->setInterval( interval );
1574  x->setSubSymbol( marker );
1575  x->setOffset( offset );
1576  return x;
1577 }
1578 
1580 {
1581  mMarker->setSize( width );
1582 }
1583 
1585 {
1586  if ( property == QgsSymbolLayerV2::EXPR_WIDTH && mMarker && dataDefined )
1587  {
1588  mMarker->setDataDefinedSize( *dataDefined );
1589  }
1590  QgsLineSymbolLayerV2::setDataDefinedProperty( property, dataDefined );
1591 }
1592 
1594 {
1595  return mMarker->size();
1596 }
1597 
1599 {
1601  mMarker->setOutputUnit( unit );
1602  mIntervalUnit = unit;
1603  mOffsetUnit = unit;
1604  mOffsetAlongLineUnit = unit;
1605 }
1606 
1608 {
1610  if ( mIntervalUnit != unit || mOffsetUnit != unit || mOffsetAlongLineUnit != unit )
1611  {
1612  return QgsSymbolV2::Mixed;
1613  }
1614  return unit;
1615 }
1616 
1618 {
1620  mIntervalMapUnitScale = scale;
1621  mOffsetMapUnitScale = scale;
1622  mOffsetAlongLineMapUnitScale = scale;
1623 }
1624 
1626 {
1627  if ( QgsLineSymbolLayerV2::mapUnitScale() == mIntervalMapUnitScale &&
1628  mIntervalMapUnitScale == mOffsetMapUnitScale &&
1629  mOffsetMapUnitScale == mOffsetAlongLineMapUnitScale )
1630  {
1631  return mOffsetMapUnitScale;
1632  }
1633  return QgsMapUnitScale();
1634 }
1635 
1637 {
1639  if ( mMarker )
1640  attr.unite( mMarker->usedAttributes() );
1641  return attr;
1642 }
1643 
1645 {
1646  return ( mMarker->size() / 2.0 ) + mOffset;
1647 }
1648 
1649 
1650 
void setIntervalUnit(QgsSymbolV2::OutputUnit unit)
static QString encodeOutputUnit(QgsSymbolV2::OutputUnit unit)
static double mapUnitScaleFactor(double scaleDenominator, QgsSymbolV2::OutputUnit symbolUnits, QGis::UnitType mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
Qt::PenCapStyle penCapStyle() const
#define DEFAULT_SIMPLELINE_PENSTYLE
Single variable definition for use within a QgsExpressionContextScope.
#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:394
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
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
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 attribute(const QString &name, const QString &defValue) const
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
static double sizeInPixelsFromSldUom(const QString &uom, double size)
Returns the size scaled in pixels according to the uom attribute.
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
The output shall be in pixels.
Definition: qgssymbolv2.h:70
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:388
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
QGis::UnitType mapUnits() const
Retrieve map units.
Definition: qgsdxfexport.h:122
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
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
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:109
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:376
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
The output shall be in map unitx.
Definition: qgssymbolv2.h:68
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.
double mapUnitsPerPixel() const
Return current map units per pixel.
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
QgsSymbolV2::OutputUnit offsetUnit() const
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:359
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
double scaleFactor() 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.