QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgslinesymbollayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslinesymbollayer.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 "qgslinesymbollayer.h"
17 #include "qgscurve.h"
18 #include "qgscurvepolygon.h"
19 #include "qgsdxfexport.h"
20 #include "qgssymbollayerutils.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 #include "qgsproperty.h"
29 
30 #include <QPainter>
31 #include <QDomDocument>
32 #include <QDomElement>
33 
34 #include <cmath>
35 
36 QgsSimpleLineSymbolLayer::QgsSimpleLineSymbolLayer( const QColor &color, double width, Qt::PenStyle penStyle )
37  : mPenStyle( penStyle )
38 {
39  mColor = color;
40  mWidth = width;
41  mCustomDashVector << 5 << 2;
42 }
43 
45 {
47  mWidthUnit = unit;
48  mOffsetUnit = unit;
49  mCustomDashPatternUnit = unit;
50 }
51 
53 {
55  if ( mWidthUnit != unit || mOffsetUnit != unit || mCustomDashPatternUnit != unit )
56  {
58  }
59  return unit;
60 }
61 
63 {
65  mWidthMapUnitScale = scale;
66  mOffsetMapUnitScale = scale;
67  mCustomDashPatternMapUnitScale = scale;
68 }
69 
71 {
74  mOffsetMapUnitScale == mCustomDashPatternMapUnitScale )
75  {
76  return mWidthMapUnitScale;
77  }
78  return QgsMapUnitScale();
79 }
80 
82 {
86 
87  if ( props.contains( QStringLiteral( "line_color" ) ) )
88  {
89  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] );
90  }
91  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
92  {
93  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] );
94  }
95  else if ( props.contains( QStringLiteral( "color" ) ) )
96  {
97  //pre 2.5 projects used "color"
98  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
99  }
100  if ( props.contains( QStringLiteral( "line_width" ) ) )
101  {
102  width = props[QStringLiteral( "line_width" )].toDouble();
103  }
104  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
105  {
106  width = props[QStringLiteral( "outline_width" )].toDouble();
107  }
108  else if ( props.contains( QStringLiteral( "width" ) ) )
109  {
110  //pre 2.5 projects used "width"
111  width = props[QStringLiteral( "width" )].toDouble();
112  }
113  if ( props.contains( QStringLiteral( "line_style" ) ) )
114  {
115  penStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )] );
116  }
117  else if ( props.contains( QStringLiteral( "outline_style" ) ) )
118  {
119  penStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )] );
120  }
121  else if ( props.contains( QStringLiteral( "penstyle" ) ) )
122  {
123  penStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "penstyle" )] );
124  }
125 
126  QgsSimpleLineSymbolLayer *l = new QgsSimpleLineSymbolLayer( color, width, penStyle );
127  if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
128  {
129  l->setWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
130  }
131  else if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
132  {
133  l->setWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
134  }
135  else if ( props.contains( QStringLiteral( "width_unit" ) ) )
136  {
137  //pre 2.5 projects used "width_unit"
138  l->setWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "width_unit" )] ) );
139  }
140  if ( props.contains( QStringLiteral( "width_map_unit_scale" ) ) )
141  l->setWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "width_map_unit_scale" )] ) );
142  if ( props.contains( QStringLiteral( "offset" ) ) )
143  l->setOffset( props[QStringLiteral( "offset" )].toDouble() );
144  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
145  l->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
146  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
147  l->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
148  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
149  l->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] ) );
150  if ( props.contains( QStringLiteral( "capstyle" ) ) )
151  l->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "capstyle" )] ) );
152 
153  if ( props.contains( QStringLiteral( "use_custom_dash" ) ) )
154  {
155  l->setUseCustomDashPattern( props[QStringLiteral( "use_custom_dash" )].toInt() );
156  }
157  if ( props.contains( QStringLiteral( "customdash" ) ) )
158  {
159  l->setCustomDashVector( QgsSymbolLayerUtils::decodeRealVector( props[QStringLiteral( "customdash" )] ) );
160  }
161  if ( props.contains( QStringLiteral( "customdash_unit" ) ) )
162  {
163  l->setCustomDashPatternUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "customdash_unit" )] ) );
164  }
165  if ( props.contains( QStringLiteral( "customdash_map_unit_scale" ) ) )
166  {
167  l->setCustomDashPatternMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "customdash_map_unit_scale" )] ) );
168  }
169 
170  if ( props.contains( QStringLiteral( "draw_inside_polygon" ) ) )
171  {
172  l->setDrawInsidePolygon( props[QStringLiteral( "draw_inside_polygon" )].toInt() );
173  }
174 
175  if ( props.contains( QStringLiteral( "ring_filter" ) ) )
176  {
177  l->setRingFilter( static_cast< RenderRingFilter>( props[QStringLiteral( "ring_filter" )].toInt() ) );
178  }
179 
181 
182  return l;
183 }
184 
185 
187 {
188  return QStringLiteral( "SimpleLine" );
189 }
190 
192 {
193  QColor penColor = mColor;
194  penColor.setAlphaF( mColor.alphaF() * context.opacity() );
195  mPen.setColor( penColor );
196  double scaledWidth = context.renderContext().convertToPainterUnits( mWidth, mWidthUnit, mWidthMapUnitScale );
197  mPen.setWidthF( scaledWidth );
198  if ( mUseCustomDashPattern )
199  {
200  mPen.setStyle( Qt::CustomDashLine );
201 
202  //scale pattern vector
203  double dashWidthDiv = qgsDoubleNear( scaledWidth, 0 ) ? 1.0 : scaledWidth;
204 
205  //fix dash pattern width in Qt 4.8
206  QStringList versionSplit = QString( qVersion() ).split( '.' );
207  if ( versionSplit.size() > 1
208  && versionSplit.at( 1 ).toInt() >= 8
209  && scaledWidth < 1.0 )
210  {
211  dashWidthDiv = 1.0;
212  }
213  QVector<qreal> scaledVector;
214  QVector<qreal>::const_iterator it = mCustomDashVector.constBegin();
215  for ( ; it != mCustomDashVector.constEnd(); ++it )
216  {
217  //the dash is specified in terms of pen widths, therefore the division
218  scaledVector << context.renderContext().convertToPainterUnits( ( *it ), mCustomDashPatternUnit, mCustomDashPatternMapUnitScale ) / dashWidthDiv;
219  }
220  mPen.setDashPattern( scaledVector );
221  }
222  else
223  {
224  mPen.setStyle( mPenStyle );
225  }
226  mPen.setJoinStyle( mPenJoinStyle );
227  mPen.setCapStyle( mPenCapStyle );
228 
229  mSelPen = mPen;
230  QColor selColor = context.renderContext().selectionColor();
231  if ( ! SELECTION_IS_OPAQUE )
232  selColor.setAlphaF( context.opacity() );
233  mSelPen.setColor( selColor );
234 }
235 
237 {
238  Q_UNUSED( context )
239 }
240 
241 void QgsSimpleLineSymbolLayer::renderPolygonStroke( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
242 {
243  QPainter *p = context.renderContext().painter();
244  if ( !p )
245  {
246  return;
247  }
248 
249  if ( mDrawInsidePolygon )
250  p->save();
251 
252  switch ( mRingFilter )
253  {
254  case AllRings:
255  case ExteriorRingOnly:
256  {
257  if ( mDrawInsidePolygon )
258  {
259  //only drawing the line on the interior of the polygon, so set clip path for painter
260  QPainterPath clipPath;
261  clipPath.addPolygon( points );
262 
263  if ( rings )
264  {
265  //add polygon rings
266  QList<QPolygonF>::const_iterator it = rings->constBegin();
267  for ( ; it != rings->constEnd(); ++it )
268  {
269  QPolygonF ring = *it;
270  clipPath.addPolygon( ring );
271  }
272  }
273 
274  //use intersect mode, as a clip path may already exist (e.g., for composer maps)
275  p->setClipPath( clipPath, Qt::IntersectClip );
276  }
277 
278  renderPolyline( points, context );
279  }
280  break;
281 
282  case InteriorRingsOnly:
283  break;
284  }
285 
286  if ( rings )
287  {
288  switch ( mRingFilter )
289  {
290  case AllRings:
291  case InteriorRingsOnly:
292  {
293  mOffset = -mOffset; // invert the offset for rings!
294  for ( const QPolygonF &ring : qgis::as_const( *rings ) )
295  renderPolyline( ring, context );
296  mOffset = -mOffset;
297  }
298  break;
299  case ExteriorRingOnly:
300  break;
301  }
302  }
303 
304  if ( mDrawInsidePolygon )
305  {
306  //restore painter to reset clip path
307  p->restore();
308  }
309 
310 }
311 
312 void QgsSimpleLineSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context )
313 {
314  QPainter *p = context.renderContext().painter();
315  if ( !p )
316  {
317  return;
318  }
319 
320  double offset = mOffset;
321  applyDataDefinedSymbology( context, mPen, mSelPen, offset );
322 
323  p->setPen( context.selected() ? mSelPen : mPen );
324  p->setBrush( Qt::NoBrush );
325 
326  // Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #2 points).
327  if ( points.size() <= 2 &&
330  ( p->renderHints() & QPainter::Antialiasing ) )
331  {
332  p->setRenderHint( QPainter::Antialiasing, false );
333 #if 0
334  p->drawPolyline( points );
335 #else
336  QPainterPath path;
337  path.addPolygon( points );
338  p->drawPath( path );
339 #endif
340  p->setRenderHint( QPainter::Antialiasing, true );
341  return;
342  }
343 
344  if ( qgsDoubleNear( offset, 0 ) )
345  {
346 #if 0
347  p->drawPolyline( points );
348 #else
349  QPainterPath path;
350  path.addPolygon( points );
351  p->drawPath( path );
352 #endif
353  }
354  else
355  {
356  double scaledOffset = context.renderContext().convertToPainterUnits( offset, mOffsetUnit, mOffsetMapUnitScale );
357  QList<QPolygonF> mline = ::offsetLine( points, scaledOffset, context.originalGeometryType() != QgsWkbTypes::UnknownGeometry ? context.originalGeometryType() : QgsWkbTypes::LineGeometry );
358  for ( int part = 0; part < mline.count(); ++part )
359  {
360 #if 0
361  p->drawPolyline( mline );
362 #else
363  QPainterPath path;
364  path.addPolygon( mline[ part ] );
365  p->drawPath( path );
366 #endif
367  }
368  }
369 }
370 
372 {
373  QgsStringMap map;
374  map[QStringLiteral( "line_color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
375  map[QStringLiteral( "line_width" )] = QString::number( mWidth );
376  map[QStringLiteral( "line_width_unit" )] = QgsUnitTypes::encodeUnit( mWidthUnit );
377  map[QStringLiteral( "width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mWidthMapUnitScale );
378  map[QStringLiteral( "line_style" )] = QgsSymbolLayerUtils::encodePenStyle( mPenStyle );
379  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
380  map[QStringLiteral( "capstyle" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
381  map[QStringLiteral( "offset" )] = QString::number( mOffset );
382  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
383  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
384  map[QStringLiteral( "use_custom_dash" )] = ( mUseCustomDashPattern ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
385  map[QStringLiteral( "customdash" )] = QgsSymbolLayerUtils::encodeRealVector( mCustomDashVector );
386  map[QStringLiteral( "customdash_unit" )] = QgsUnitTypes::encodeUnit( mCustomDashPatternUnit );
387  map[QStringLiteral( "customdash_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mCustomDashPatternMapUnitScale );
388  map[QStringLiteral( "draw_inside_polygon" )] = ( mDrawInsidePolygon ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
389  map[QStringLiteral( "ring_filter" )] = QString::number( static_cast< int >( mRingFilter ) );
390  return map;
391 }
392 
394 {
396  l->setWidthUnit( mWidthUnit );
400  l->setCustomDashPatternUnit( mCustomDashPatternUnit );
401  l->setCustomDashPatternMapUnitScale( mCustomDashPatternMapUnitScale );
402  l->setOffset( mOffset );
403  l->setPenJoinStyle( mPenJoinStyle );
404  l->setPenCapStyle( mPenCapStyle );
405  l->setUseCustomDashPattern( mUseCustomDashPattern );
406  l->setCustomDashVector( mCustomDashVector );
407  l->setDrawInsidePolygon( mDrawInsidePolygon );
410  copyPaintEffect( l );
411  return l;
412 }
413 
414 void QgsSimpleLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
415 {
416  if ( mPenStyle == Qt::NoPen )
417  return;
418 
419  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:LineSymbolizer" ) );
420  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
421  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
422  element.appendChild( symbolizerElem );
423 
424  // <Geometry>
425  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
426 
427  // <Stroke>
428  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
429  symbolizerElem.appendChild( strokeElem );
430 
431  Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
433  QVector<qreal> customDashVector = QgsSymbolLayerUtils::rescaleUom( mCustomDashVector, mCustomDashPatternUnit, props );
434  QgsSymbolLayerUtils::lineToSld( doc, strokeElem, penStyle, mColor, width,
435  &mPenJoinStyle, &mPenCapStyle, &customDashVector );
436 
437  // <se:PerpendicularOffset>
438  if ( !qgsDoubleNear( mOffset, 0.0 ) )
439  {
440  QDomElement perpOffsetElem = doc.createElement( QStringLiteral( "se:PerpendicularOffset" ) );
442  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
443  symbolizerElem.appendChild( perpOffsetElem );
444  }
445 }
446 
447 QString QgsSimpleLineSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
448 {
449  if ( mUseCustomDashPattern )
450  {
451  return QgsSymbolLayerUtils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor,
452  mPen.color(), mPenJoinStyle,
453  mPenCapStyle, mOffset, &mCustomDashVector );
454  }
455  else
456  {
457  return QgsSymbolLayerUtils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor, mPen.color(), mPenJoinStyle,
458  mPenCapStyle, mOffset );
459  }
460 }
461 
463 {
464  QgsDebugMsg( QStringLiteral( "Entered." ) );
465 
466  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
467  if ( strokeElem.isNull() )
468  return nullptr;
469 
470  Qt::PenStyle penStyle;
471  QColor color;
472  double width;
473  Qt::PenJoinStyle penJoinStyle;
474  Qt::PenCapStyle penCapStyle;
475  QVector<qreal> customDashVector;
476 
477  if ( !QgsSymbolLayerUtils::lineFromSld( strokeElem, penStyle,
478  color, width,
479  &penJoinStyle, &penCapStyle,
480  &customDashVector ) )
481  return nullptr;
482 
483  double offset = 0.0;
484  QDomElement perpOffsetElem = element.firstChildElement( QStringLiteral( "PerpendicularOffset" ) );
485  if ( !perpOffsetElem.isNull() )
486  {
487  bool ok;
488  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
489  if ( ok )
490  offset = d;
491  }
492 
493  QString uom = element.attribute( QStringLiteral( "uom" ) );
494  width = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, width );
495  offset = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset );
496 
497  QgsSimpleLineSymbolLayer *l = new QgsSimpleLineSymbolLayer( color, width, penStyle );
498  l->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
499  l->setOffset( offset );
500  l->setPenJoinStyle( penJoinStyle );
501  l->setPenCapStyle( penCapStyle );
502  l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine );
503  l->setCustomDashVector( customDashVector );
504  return l;
505 }
506 
507 void QgsSimpleLineSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, QPen &pen, QPen &selPen, double &offset )
508 {
509  if ( !dataDefinedProperties().hasActiveProperties() )
510  return; // shortcut
511 
512  //data defined properties
513  bool hasStrokeWidthExpression = false;
515  {
516  context.setOriginalValueVariable( mWidth );
517  double scaledWidth = context.renderContext().convertToPainterUnits(
520  pen.setWidthF( scaledWidth );
521  selPen.setWidthF( scaledWidth );
522  hasStrokeWidthExpression = true;
523  }
524 
525  //color
527  {
530  }
531 
532  //offset
534  {
537  }
538 
539  //dash dot vector
541  {
542  double scaledWidth = context.renderContext().convertToPainterUnits( mWidth, mWidthUnit, mWidthMapUnitScale );
543  double dashWidthDiv = mPen.widthF();
544 
545  if ( hasStrokeWidthExpression )
546  {
547  dashWidthDiv = pen.widthF();
548  scaledWidth = pen.widthF();
549  }
550 
551  //fix dash pattern width in Qt 4.8
552  QStringList versionSplit = QString( qVersion() ).split( '.' );
553  if ( versionSplit.size() > 1
554  && versionSplit.at( 1 ).toInt() >= 8
555  && scaledWidth < 1.0 )
556  {
557  dashWidthDiv = 1.0;
558  }
559 
560  QVector<qreal> dashVector;
562  if ( exprVal.isValid() )
563  {
564  QStringList dashList = exprVal.toString().split( ';' );
565  QStringList::const_iterator dashIt = dashList.constBegin();
566  for ( ; dashIt != dashList.constEnd(); ++dashIt )
567  {
568  dashVector.push_back( context.renderContext().convertToPainterUnits( dashIt->toDouble(), mCustomDashPatternUnit, mCustomDashPatternMapUnitScale ) / dashWidthDiv );
569  }
570  pen.setDashPattern( dashVector );
571  }
572  }
573 
574  //line style
576  {
579  if ( exprVal.isValid() )
580  pen.setStyle( QgsSymbolLayerUtils::decodePenStyle( exprVal.toString() ) );
581  }
582 
583  //join style
585  {
588  if ( exprVal.isValid() )
589  pen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( exprVal.toString() ) );
590  }
591 
592  //cap style
594  {
597  if ( exprVal.isValid() )
598  pen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( exprVal.toString() ) );
599  }
600 }
601 
603 {
604  if ( mDrawInsidePolygon )
605  {
606  //set to clip line to the interior of polygon, so we expect no bleed
607  return 0;
608  }
609  else
610  {
611  return context.convertToPainterUnits( ( mWidth / 2.0 ), mWidthUnit, mWidthMapUnitScale ) +
613  }
614 }
615 
617 {
618  unit = mCustomDashPatternUnit;
619  return mUseCustomDashPattern ? mCustomDashVector : QVector<qreal>();
620 }
621 
623 {
624  return mPenStyle;
625 }
626 
628 {
629  double width = mWidth;
631  {
632  context.setOriginalValueVariable( mWidth );
634  }
635 
638  {
640  }
641  return width;
642 }
643 
645 {
647  {
650  }
651  return mColor;
652 }
653 
655 {
656  Q_UNUSED( e )
657  double offset = mOffset;
658 
660  {
663  }
664 
667  {
669  }
670  return -offset; //direction seems to be inverse to symbology offset
671 }
672 
674 
676 
677 class MyLine
678 {
679  public:
680  MyLine( QPointF p1, QPointF p2 )
681  : mVertical( false )
682  , mIncreasing( false )
683  , mT( 0.0 )
684  , mLength( 0.0 )
685  {
686  if ( p1 == p2 )
687  return; // invalid
688 
689  // tangent and direction
690  if ( qgsDoubleNear( p1.x(), p2.x() ) )
691  {
692  // vertical line - tangent undefined
693  mVertical = true;
694  mIncreasing = ( p2.y() > p1.y() );
695  }
696  else
697  {
698  mVertical = false;
699  mT = ( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
700  mIncreasing = ( p2.x() > p1.x() );
701  }
702 
703  // length
704  double x = ( p2.x() - p1.x() );
705  double y = ( p2.y() - p1.y() );
706  mLength = std::sqrt( x * x + y * y );
707  }
708 
709  // return angle in radians
710  double angle()
711  {
712  double a = ( mVertical ? M_PI_2 : std::atan( mT ) );
713 
714  if ( !mIncreasing )
715  a += M_PI;
716  return a;
717  }
718 
719  // return difference for x,y when going along the line with specified interval
720  QPointF diffForInterval( double interval )
721  {
722  if ( mVertical )
723  return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
724 
725  double alpha = std::atan( mT );
726  double dx = std::cos( alpha ) * interval;
727  double dy = std::sin( alpha ) * interval;
728  return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
729  }
730 
731  double length() { return mLength; }
732 
733  protected:
734  bool mVertical;
735  bool mIncreasing;
736  double mT;
737  double mLength;
738 };
739 
741 
742 //
743 // QgsTemplatedLineSymbolLayerBase
744 //
746  : mRotateSymbols( rotateSymbol )
747  , mInterval( interval )
748 {
749 
750 }
751 
753 {
754  double offset = mOffset;
755 
757  {
760  }
761 
763 
765  {
767  if ( exprVal.isValid() )
768  {
769  QString placementString = exprVal.toString();
770  if ( placementString.compare( QLatin1String( "interval" ), Qt::CaseInsensitive ) == 0 )
771  {
773  }
774  else if ( placementString.compare( QLatin1String( "vertex" ), Qt::CaseInsensitive ) == 0 )
775  {
777  }
778  else if ( placementString.compare( QLatin1String( "lastvertex" ), Qt::CaseInsensitive ) == 0 )
779  {
781  }
782  else if ( placementString.compare( QLatin1String( "firstvertex" ), Qt::CaseInsensitive ) == 0 )
783  {
785  }
786  else if ( placementString.compare( QLatin1String( "centerpoint" ), Qt::CaseInsensitive ) == 0 )
787  {
789  }
790  else if ( placementString.compare( QLatin1String( "curvepoint" ), Qt::CaseInsensitive ) == 0 )
791  {
793  }
794  else if ( placementString.compare( QLatin1String( "segmentcenter" ), Qt::CaseInsensitive ) == 0 )
795  {
797  }
798  else
799  {
801  }
802  }
803  }
804 
805 
806  context.renderContext().painter()->save();
807 
808  double averageOver = mAverageAngleLength;
810  {
811  context.setOriginalValueVariable( mAverageAngleLength );
813  }
814  averageOver = context.renderContext().convertToPainterUnits( averageOver, mAverageAngleLengthUnit, mAverageAngleLengthMapUnitScale ) / 2.0;
815 
816  if ( qgsDoubleNear( offset, 0.0 ) )
817  {
818  switch ( placement )
819  {
820  case Interval:
821  renderPolylineInterval( points, context, averageOver );
822  break;
823 
824  case CentralPoint:
825  renderPolylineCentral( points, context, averageOver );
826  break;
827 
828  case Vertex:
829  case LastVertex:
830  case FirstVertex:
831  case CurvePoint:
832  case SegmentCenter:
833  renderPolylineVertex( points, context, placement );
834  break;
835  }
836  }
837  else
838  {
839  context.renderContext().setGeometry( nullptr ); //always use segmented geometry with offset
841 
842  for ( int part = 0; part < mline.count(); ++part )
843  {
844  const QPolygonF &points2 = mline[ part ];
845 
846  switch ( placement )
847  {
848  case Interval:
849  renderPolylineInterval( points2, context, averageOver );
850  break;
851 
852  case CentralPoint:
853  renderPolylineCentral( points2, context, averageOver );
854  break;
855 
856  case Vertex:
857  case LastVertex:
858  case FirstVertex:
859  case CurvePoint:
860  case SegmentCenter:
861  renderPolylineVertex( points2, context, placement );
862  break;
863  }
864  }
865  }
866 
867  context.renderContext().painter()->restore();
868 }
869 
870 void QgsTemplatedLineSymbolLayerBase::renderPolygonStroke( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
871 {
872  const QgsCurvePolygon *curvePolygon = dynamic_cast<const QgsCurvePolygon *>( context.renderContext().geometry() );
873 
874  if ( curvePolygon )
875  {
876  context.renderContext().setGeometry( curvePolygon->exteriorRing() );
877  }
878 
879  switch ( mRingFilter )
880  {
881  case AllRings:
882  case ExteriorRingOnly:
883  renderPolyline( points, context );
884  break;
885  case InteriorRingsOnly:
886  break;
887  }
888 
889  if ( rings )
890  {
891  switch ( mRingFilter )
892  {
893  case AllRings:
894  case InteriorRingsOnly:
895  {
896  mOffset = -mOffset; // invert the offset for rings!
897  for ( int i = 0; i < rings->size(); ++i )
898  {
899  if ( curvePolygon )
900  {
901  context.renderContext().setGeometry( curvePolygon->interiorRing( i ) );
902  }
903  renderPolyline( rings->at( i ), context );
904  }
905  mOffset = -mOffset;
906  }
907  break;
908  case ExteriorRingOnly:
909  break;
910  }
911  }
912 }
913 
915 {
917  if ( intervalUnit() != unit || mOffsetUnit != unit || offsetAlongLineUnit() != unit )
918  {
920  }
921  return unit;
922 }
923 
925 {
927  setIntervalMapUnitScale( scale );
928  mOffsetMapUnitScale = scale;
930 }
931 
933 {
937  {
938  return mOffsetMapUnitScale;
939  }
940  return QgsMapUnitScale();
941 }
942 
944 {
945  QgsStringMap map;
946  map[QStringLiteral( "rotate" )] = ( rotateSymbols() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
947  map[QStringLiteral( "interval" )] = QString::number( interval() );
948  map[QStringLiteral( "offset" )] = QString::number( mOffset );
949  map[QStringLiteral( "offset_along_line" )] = QString::number( offsetAlongLine() );
950  map[QStringLiteral( "offset_along_line_unit" )] = QgsUnitTypes::encodeUnit( offsetAlongLineUnit() );
951  map[QStringLiteral( "offset_along_line_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( offsetAlongLineMapUnitScale() );
952  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
953  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
954  map[QStringLiteral( "interval_unit" )] = QgsUnitTypes::encodeUnit( intervalUnit() );
955  map[QStringLiteral( "interval_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( intervalMapUnitScale() );
956  map[QStringLiteral( "average_angle_length" )] = QString::number( mAverageAngleLength );
957  map[QStringLiteral( "average_angle_unit" )] = QgsUnitTypes::encodeUnit( mAverageAngleLengthUnit );
958  map[QStringLiteral( "average_angle_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mAverageAngleLengthMapUnitScale );
959 
960  switch ( mPlacement )
961  {
962  case Vertex:
963  map[QStringLiteral( "placement" )] = QStringLiteral( "vertex" );
964  break;
965  case LastVertex:
966  map[QStringLiteral( "placement" )] = QStringLiteral( "lastvertex" );
967  break;
968  case FirstVertex:
969  map[QStringLiteral( "placement" )] = QStringLiteral( "firstvertex" );
970  break;
971  case CentralPoint:
972  map[QStringLiteral( "placement" )] = QStringLiteral( "centralpoint" );
973  break;
974  case CurvePoint:
975  map[QStringLiteral( "placement" )] = QStringLiteral( "curvepoint" );
976  break;
977  case Interval:
978  map[QStringLiteral( "placement" )] = QStringLiteral( "interval" );
979  break;
980  case SegmentCenter:
981  map[QStringLiteral( "placement" )] = QStringLiteral( "segmentcenter" );
982  break;
983  }
984 
985  map[QStringLiteral( "ring_filter" )] = QString::number( static_cast< int >( mRingFilter ) );
986  return map;
987 }
988 
990 {
991  destLayer->setSubSymbol( const_cast< QgsTemplatedLineSymbolLayerBase * >( this )->subSymbol()->clone() );
992  destLayer->setOffset( mOffset );
993  destLayer->setPlacement( placement() );
994  destLayer->setOffsetUnit( mOffsetUnit );
996  destLayer->setIntervalUnit( intervalUnit() );
998  destLayer->setOffsetAlongLine( offsetAlongLine() );
1001  destLayer->setAverageAngleLength( mAverageAngleLength );
1002  destLayer->setAverageAngleUnit( mAverageAngleLengthUnit );
1003  destLayer->setAverageAngleMapUnitScale( mAverageAngleLengthMapUnitScale );
1004  destLayer->setRingFilter( mRingFilter );
1005  copyDataDefinedProperties( destLayer );
1006  copyPaintEffect( destLayer );
1007 }
1008 
1010 {
1011  if ( properties.contains( QStringLiteral( "offset" ) ) )
1012  {
1013  destLayer->setOffset( properties[QStringLiteral( "offset" )].toDouble() );
1014  }
1015  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
1016  {
1017  destLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )] ) );
1018  }
1019  if ( properties.contains( QStringLiteral( "interval_unit" ) ) )
1020  {
1021  destLayer->setIntervalUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "interval_unit" )] ) );
1022  }
1023  if ( properties.contains( QStringLiteral( "offset_along_line" ) ) )
1024  {
1025  destLayer->setOffsetAlongLine( properties[QStringLiteral( "offset_along_line" )].toDouble() );
1026  }
1027  if ( properties.contains( QStringLiteral( "offset_along_line_unit" ) ) )
1028  {
1029  destLayer->setOffsetAlongLineUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_along_line_unit" )] ) );
1030  }
1031  if ( properties.contains( ( QStringLiteral( "offset_along_line_map_unit_scale" ) ) ) )
1032  {
1033  destLayer->setOffsetAlongLineMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_along_line_map_unit_scale" )] ) );
1034  }
1035 
1036  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1037  {
1038  destLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )] ) );
1039  }
1040  if ( properties.contains( QStringLiteral( "interval_map_unit_scale" ) ) )
1041  {
1042  destLayer->setIntervalMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "interval_map_unit_scale" )] ) );
1043  }
1044 
1045  if ( properties.contains( QStringLiteral( "average_angle_length" ) ) )
1046  {
1047  destLayer->setAverageAngleLength( properties[QStringLiteral( "average_angle_length" )].toDouble() );
1048  }
1049  if ( properties.contains( QStringLiteral( "average_angle_unit" ) ) )
1050  {
1051  destLayer->setAverageAngleUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "average_angle_unit" )] ) );
1052  }
1053  if ( properties.contains( ( QStringLiteral( "average_angle_map_unit_scale" ) ) ) )
1054  {
1055  destLayer->setAverageAngleMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "average_angle_map_unit_scale" )] ) );
1056  }
1057 
1058  if ( properties.contains( QStringLiteral( "placement" ) ) )
1059  {
1060  if ( properties[QStringLiteral( "placement" )] == QLatin1String( "vertex" ) )
1062  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "lastvertex" ) )
1064  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "firstvertex" ) )
1066  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "centralpoint" ) )
1068  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "curvepoint" ) )
1070  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "segmentcenter" ) )
1072  else
1074  }
1075 
1076  if ( properties.contains( QStringLiteral( "ring_filter" ) ) )
1077  {
1078  destLayer->setRingFilter( static_cast< RenderRingFilter>( properties[QStringLiteral( "ring_filter" )].toInt() ) );
1079  }
1080 
1081  destLayer->restoreOldDataDefinedProperties( properties );
1082 }
1083 
1084 void QgsTemplatedLineSymbolLayerBase::renderPolylineInterval( const QPolygonF &points, QgsSymbolRenderContext &context, double averageOver )
1085 {
1086  if ( points.isEmpty() )
1087  return;
1088 
1089  double lengthLeft = 0; // how much is left until next marker
1090 
1091  QgsRenderContext &rc = context.renderContext();
1092  double interval = mInterval;
1093 
1095  QgsExpressionContextScopePopper scopePopper( context.renderContext().expressionContext(), scope );
1096 
1098  {
1099  context.setOriginalValueVariable( mInterval );
1101  }
1102  if ( interval <= 0 )
1103  {
1104  interval = 0.1;
1105  }
1106  double offsetAlongLine = mOffsetAlongLine;
1108  {
1109  context.setOriginalValueVariable( mOffsetAlongLine );
1111  }
1112 
1113  const double painterUnitInterval = rc.convertToPainterUnits( interval, intervalUnit(), intervalMapUnitScale() );
1114 
1115  if ( painterUnitInterval < 0 )
1116  return;
1117 
1118  const double painterUnitOffsetAlongLine = rc.convertToPainterUnits( offsetAlongLine, offsetAlongLineUnit(), offsetAlongLineMapUnitScale() );
1119  lengthLeft = painterUnitInterval - painterUnitOffsetAlongLine;
1120 
1121  if ( averageOver > 0 && !qgsDoubleNear( averageOver, 0.0 ) )
1122  {
1123  QVector< QPointF > angleStartPoints;
1124  QVector< QPointF > symbolPoints;
1125  QVector< QPointF > angleEndPoints;
1126 
1127  // we collect 3 arrays of points. These correspond to
1128  // 1. the actual point at which to render the symbol
1129  // 2. the start point of a line averaging the angle over the desired distance (i.e. -averageOver distance from the points in array 1)
1130  // 3. the end point of a line averaging the angle over the desired distance (i.e. +averageOver distance from the points in array 2)
1131  // it gets quite tricky, because for closed rings we need to trace backwards from the initial point to calculate this
1132  // (or trace past the final point)
1133  collectOffsetPoints( points, symbolPoints, painterUnitInterval, lengthLeft );
1134 
1135  if ( symbolPoints.empty() )
1136  {
1137  // no symbols to draw, shortcut out early
1138  return;
1139  }
1140 
1141  if ( symbolPoints.count() > 1 && symbolPoints.constFirst() == symbolPoints.constLast() )
1142  {
1143  // avoid duplicate points at start and end of closed rings
1144  symbolPoints.pop_back();
1145  }
1146 
1147  angleEndPoints.reserve( symbolPoints.size() );
1148  angleStartPoints.reserve( symbolPoints.size() );
1149  if ( averageOver <= painterUnitOffsetAlongLine )
1150  {
1151  collectOffsetPoints( points, angleStartPoints, painterUnitInterval, lengthLeft + averageOver, 0, symbolPoints.size() );
1152  }
1153  else
1154  {
1155  collectOffsetPoints( points, angleStartPoints, painterUnitInterval, 0, averageOver - painterUnitOffsetAlongLine, symbolPoints.size() );
1156  }
1157  collectOffsetPoints( points, angleEndPoints, painterUnitInterval, lengthLeft - averageOver, 0, symbolPoints.size() );
1158 
1159  int pointNum = 0;
1160  for ( int i = 0; i < symbolPoints.size(); ++ i )
1161  {
1162  if ( context.renderContext().renderingStopped() )
1163  break;
1164 
1165  const QPointF pt = symbolPoints[i];
1166  const QPointF startPt = angleStartPoints[i];
1167  const QPointF endPt = angleEndPoints[i];
1168 
1169  MyLine l( startPt, endPt );
1170  // rotate marker (if desired)
1171  if ( rotateSymbols() )
1172  {
1173  setSymbolLineAngle( l.angle() * 180 / M_PI );
1174  }
1175 
1177  renderSymbol( pt, context.feature(), rc, -1, context.selected() );
1178  }
1179  }
1180  else
1181  {
1182  // not averaging line angle -- always use exact section angle
1183  int pointNum = 0;
1184  QPointF lastPt = points[0];
1185  for ( int i = 1; i < points.count(); ++i )
1186  {
1187  if ( context.renderContext().renderingStopped() )
1188  break;
1189 
1190  const QPointF &pt = points[i];
1191 
1192  if ( lastPt == pt ) // must not be equal!
1193  continue;
1194 
1195  // for each line, find out dx and dy, and length
1196  MyLine l( lastPt, pt );
1197  QPointF diff = l.diffForInterval( painterUnitInterval );
1198 
1199  // if there's some length left from previous line
1200  // use only the rest for the first point in new line segment
1201  double c = 1 - lengthLeft / painterUnitInterval;
1202 
1203  lengthLeft += l.length();
1204 
1205  // rotate marker (if desired)
1206  if ( rotateSymbols() )
1207  {
1208  setSymbolLineAngle( l.angle() * 180 / M_PI );
1209  }
1210 
1211  // while we're not at the end of line segment, draw!
1212  while ( lengthLeft > painterUnitInterval )
1213  {
1214  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
1215  lastPt += c * diff;
1216  lengthLeft -= painterUnitInterval;
1218  renderSymbol( lastPt, context.feature(), rc, -1, context.selected() );
1219  c = 1; // reset c (if wasn't 1 already)
1220  }
1221 
1222  lastPt = pt;
1223  }
1224 
1225  }
1226 }
1227 
1228 static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt )
1229 {
1230  // calc average angle between the previous and next point
1231  double a1 = MyLine( prevPt, pt ).angle();
1232  double a2 = MyLine( pt, nextPt ).angle();
1233  double unitX = std::cos( a1 ) + std::cos( a2 ), unitY = std::sin( a1 ) + std::sin( a2 );
1234 
1235  return std::atan2( unitY, unitX );
1236 }
1237 
1238 void QgsTemplatedLineSymbolLayerBase::renderPolylineVertex( const QPolygonF &points, QgsSymbolRenderContext &context, QgsTemplatedLineSymbolLayerBase::Placement placement )
1239 {
1240  if ( points.isEmpty() )
1241  return;
1242 
1243  QgsRenderContext &rc = context.renderContext();
1244 
1245  double origAngle = symbolAngle();
1246  int i = -1, maxCount = 0;
1247  bool isRing = false;
1248 
1250  QgsExpressionContextScopePopper scopePopper( context.renderContext().expressionContext(), scope );
1252 
1253  double offsetAlongLine = mOffsetAlongLine;
1255  {
1256  context.setOriginalValueVariable( mOffsetAlongLine );
1258  }
1259  if ( !qgsDoubleNear( offsetAlongLine, 0.0 ) )
1260  {
1261  //scale offset along line
1262  offsetAlongLine = rc.convertToPainterUnits( offsetAlongLine, offsetAlongLineUnit(), offsetAlongLineMapUnitScale() );
1263  }
1264 
1265  if ( qgsDoubleNear( offsetAlongLine, 0.0 ) && context.renderContext().geometry()
1267  {
1269  const QgsMapToPixel &mtp = context.renderContext().mapToPixel();
1270 
1271  QgsVertexId vId;
1272  QgsPoint vPoint;
1273  double x, y, z;
1274  QPointF mapPoint;
1275  int pointNum = 0;
1276  while ( context.renderContext().geometry()->nextVertex( vId, vPoint ) )
1277  {
1278  if ( context.renderContext().renderingStopped() )
1279  break;
1280 
1282 
1285  {
1286  //transform
1287  x = vPoint.x();
1288  y = vPoint.y();
1289  z = 0.0;
1290  if ( ct.isValid() )
1291  {
1292  ct.transformInPlace( x, y, z );
1293  }
1294  mapPoint.setX( x );
1295  mapPoint.setY( y );
1296  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1297  if ( rotateSymbols() )
1298  {
1299  double angle = context.renderContext().geometry()->vertexAngle( vId );
1300  setSymbolAngle( angle * 180 / M_PI );
1301  }
1302  renderSymbol( mapPoint, context.feature(), rc, -1, context.selected() );
1303  }
1304  }
1305 
1306  return;
1307  }
1308 
1309  switch ( placement )
1310  {
1311  case FirstVertex:
1312  {
1313  i = 0;
1314  maxCount = 1;
1315  break;
1316  }
1317 
1318  case LastVertex:
1319  {
1320  i = points.count() - 1;
1321  maxCount = points.count();
1322  break;
1323  }
1324 
1325  case Vertex:
1326  case SegmentCenter:
1327  {
1328  i = placement == Vertex ? 0 : 1;
1329  maxCount = points.count();
1330  if ( points.first() == points.last() )
1331  isRing = true;
1332  break;
1333  }
1334 
1335  case Interval:
1336  case CentralPoint:
1337  case CurvePoint:
1338  {
1339  return;
1340  }
1341  }
1342 
1343  if ( offsetAlongLine > 0 && ( placement == QgsTemplatedLineSymbolLayerBase::FirstVertex || placement == QgsTemplatedLineSymbolLayerBase::LastVertex ) )
1344  {
1345  double distance;
1347  renderOffsetVertexAlongLine( points, i, distance, context );
1348  // restore original rotation
1349  setSymbolAngle( origAngle );
1350 
1351  return;
1352  }
1353 
1354  int pointNum = 0;
1355  QPointF prevPoint;
1356  if ( placement == SegmentCenter && !points.empty() )
1357  prevPoint = points.at( 0 );
1358 
1359  QPointF symbolPoint;
1360  for ( ; i < maxCount; ++i )
1361  {
1363 
1364  if ( isRing && placement == QgsTemplatedLineSymbolLayerBase::Vertex && i == points.count() - 1 )
1365  {
1366  continue; // don't draw the last marker - it has been drawn already
1367  }
1368 
1369  if ( placement == SegmentCenter )
1370  {
1371  QPointF currentPoint = points.at( i );
1372  symbolPoint = QPointF( 0.5 * ( currentPoint.x() + prevPoint.x() ),
1373  0.5 * ( currentPoint.y() + prevPoint.y() ) );
1374  if ( rotateSymbols() )
1375  {
1376  double angle = std::atan2( currentPoint.y() - prevPoint.y(),
1377  currentPoint.x() - prevPoint.x() );
1378  setSymbolAngle( origAngle + angle * 180 / M_PI );
1379  }
1380  prevPoint = currentPoint;
1381  }
1382  else
1383  {
1384  symbolPoint = points.at( i );
1385  // rotate marker (if desired)
1386  if ( rotateSymbols() )
1387  {
1388  double angle = markerAngle( points, isRing, i );
1389  setSymbolAngle( origAngle + angle * 180 / M_PI );
1390  }
1391  }
1392 
1393  renderSymbol( symbolPoint, context.feature(), rc, -1, context.selected() );
1394  }
1395 
1396  // restore original rotation
1397  setSymbolAngle( origAngle );
1398 }
1399 
1400 double QgsTemplatedLineSymbolLayerBase::markerAngle( const QPolygonF &points, bool isRing, int vertex )
1401 {
1402  double angle = 0;
1403  const QPointF &pt = points[vertex];
1404 
1405  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1406  {
1407  int prevIndex = vertex - 1;
1408  int nextIndex = vertex + 1;
1409 
1410  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1411  {
1412  prevIndex = points.count() - 2;
1413  nextIndex = 1;
1414  }
1415 
1416  QPointF prevPoint, nextPoint;
1417  while ( prevIndex >= 0 )
1418  {
1419  prevPoint = points[ prevIndex ];
1420  if ( prevPoint != pt )
1421  {
1422  break;
1423  }
1424  --prevIndex;
1425  }
1426 
1427  while ( nextIndex < points.count() )
1428  {
1429  nextPoint = points[ nextIndex ];
1430  if ( nextPoint != pt )
1431  {
1432  break;
1433  }
1434  ++nextIndex;
1435  }
1436 
1437  if ( prevIndex >= 0 && nextIndex < points.count() )
1438  {
1439  angle = _averageAngle( prevPoint, pt, nextPoint );
1440  }
1441  }
1442  else //no ring and vertex is at start / at end
1443  {
1444  if ( vertex == 0 )
1445  {
1446  while ( vertex < points.size() - 1 )
1447  {
1448  const QPointF &nextPt = points[vertex + 1];
1449  if ( pt != nextPt )
1450  {
1451  angle = MyLine( pt, nextPt ).angle();
1452  return angle;
1453  }
1454  ++vertex;
1455  }
1456  }
1457  else
1458  {
1459  // use last segment's angle
1460  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1461  {
1462  const QPointF &prevPt = points[vertex - 1];
1463  if ( pt != prevPt )
1464  {
1465  angle = MyLine( prevPt, pt ).angle();
1466  return angle;
1467  }
1468  --vertex;
1469  }
1470  }
1471  }
1472  return angle;
1473 }
1474 
1475 void QgsTemplatedLineSymbolLayerBase::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolRenderContext &context )
1476 {
1477  if ( points.isEmpty() )
1478  return;
1479 
1480  QgsRenderContext &rc = context.renderContext();
1481  double origAngle = symbolAngle();
1482  if ( qgsDoubleNear( distance, 0.0 ) )
1483  {
1484  // rotate marker (if desired)
1485  if ( rotateSymbols() )
1486  {
1487  bool isRing = false;
1488  if ( points.first() == points.last() )
1489  isRing = true;
1490  double angle = markerAngle( points, isRing, vertex );
1491  setSymbolAngle( origAngle + angle * 180 / M_PI );
1492  }
1493  renderSymbol( points[vertex], context.feature(), rc, -1, context.selected() );
1494  return;
1495  }
1496 
1497  int pointIncrement = distance > 0 ? 1 : -1;
1498  QPointF previousPoint = points[vertex];
1499  int startPoint = distance > 0 ? std::min( vertex + 1, points.count() - 1 ) : std::max( vertex - 1, 0 );
1500  int endPoint = distance > 0 ? points.count() - 1 : 0;
1501  double distanceLeft = std::fabs( distance );
1502 
1503  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1504  {
1505  const QPointF &pt = points[i];
1506 
1507  if ( previousPoint == pt ) // must not be equal!
1508  continue;
1509 
1510  // create line segment
1511  MyLine l( previousPoint, pt );
1512 
1513  if ( distanceLeft < l.length() )
1514  {
1515  //destination point is in current segment
1516  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1517  // rotate marker (if desired)
1518  if ( rotateSymbols() )
1519  {
1520  setSymbolAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1521  }
1522  renderSymbol( markerPoint, context.feature(), rc, -1, context.selected() );
1523  return;
1524  }
1525 
1526  distanceLeft -= l.length();
1527  previousPoint = pt;
1528  }
1529 
1530  //didn't find point
1531 }
1532 
1533 void QgsTemplatedLineSymbolLayerBase::collectOffsetPoints( const QVector<QPointF> &p, QVector<QPointF> &dest, double intervalPainterUnits, double initialOffset, double initialLag, int numberPointsRequired )
1534 {
1535  if ( p.empty() )
1536  return;
1537 
1538  QVector< QPointF > points = p;
1539  const bool closedRing = points.first() == points.last();
1540 
1541  double lengthLeft = initialOffset;
1542 
1543  double initialLagLeft = initialLag > 0 ? -initialLag : 1; // an initialLagLeft of > 0 signifies end of lagging start points
1544  if ( initialLagLeft < 0 && closedRing )
1545  {
1546  // tracking back around the ring from the first point, insert pseudo vertices before the first vertex
1547  QPointF lastPt = points.constLast();
1548  QVector< QPointF > pseudoPoints;
1549  for ( int i = points.count() - 2; i > 0; --i )
1550  {
1551  if ( initialLagLeft >= 0 )
1552  {
1553  break;
1554  }
1555 
1556  const QPointF &pt = points[i];
1557 
1558  if ( lastPt == pt ) // must not be equal!
1559  continue;
1560 
1561  MyLine l( lastPt, pt );
1562  initialLagLeft += l.length();
1563  lastPt = pt;
1564 
1565  pseudoPoints << pt;
1566  }
1567  std::reverse( pseudoPoints.begin(), pseudoPoints.end() );
1568 
1569  points = pseudoPoints;
1570  points.append( p );
1571  }
1572  else
1573  {
1574  while ( initialLagLeft < 0 )
1575  {
1576  dest << points.constFirst();
1577  initialLagLeft += intervalPainterUnits;
1578  }
1579  }
1580  if ( initialLag > 0 )
1581  {
1582  lengthLeft += intervalPainterUnits - initialLagLeft;
1583  }
1584 
1585  QPointF lastPt = points[0];
1586  for ( int i = 1; i < points.count(); ++i )
1587  {
1588  const QPointF &pt = points[i];
1589 
1590  if ( lastPt == pt ) // must not be equal!
1591  {
1592  if ( closedRing && i == points.count() - 1 && numberPointsRequired > 0 && dest.size() < numberPointsRequired )
1593  {
1594  lastPt = points[0];
1595  i = 0;
1596  }
1597  continue;
1598  }
1599 
1600  // for each line, find out dx and dy, and length
1601  MyLine l( lastPt, pt );
1602  QPointF diff = l.diffForInterval( intervalPainterUnits );
1603 
1604  // if there's some length left from previous line
1605  // use only the rest for the first point in new line segment
1606  double c = 1 - lengthLeft / intervalPainterUnits;
1607 
1608  lengthLeft += l.length();
1609 
1610 
1611  while ( lengthLeft > intervalPainterUnits || qgsDoubleNear( lengthLeft, intervalPainterUnits, 0.000000001 ) )
1612  {
1613  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
1614  lastPt += c * diff;
1615  lengthLeft -= intervalPainterUnits;
1616  dest << lastPt;
1617  c = 1; // reset c (if wasn't 1 already)
1618  if ( numberPointsRequired > 0 && dest.size() >= numberPointsRequired )
1619  break;
1620  }
1621  lastPt = pt;
1622 
1623  if ( numberPointsRequired > 0 && dest.size() >= numberPointsRequired )
1624  break;
1625 
1626  // if a closed ring, we keep looping around the ring until we hit the required number of points
1627  if ( closedRing && i == points.count() - 1 && numberPointsRequired > 0 && dest.size() < numberPointsRequired )
1628  {
1629  lastPt = points[0];
1630  i = 0;
1631  }
1632  }
1633 
1634  if ( !closedRing && numberPointsRequired > 0 && dest.size() < numberPointsRequired )
1635  {
1636  // pad with repeating last point to match desired size
1637  while ( dest.size() < numberPointsRequired )
1638  dest << points.constLast();
1639  }
1640 }
1641 
1642 void QgsTemplatedLineSymbolLayerBase::renderPolylineCentral( const QPolygonF &points, QgsSymbolRenderContext &context, double averageAngleOver )
1643 {
1644  if ( !points.isEmpty() )
1645  {
1646  // calc length
1647  qreal length = 0;
1648  QPolygonF::const_iterator it = points.constBegin();
1649  QPointF last = *it;
1650  for ( ++it; it != points.constEnd(); ++it )
1651  {
1652  length += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
1653  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1654  last = *it;
1655  }
1656 
1657  const double midPoint = length / 2;
1658 
1659  QPointF pt;
1660  double thisSymbolAngle = 0;
1661 
1662  if ( averageAngleOver > 0 && !qgsDoubleNear( averageAngleOver, 0.0 ) )
1663  {
1664  QVector< QPointF > angleStartPoints;
1665  QVector< QPointF > symbolPoints;
1666  QVector< QPointF > angleEndPoints;
1667  // collectOffsetPoints will have the first point in the line as the first result -- we don't want this, we need the second
1668  collectOffsetPoints( points, symbolPoints, midPoint, midPoint, 0.0, 2 );
1669  collectOffsetPoints( points, angleStartPoints, midPoint, 0, averageAngleOver, 2 );
1670  collectOffsetPoints( points, angleEndPoints, midPoint, midPoint - averageAngleOver, 0, 2 );
1671 
1672  pt = symbolPoints.at( 1 );
1673  MyLine l( angleStartPoints.at( 1 ), angleEndPoints.at( 1 ) );
1674  thisSymbolAngle = l.angle();
1675  }
1676  else
1677  {
1678  // find the segment where the central point lies
1679  it = points.constBegin();
1680  last = *it;
1681  qreal last_at = 0, next_at = 0;
1682  QPointF next;
1683  int segment = 0;
1684  for ( ++it; it != points.constEnd(); ++it )
1685  {
1686  next = *it;
1687  next_at += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
1688  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1689  if ( next_at >= midPoint )
1690  break; // we have reached the center
1691  last = *it;
1692  last_at = next_at;
1693  segment++;
1694  }
1695 
1696  // find out the central point on segment
1697  MyLine l( last, next ); // for line angle
1698  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1699  pt = last + ( next - last ) * k;
1700  thisSymbolAngle = l.angle();
1701  }
1702 
1703  // draw the marker
1704  double origAngle = symbolAngle();
1705  if ( rotateSymbols() )
1706  setSymbolAngle( origAngle + thisSymbolAngle * 180 / M_PI );
1707  renderSymbol( pt, context.feature(), context.renderContext(), -1, context.selected() );
1708  if ( rotateSymbols() )
1709  setSymbolAngle( origAngle );
1710  }
1711 }
1712 
1714 {
1715  return mMarker.get();
1716 }
1717 
1719 {
1720  if ( !symbol || symbol->type() != QgsSymbol::Marker )
1721  {
1722  delete symbol;
1723  return false;
1724  }
1725 
1726  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
1727  mColor = mMarker->color();
1728  return true;
1729 }
1730 
1731 
1732 
1733 //
1734 // QgsMarkerLineSymbolLayer
1735 //
1736 
1738  : QgsTemplatedLineSymbolLayerBase( rotateMarker, interval )
1739 {
1740  setSubSymbol( new QgsMarkerSymbol() );
1741 }
1742 
1744 {
1745  bool rotate = DEFAULT_MARKERLINE_ROTATE;
1747 
1748  if ( props.contains( QStringLiteral( "interval" ) ) )
1749  interval = props[QStringLiteral( "interval" )].toDouble();
1750  if ( props.contains( QStringLiteral( "rotate" ) ) )
1751  rotate = ( props[QStringLiteral( "rotate" )] == QLatin1String( "1" ) );
1752 
1753  std::unique_ptr< QgsMarkerLineSymbolLayer > x = qgis::make_unique< QgsMarkerLineSymbolLayer >( rotate, interval );
1754  setCommonProperties( x.get(), props );
1755  return x.release();
1756 }
1757 
1759 {
1760  return QStringLiteral( "MarkerLine" );
1761 }
1762 
1764 {
1765  mMarker->setColor( color );
1766  mColor = color;
1767 }
1768 
1770 {
1771  return mMarker ? mMarker->color() : mColor;
1772 }
1773 
1775 {
1776  mMarker->setOpacity( context.opacity() );
1777 
1778  // if being rotated, it gets initialized with every line segment
1779  QgsSymbol::RenderHints hints = nullptr;
1780  if ( rotateSymbols() )
1781  hints |= QgsSymbol::DynamicRotation;
1782  mMarker->setRenderHints( hints );
1783 
1784  mMarker->startRender( context.renderContext(), context.fields() );
1785 }
1786 
1788 {
1789  mMarker->stopRender( context.renderContext() );
1790 }
1791 
1792 
1794 {
1795  std::unique_ptr< QgsMarkerLineSymbolLayer > x = qgis::make_unique< QgsMarkerLineSymbolLayer >( rotateSymbols(), interval() );
1796  copyTemplateSymbolProperties( x.get() );
1797  return x.release();
1798 }
1799 
1800 void QgsMarkerLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
1801 {
1802  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1803  {
1804  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:LineSymbolizer" ) );
1805  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
1806  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
1807  element.appendChild( symbolizerElem );
1808 
1809  // <Geometry>
1810  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
1811 
1812  QString gap;
1813  switch ( placement() )
1814  {
1816  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "firstPoint" ) ) );
1817  break;
1819  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "lastPoint" ) ) );
1820  break;
1822  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "centralPoint" ) ) );
1823  break;
1825  // no way to get line/polygon's vertices, use a VendorOption
1826  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "points" ) ) );
1827  break;
1828  default:
1830  gap = qgsDoubleToString( interval );
1831  break;
1832  }
1833 
1834  if ( !rotateSymbols() )
1835  {
1836  // markers in LineSymbolizer must be drawn following the line orientation,
1837  // use a VendorOption when no marker rotation
1838  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "rotateMarker" ), QStringLiteral( "0" ) ) );
1839  }
1840 
1841  // <Stroke>
1842  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
1843  symbolizerElem.appendChild( strokeElem );
1844 
1845  // <GraphicStroke>
1846  QDomElement graphicStrokeElem = doc.createElement( QStringLiteral( "se:GraphicStroke" ) );
1847  strokeElem.appendChild( graphicStrokeElem );
1848 
1849  QgsSymbolLayer *layer = mMarker->symbolLayer( i );
1850  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
1851  if ( !markerLayer )
1852  {
1853  graphicStrokeElem.appendChild( doc.createComment( QStringLiteral( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() ) ) );
1854  }
1855  else
1856  {
1857  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1858  }
1859 
1860  if ( !gap.isEmpty() )
1861  {
1862  QDomElement gapElem = doc.createElement( QStringLiteral( "se:Gap" ) );
1863  QgsSymbolLayerUtils::createExpressionElement( doc, gapElem, gap );
1864  graphicStrokeElem.appendChild( gapElem );
1865  }
1866 
1867  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1868  {
1869  QDomElement perpOffsetElem = doc.createElement( QStringLiteral( "se:PerpendicularOffset" ) );
1871  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
1872  symbolizerElem.appendChild( perpOffsetElem );
1873  }
1874  }
1875 }
1876 
1878 {
1879  QgsDebugMsg( QStringLiteral( "Entered." ) );
1880 
1881  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
1882  if ( strokeElem.isNull() )
1883  return nullptr;
1884 
1885  QDomElement graphicStrokeElem = strokeElem.firstChildElement( QStringLiteral( "GraphicStroke" ) );
1886  if ( graphicStrokeElem.isNull() )
1887  return nullptr;
1888 
1889  // retrieve vendor options
1890  bool rotateMarker = true;
1892 
1893  QgsStringMap vendorOptions = QgsSymbolLayerUtils::getVendorOptionList( element );
1894  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1895  {
1896  if ( it.key() == QLatin1String( "placement" ) )
1897  {
1898  if ( it.value() == QLatin1String( "points" ) )
1900  else if ( it.value() == QLatin1String( "firstPoint" ) )
1902  else if ( it.value() == QLatin1String( "lastPoint" ) )
1904  else if ( it.value() == QLatin1String( "centralPoint" ) )
1906  }
1907  else if ( it.value() == QLatin1String( "rotateMarker" ) )
1908  {
1909  rotateMarker = it.value() == QLatin1String( "0" );
1910  }
1911  }
1912 
1913  std::unique_ptr< QgsMarkerSymbol > marker;
1914 
1916  if ( l )
1917  {
1918  QgsSymbolLayerList layers;
1919  layers.append( l );
1920  marker.reset( new QgsMarkerSymbol( layers ) );
1921  }
1922 
1923  if ( !marker )
1924  return nullptr;
1925 
1926  double interval = 0.0;
1927  QDomElement gapElem = graphicStrokeElem.firstChildElement( QStringLiteral( "Gap" ) );
1928  if ( !gapElem.isNull() )
1929  {
1930  bool ok;
1931  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1932  if ( ok )
1933  interval = d;
1934  }
1935 
1936  double offset = 0.0;
1937  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( QStringLiteral( "PerpendicularOffset" ) );
1938  if ( !perpOffsetElem.isNull() )
1939  {
1940  bool ok;
1941  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1942  if ( ok )
1943  offset = d;
1944  }
1945 
1946  QString uom = element.attribute( QStringLiteral( "uom" ) );
1947  interval = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, interval );
1948  offset = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset );
1949 
1950  QgsMarkerLineSymbolLayer *x = new QgsMarkerLineSymbolLayer( rotateMarker );
1951  x->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1952  x->setPlacement( placement );
1953  x->setInterval( interval );
1954  x->setSubSymbol( marker.release() );
1955  x->setOffset( offset );
1956  return x;
1957 }
1958 
1960 {
1961  mMarker->setSize( width );
1962 }
1963 
1965 {
1966  if ( key == QgsSymbolLayer::PropertyWidth && mMarker && property )
1967  {
1968  mMarker->setDataDefinedSize( property );
1969  }
1971 }
1972 
1974 {
1975  mMarker->setLineAngle( angle );
1976 }
1977 
1979 {
1980  return mMarker->angle();
1981 }
1982 
1984 {
1985  mMarker->setAngle( angle );
1986 }
1987 
1988 void QgsMarkerLineSymbolLayer::renderSymbol( const QPointF &point, const QgsFeature *feature, QgsRenderContext &context, int layer, bool selected )
1989 {
1990  mMarker->renderPoint( point, feature, context, layer, selected );
1991 }
1992 
1994 {
1995  return mMarker->size();
1996 }
1997 
1999 {
2000  return mMarker->size( context );
2001 }
2002 
2004 {
2006  mMarker->setOutputUnit( unit );
2007  setIntervalUnit( unit );
2008  mOffsetUnit = unit;
2009  setOffsetAlongLineUnit( unit );
2010 }
2011 
2012 
2013 QSet<QString> QgsMarkerLineSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
2014 {
2015  QSet<QString> attr = QgsLineSymbolLayer::usedAttributes( context );
2016  if ( mMarker )
2017  attr.unite( mMarker->usedAttributes( context ) );
2018  return attr;
2019 }
2020 
2022 {
2024  return true;
2025  if ( mMarker && mMarker->hasDataDefinedProperties() )
2026  return true;
2027  return false;
2028 }
2029 
2031 {
2032  return ( mMarker->size( context ) / 2.0 ) +
2034 }
2035 
2036 
2037 //
2038 // QgsHashedLineSymbolLayer
2039 //
2040 
2042  : QgsTemplatedLineSymbolLayerBase( rotateSymbol, interval )
2043 {
2044  setSubSymbol( new QgsLineSymbol() );
2045 }
2046 
2048 {
2049  bool rotate = DEFAULT_MARKERLINE_ROTATE;
2051 
2052  if ( props.contains( QStringLiteral( "interval" ) ) )
2053  interval = props[QStringLiteral( "interval" )].toDouble();
2054  if ( props.contains( QStringLiteral( "rotate" ) ) )
2055  rotate = ( props[QStringLiteral( "rotate" )] == QLatin1String( "1" ) );
2056 
2057  std::unique_ptr< QgsHashedLineSymbolLayer > x = qgis::make_unique< QgsHashedLineSymbolLayer >( rotate, interval );
2058  setCommonProperties( x.get(), props );
2059  if ( props.contains( QStringLiteral( "hash_angle" ) ) )
2060  {
2061  x->setHashAngle( props[QStringLiteral( "hash_angle" )].toDouble() );
2062  }
2063 
2064  if ( props.contains( QStringLiteral( "hash_length" ) ) )
2065  x->setHashLength( props[QStringLiteral( "hash_length" )].toDouble() );
2066 
2067  if ( props.contains( QStringLiteral( "hash_length_unit" ) ) )
2068  x->setHashLengthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "hash_length_unit" )] ) );
2069 
2070  if ( props.contains( QStringLiteral( "hash_length_map_unit_scale" ) ) )
2071  x->setHashLengthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "hash_length_map_unit_scale" )] ) );
2072 
2073  return x.release();
2074 }
2075 
2077 {
2078  return QStringLiteral( "HashLine" );
2079 }
2080 
2082 {
2083  mHashSymbol->setOpacity( context.opacity() );
2084 
2085  // if being rotated, it gets initialized with every line segment
2086  QgsSymbol::RenderHints hints = nullptr;
2087  if ( rotateSymbols() )
2088  hints |= QgsSymbol::DynamicRotation;
2089  mHashSymbol->setRenderHints( hints );
2090 
2091  mHashSymbol->startRender( context.renderContext(), context.fields() );
2092 }
2093 
2095 {
2096  mHashSymbol->stopRender( context.renderContext() );
2097 }
2098 
2100 {
2102  map[ QStringLiteral( "hash_angle" ) ] = QString::number( mHashAngle );
2103 
2104  map[QStringLiteral( "hash_length" )] = QString::number( mHashLength );
2105  map[QStringLiteral( "hash_length_unit" )] = QgsUnitTypes::encodeUnit( mHashLengthUnit );
2106  map[QStringLiteral( "hash_length_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mHashLengthMapUnitScale );
2107 
2108  return map;
2109 }
2110 
2112 {
2113  std::unique_ptr< QgsHashedLineSymbolLayer > x = qgis::make_unique< QgsHashedLineSymbolLayer >( rotateSymbols(), interval() );
2114  copyTemplateSymbolProperties( x.get() );
2115  x->setHashAngle( mHashAngle );
2116  x->setHashLength( mHashLength );
2117  x->setHashLengthUnit( mHashLengthUnit );
2118  x->setHashLengthMapUnitScale( mHashLengthMapUnitScale );
2119  return x.release();
2120 }
2121 
2123 {
2124  mHashSymbol->setColor( color );
2125  mColor = color;
2126 }
2127 
2129 {
2130  return mHashSymbol ? mHashSymbol->color() : mColor;
2131 }
2132 
2134 {
2135  return mHashSymbol.get();
2136 }
2137 
2139 {
2140  if ( !symbol || symbol->type() != QgsSymbol::Line )
2141  {
2142  delete symbol;
2143  return false;
2144  }
2145 
2146  mHashSymbol.reset( static_cast<QgsLineSymbol *>( symbol ) );
2147  mColor = mHashSymbol->color();
2148  return true;
2149 }
2150 
2152 {
2153  mHashLength = width;
2154 }
2155 
2157 {
2158  return mHashLength;
2159 }
2160 
2162 {
2163  return context.convertToPainterUnits( mHashLength, mHashLengthUnit, mHashLengthMapUnitScale );
2164 }
2165 
2167 {
2168  return ( mHashSymbol->width( context ) / 2.0 )
2169  + context.convertToPainterUnits( mHashLength, mHashLengthUnit, mHashLengthMapUnitScale )
2170  + context.convertToPainterUnits( std::fabs( mOffset ), mOffsetUnit, mOffsetMapUnitScale );
2171 }
2172 
2174 {
2176  mHashSymbol->setOutputUnit( unit );
2177  setIntervalUnit( unit );
2178  mOffsetUnit = unit;
2179  setOffsetAlongLineUnit( unit );
2180 }
2181 
2182 QSet<QString> QgsHashedLineSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
2183 {
2184  QSet<QString> attr = QgsLineSymbolLayer::usedAttributes( context );
2185  if ( mHashSymbol )
2186  attr.unite( mHashSymbol->usedAttributes( context ) );
2187  return attr;
2188 }
2189 
2191 {
2193  return true;
2194  if ( mHashSymbol && mHashSymbol->hasDataDefinedProperties() )
2195  return true;
2196  return false;
2197 }
2198 
2200 {
2201  if ( key == QgsSymbolLayer::PropertyWidth && mHashSymbol && property )
2202  {
2203  mHashSymbol->setDataDefinedWidth( property );
2204  }
2206 }
2207 
2209 {
2210  mSymbolLineAngle = angle;
2211 }
2212 
2214 {
2215  return mSymbolAngle;
2216 }
2217 
2219 {
2220  mSymbolAngle = angle;
2221 }
2222 
2223 void QgsHashedLineSymbolLayer::renderSymbol( const QPointF &point, const QgsFeature *feature, QgsRenderContext &context, int layer, bool selected )
2224 {
2225  double lineLength = mHashLength;
2227  {
2228  context.expressionContext().setOriginalValueVariable( mHashLength );
2230  }
2231  const double w = context.convertToPainterUnits( lineLength, mHashLengthUnit, mHashLengthMapUnitScale ) / 2.0;
2232 
2233  double hashAngle = mHashAngle;
2235  {
2236  context.expressionContext().setOriginalValueVariable( mHashAngle );
2238  }
2239 
2240  QgsPointXY center( point );
2241  QgsPointXY start = center.project( w, 180 - ( mSymbolAngle + mSymbolLineAngle + hashAngle ) );
2242  QgsPointXY end = center.project( -w, 180 - ( mSymbolAngle + mSymbolLineAngle + hashAngle ) );
2243 
2244  QPolygonF points;
2245  points << QPointF( start.x(), start.y() ) << QPointF( end.x(), end.y() );
2246 
2247  mHashSymbol->renderPolyline( points, feature, context, layer, selected );
2248 }
2249 
2251 {
2252  return mHashAngle;
2253 }
2254 
2256 {
2257  mHashAngle = angle;
2258 }
2259 
2260 
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setOffsetAlongLine(double offsetAlongLine)
Sets the the offset along the line for the symbol placement.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color...
Place symbols on the first vertex in the line.
Single variable definition for use within a QgsExpressionContextScope.
void setMapUnitScale(const QgsMapUnitScale &scale) override
double symbolAngle() const override
Returns the symbol&#39;s current angle, in degrees clockwise.
double dxfOffset(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets offset.
double y
Definition: qgspoint.h:42
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:154
QgsHashedLineSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setMapUnitScale(const QgsMapUnitScale &scale) override
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
void renderPolygonStroke(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
Renders the line symbol layer along the outline of polygon, using the given render context...
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
void setPlacement(Placement placement)
Sets the placement of the symbols.
const QgsVectorSimplifyMethod & vectorSimplifyMethod() const
Returns the simplification settings to use when rendering vector layers.
QgsFields fields() const
Fields of the layer.
Definition: qgssymbol.h:774
static const QString EXPR_GEOMETRY_POINT_COUNT
Inbuilt variable name for point count variable.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double...
void setUseCustomDashPattern(bool b)
Sets whether the line uses a custom dash pattern.
virtual QgsSymbol * subSymbol()
Returns the symbol&#39;s sub symbol, if present.
void renderPolyline(const QPolygonF &points, QgsSymbolRenderContext &context) FINAL
Renders the line symbol layer along the line joining points, using the given render context...
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
double y
Definition: qgspointxy.h:48
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSimpleLineSymbolLayer from an SLD XML DOM element.
A class to represent a 2D point.
Definition: qgspointxy.h:43
void setSymbolLineAngle(double angle) override
Sets the line angle modification for the symbol&#39;s angle.
A simple line symbol layer, which renders lines using a line in a variety of styles (e...
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:280
Render both exterior and interior rings.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
virtual void setSymbolLineAngle(double angle)=0
Sets the line angle modification for the symbol&#39;s angle.
void renderSymbol(const QPointF &point, const QgsFeature *feature, QgsRenderContext &context, int layer=-1, bool selected=false) override
Renders the templated symbol at the specified point, using the given render context.
#define DEFAULT_MARKERLINE_INTERVAL
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
Curve polygon geometry type.
QgsUnitTypes::RenderUnit mOffsetUnit
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
Mixed or unknown units.
Definition: qgsunittypes.h:153
void restoreOldDataDefinedProperties(const QgsStringMap &stringMap)
Restores older data defined properties from string map.
static double sizeInPixelsFromSldUom(const QString &uom, double size)
Returns the size scaled in pixels according to the uom attribute.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsHashedLineSymbolLayer, using the settings serialized in the properties map (correspo...
void copyTemplateSymbolProperties(QgsTemplatedLineSymbolLayerBase *destLayer) const
Copies all common properties of this layer to another templated symbol layer.
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units for the line&#39;s offset.
Line symbol.
Definition: qgssymbol.h:86
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
virtual bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const =0
Returns next vertex id and coordinates.
Place symbols on every vertex in the line.
QgsUnitTypes::RenderUnit intervalUnit() const
Returns the units for the interval between symbols.
QgsHashedLineSymbolLayer(bool rotateSymbol=true, double interval=3)
Constructor for QgsHashedLineSymbolLayer.
double interval() const
Returns the interval between individual symbols.
static bool createExpressionElement(QDomDocument &doc, QDomElement &element, const QString &function)
Creates a OGC Expression element based on the provided function expression.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:612
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition: qgssymbol.h:104
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
void setInterval(double interval)
Sets the interval between individual symbols.
virtual double width() const
Returns the estimated width for the line symbol layer.
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:860
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
void setWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the line&#39;s width.
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgssymbol.h:1060
void setWidth(double width) override
Sets the width of the line symbol layer.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
Place symbols at every virtual curve point in the line (used when rendering curved geometry types onl...
static QVector< qreal > decodeRealVector(const QString &s)
void setCustomDashPatternUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit for lengths used in the custom dash pattern.
static QString encodeColor(const QColor &color)
double symbolAngle() const override
Returns the symbol&#39;s current angle, in degrees clockwise.
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
Place symbols at the center of every line segment.
void setAverageAngleUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit for the length over which the line&#39;s direction is averaged when calculating individual ...
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
std::unique_ptr< QgsMarkerSymbol > mMarker
void transformInPlace(double &x, double &y) const
Transform device coordinates to map coordinates.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection...
QString layerType() const override
Returns a string that represents this layer type.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
Definition: qgssymbol.cpp:1192
static QString encodePenStyle(Qt::PenStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Writes the symbol layer definition as a SLD XML element.
static double rescaleUom(double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap &props)
Rescales the given size based on the uomScale found in the props, if any is found, otherwise returns the value un-modified.
virtual bool setSubSymbol(QgsSymbol *symbol)
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
void setOffsetAlongLineMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale used for calculating the offset in map units along line for symbols...
QgsSymbol * subSymbol() override
Returns the symbol&#39;s sub symbol, if present.
void setIntervalUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the interval between symbols.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsMarkerLineSymbolLayer, using the settings serialized in the properties map (correspo...
QgsUnitTypes::RenderUnit widthUnit() const
Returns the units for the line&#39;s width.
Utility class for identifying a unique vertex within a geometry.
void setGeometry(const QgsAbstractGeometry *geometry)
Sets pointer to original (unsegmentized) geometry.
The geometries can be rendered with &#39;AntiAliasing&#39; disabled because of it is &#39;1-pixel size&#39;...
QVector< qreal > customDashVector() const
Returns the custom dash vector, which is the pattern of alternating drawn/skipped lengths used while ...
static QgsStringMap getVendorOptionList(QDomElement &element)
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:711
static void setCommonProperties(QgsTemplatedLineSymbolLayerBase *destLayer, const QgsStringMap &properties)
Sets all common symbol properties in the destLayer, using the settings serialized in the properties m...
const QgsAbstractGeometry * geometry() const
Returns pointer to the unsegmentized geometry.
Render the interior rings only.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:240
QgsTemplatedLineSymbolLayerBase(bool rotateSymbol=true, double interval=3)
Constructor for QgsTemplatedLineSymbolLayerBase.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer&#39;s property collection, used for data defined overrides...
Line symbol layer type which draws repeating line sections along a line feature.
virtual QgsSymbolLayer * clone() const =0
Shall be reimplemented by subclasses to create a deep copy of the instance.
#define DEFAULT_SIMPLELINE_PENSTYLE
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
QgsMapUnitScale mOffsetMapUnitScale
virtual QColor color() const
The fill color.
static QgsSymbolLayer * createMarkerLayerFromSld(QDomElement &element)
QColor selectionColor() const
Returns the color to use when rendering selected features.
QgsMarkerLineSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void renderPolyline(const QPolygonF &points, QgsSymbolRenderContext &context) override
Renders the line symbol layer along the line joining points, using the given render context...
double width() const override
Returns the estimated width for the line symbol layer.
virtual double symbolAngle() const =0
Returns the symbol&#39;s current angle, in degrees clockwise.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
float threshold() const
Gets the simplification threshold of the vector layer managed.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:51
void stopRender(QgsSymbolRenderContext &context) override
#define DEFAULT_SIMPLELINE_COLOR
QgsUnitTypes::RenderUnit offsetAlongLineUnit() const
Returns the unit used for calculating the offset along line for symbols.
QgsSimpleLineSymbolLayer(const QColor &color=DEFAULT_SIMPLELINE_COLOR, double width=DEFAULT_SIMPLELINE_WIDTH, Qt::PenStyle penStyle=DEFAULT_SIMPLELINE_PENSTYLE)
Constructor for QgsSimpleLineSymbolLayer.
QgsPointXY project(double distance, double bearing) const
Returns a new point which corresponds to this point projected by a specified distance in a specified ...
Definition: qgspointxy.cpp:87
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
Qt::PenJoinStyle penJoinStyle() const
Returns the pen join style used to render the line (e.g.
QgsUnitTypes::RenderUnit outputUnit() const FINAL
Returns the units to use for sizes and widths within the symbol layer.
double offset() const
Returns the line&#39;s offset.
void stopRender(QgsSymbolRenderContext &context) override
void setDataDefinedProperty(QgsSymbolLayer::Property key, const QgsProperty &property) override
Sets a data defined property for the layer.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
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)
Base class for templated line symbols, e.g.
Single scope for storing variables and functions for use within a QgsExpressionContext.
double mapUnitsPerPixel() const
Returns current map units per pixel.
Q_DECL_DEPRECATED bool rotateMarker() const
Shall the marker be rotated.
A store for object properties.
Definition: qgsproperty.h:229
QgsMapUnitScale mWidthMapUnitScale
QgsRenderContext & renderContext()
Returns a reference to the context&#39;s render context.
Definition: qgssymbol.h:668
void setHashAngle(double angle)
Sets the angle to use when drawing the hashed lines sections, in degrees clockwise.
void setSymbolAngle(double angle) override
Sets the symbol&#39;s angle, in degrees clockwise.
const QgsMapUnitScale & offsetAlongLineMapUnitScale() const
Returns the map unit scale used for calculating the offset in map units along line for symbols...
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
static Qt::PenStyle decodePenStyle(const QString &str)
QString layerType() const override
Returns a string that represents this layer type.
bool selected() const
Returns true if symbols should be rendered using the selected symbol coloring and style...
Definition: qgssymbol.h:724
void setMapUnitScale(const QgsMapUnitScale &scale) FINAL
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsSimpleLineSymbolLayer, using the settings serialized in the properties map (correspo...
double width() const override
Returns the estimated width for the line symbol layer.
double x
Definition: qgspointxy.h:47
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
Place symbols at regular intervals.
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
QgsWkbTypes::GeometryType originalGeometryType() const
Returns the geometry type for the original feature geometry being rendered.
Definition: qgssymbol.h:766
void startRender(QgsSymbolRenderContext &context) override
void setPenCapStyle(Qt::PenCapStyle style)
Sets the pen cap style used to render the line (e.g.
QgsExpressionContext & expressionContext()
Gets the expression context.
void transformInPlace(double &x, double &y, double &z, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets line width.
Line angle, or angle of hash lines for hash line symbols.
#define DEFAULT_SIMPLELINE_WIDTH
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Marker symbol.
Definition: qgssymbol.h:85
void stopRender(QgsSymbolRenderContext &context) override
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
Stroke style (eg solid, dashed)
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
double hashAngle() const
Returns the angle to use when drawing the hashed lines sections, in degrees clockwise.
Contains information about the context of a rendering operation.
Abstract base class for marker symbol layers.
QColor color() const override
The fill color.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
void startRender(QgsSymbolRenderContext &context) override
QgsSimpleLineSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
const QgsMapUnitScale & intervalMapUnitScale() const
Returns the map unit scale for the interval between symbols.
QPainter * painter()
Returns the destination QPainter for the render operation.
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
void setWidth(double width) override
Sets the width of the line symbol layer.
Line symbol layer type which draws repeating marker symbols along a line feature. ...
RenderRingFilter mRingFilter
void startRender(QgsSymbolRenderContext &context) override
SymbolType type() const
Returns the symbol&#39;s type.
Definition: qgssymbol.h:120
Struct for storing maximum and minimum scales for measurements in map units.
void setCustomDashPatternMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for lengths used in the custom dash pattern.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
Placement
Defines how/where the templated symbol should be placed on the line.
void setWidthMapUnitScale(const QgsMapUnitScale &scale)
void setIntervalMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the interval between symbols.
Qt::PenStyle penStyle() const
Returns the pen style used to render the line (e.g.
virtual void setSymbolAngle(double angle)=0
Sets the symbol&#39;s angle, in degrees clockwise.
QgsUnitTypes::RenderUnit mWidthUnit
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
const QgsFeature * feature() const
Returns the current feature being rendered.
Definition: qgssymbol.h:749
Distance between lines, or length of lines for hash line symbols.
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit for the line&#39;s offset.
void setRingFilter(QgsLineSymbolLayer::RenderRingFilter filter)
Sets the line symbol layer&#39;s ring filter, which controls which rings are rendered when the line symbo...
void setColor(const QColor &color) override
The fill color.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
SimplifyHints simplifyHints() const
Gets the simplification hints of the vector layer managed.
Class for doing transforms between two map coordinate systems.
void setDrawInsidePolygon(bool drawInsidePolygon)
Sets whether the line should only be drawn inside polygons, and any portion of the line which falls o...
QgsMarkerLineSymbolLayer(bool rotateMarker=DEFAULT_MARKERLINE_ROTATE, double interval=DEFAULT_MARKERLINE_INTERVAL)
Constructor for QgsMarkerLineSymbolLayer.
void setCustomDashVector(const QVector< qreal > &vector)
Sets the custom dash vector, which is the pattern of alternating drawn/skipped lengths used while ren...
void setDataDefinedProperty(QgsSymbolLayer::Property key, const QgsProperty &property) override
Sets a data defined property for the layer.
QgsMapUnitScale mapUnitScale() const FINAL
static const QString EXPR_GEOMETRY_POINT_NUM
Inbuilt variable name for point number variable.
QList< QPolygonF > offsetLine(QPolygonF polyline, double dist, QgsWkbTypes::GeometryType geometryType)
calculate geometry shifted by a specified distance
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
Placement placement() const
Returns the placement of the symbols.
Place symbols on the last vertex in the line.
Length to average symbol angles over.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
void setOffsetAlongLineUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit used for calculating the offset along line for symbols.
Render the exterior ring only.
static void lineToSld(QDomDocument &doc, QDomElement &element, Qt::PenStyle penStyle, const QColor &color, double width=-1, const Qt::PenJoinStyle *penJoinStyle=nullptr, const Qt::PenCapStyle *penCapStyle=nullptr, const QVector< qreal > *customDashPattern=nullptr, double dashOffset=0.0)
void setAverageAngleLength(double length)
Sets the length of line over which the line&#39;s direction is averaged when calculating individual symbo...
void setSymbolLineAngle(double angle) override
Sets the line angle modification for the symbol&#39;s angle.
static QString encodePenCapStyle(Qt::PenCapStyle style)
Place symbols at the mid point of the line.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsMarkerLineSymbolLayer from an SLD XML DOM element.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the line&#39;s offset.
const QgsCurve * exteriorRing() const
Returns the curve polygon&#39;s exterior ring.
QVector< qreal > dxfCustomDashPattern(QgsUnitTypes::RenderUnit &unit) const override
Gets dash pattern.
QgsMapUnitScale mapUnitScale() const override
void setAverageAngleMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the length over which the line&#39;s direction is averaged when calculating i...
void setColor(const QColor &color) override
The fill color.
static bool isGeneralizableByDeviceBoundingBox(const QgsRectangle &envelope, float mapToPixelTol=1.0f)
Returns whether the device-envelope can be replaced by its BBOX when is applied the specified toleran...
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Qt::PenCapStyle penCapStyle() const
Returns the pen cap style used to render the line (e.g.
void renderPolygonStroke(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) FINAL
Renders the line symbol layer along the outline of polygon, using the given render context...
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
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.
virtual bool hasCurvedSegments() const
Returns true if the geometry contains curved segments.
QColor dxfColor(QgsSymbolRenderContext &context) const override
Gets color.
RAII class to pop scope from an expression context on destruction.
Qt::PenStyle dxfPenStyle() const override
Gets pen style.
#define DEFAULT_MARKERLINE_ROTATE
double offsetAlongLine() const
Returns the offset along the line for the symbol placement.
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
virtual void renderSymbol(const QPointF &point, const QgsFeature *feature, QgsRenderContext &context, int layer=-1, bool selected=false)=0
Renders the templated symbol at the specified point, using the given render context.
void setSymbolAngle(double angle) override
Sets the symbol&#39;s angle, in degrees clockwise.
QgsPropertyCollection mDataDefinedProperties
QColor color() const override
The fill color.
QgsMapUnitScale mapUnitScale() const override
void renderSymbol(const QPointF &point, const QgsFeature *feature, QgsRenderContext &context, int layer=-1, bool selected=false) override
Renders the templated symbol at the specified point, using the given render context.
QgsSymbol * subSymbol() override
Returns the symbol&#39;s sub symbol, if present.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Property
Data definable properties.
static QString encodeRealVector(const QVector< qreal > &v)
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:145
static QColor decodeColor(const QString &str)
virtual void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the layer.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the pen join style used to render the line (e.g.
void setOffset(double offset)
Sets the line&#39;s offset.
virtual QString layerType() const =0
Returns a string that represents this layer type.
bool rotateSymbols() const
Returns true if the repeating symbols be rotated to match their line segment orientation.
double x
Definition: qgspoint.h:41