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