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