QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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"
28 
29 #include <QPainter>
30 #include <QDomDocument>
31 #include <QDomElement>
32 
33 #include <cmath>
34 
35 QgsSimpleLineSymbolLayer::QgsSimpleLineSymbolLayer( const QColor &color, double width, Qt::PenStyle penStyle )
36  : mPenStyle( penStyle )
37 {
38  mColor = color;
39  mWidth = width;
40  mCustomDashVector << 5 << 2;
41 }
42 
44 {
46  mWidthUnit = unit;
47  mOffsetUnit = unit;
48  mCustomDashPatternUnit = unit;
49 }
50 
52 {
54  if ( mWidthUnit != unit || mOffsetUnit != unit || mCustomDashPatternUnit != unit )
55  {
57  }
58  return unit;
59 }
60 
62 {
64  mWidthMapUnitScale = scale;
65  mOffsetMapUnitScale = scale;
66  mCustomDashPatternMapUnitScale = scale;
67 }
68 
70 {
73  mOffsetMapUnitScale == mCustomDashPatternMapUnitScale )
74  {
75  return mWidthMapUnitScale;
76  }
77  return QgsMapUnitScale();
78 }
79 
81 {
85 
86  if ( props.contains( QStringLiteral( "line_color" ) ) )
87  {
88  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] );
89  }
90  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
91  {
92  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] );
93  }
94  else if ( props.contains( QStringLiteral( "color" ) ) )
95  {
96  //pre 2.5 projects used "color"
97  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
98  }
99  if ( props.contains( QStringLiteral( "line_width" ) ) )
100  {
101  width = props[QStringLiteral( "line_width" )].toDouble();
102  }
103  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
104  {
105  width = props[QStringLiteral( "outline_width" )].toDouble();
106  }
107  else if ( props.contains( QStringLiteral( "width" ) ) )
108  {
109  //pre 2.5 projects used "width"
110  width = props[QStringLiteral( "width" )].toDouble();
111  }
112  if ( props.contains( QStringLiteral( "line_style" ) ) )
113  {
114  penStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )] );
115  }
116  else if ( props.contains( QStringLiteral( "outline_style" ) ) )
117  {
118  penStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )] );
119  }
120  else if ( props.contains( QStringLiteral( "penstyle" ) ) )
121  {
122  penStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "penstyle" )] );
123  }
124 
125  QgsSimpleLineSymbolLayer *l = new QgsSimpleLineSymbolLayer( color, width, penStyle );
126  if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
127  {
128  l->setWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
129  }
130  else if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
131  {
132  l->setWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
133  }
134  else if ( props.contains( QStringLiteral( "width_unit" ) ) )
135  {
136  //pre 2.5 projects used "width_unit"
137  l->setWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "width_unit" )] ) );
138  }
139  if ( props.contains( QStringLiteral( "width_map_unit_scale" ) ) )
140  l->setWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "width_map_unit_scale" )] ) );
141  if ( props.contains( QStringLiteral( "offset" ) ) )
142  l->setOffset( props[QStringLiteral( "offset" )].toDouble() );
143  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
144  l->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
145  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
146  l->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
147  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
148  l->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] ) );
149  if ( props.contains( QStringLiteral( "capstyle" ) ) )
150  l->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "capstyle" )] ) );
151 
152  if ( props.contains( QStringLiteral( "use_custom_dash" ) ) )
153  {
154  l->setUseCustomDashPattern( props[QStringLiteral( "use_custom_dash" )].toInt() );
155  }
156  if ( props.contains( QStringLiteral( "customdash" ) ) )
157  {
158  l->setCustomDashVector( QgsSymbolLayerUtils::decodeRealVector( props[QStringLiteral( "customdash" )] ) );
159  }
160  if ( props.contains( QStringLiteral( "customdash_unit" ) ) )
161  {
162  l->setCustomDashPatternUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "customdash_unit" )] ) );
163  }
164  if ( props.contains( QStringLiteral( "customdash_map_unit_scale" ) ) )
165  {
166  l->setCustomDashPatternMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "customdash_map_unit_scale" )] ) );
167  }
168 
169  if ( props.contains( QStringLiteral( "draw_inside_polygon" ) ) )
170  {
171  l->setDrawInsidePolygon( props[QStringLiteral( "draw_inside_polygon" )].toInt() );
172  }
173 
174  if ( props.contains( QStringLiteral( "ring_filter" ) ) )
175  {
176  l->setRingFilter( static_cast< RenderRingFilter>( props[QStringLiteral( "ring_filter" )].toInt() ) );
177  }
178 
180 
181  return l;
182 }
183 
184 
186 {
187  return QStringLiteral( "SimpleLine" );
188 }
189 
191 {
192  QColor penColor = mColor;
193  penColor.setAlphaF( mColor.alphaF() * context.opacity() );
194  mPen.setColor( penColor );
195  double scaledWidth = context.renderContext().convertToPainterUnits( mWidth, mWidthUnit, mWidthMapUnitScale );
196  mPen.setWidthF( scaledWidth );
197  if ( mUseCustomDashPattern )
198  {
199  mPen.setStyle( Qt::CustomDashLine );
200 
201  //scale pattern vector
202  double dashWidthDiv = qgsDoubleNear( scaledWidth, 0 ) ? 1.0 : scaledWidth;
203 
204  //fix dash pattern width in Qt 4.8
205  QStringList versionSplit = QString( qVersion() ).split( '.' );
206  if ( versionSplit.size() > 1
207  && versionSplit.at( 1 ).toInt() >= 8
208  && scaledWidth < 1.0 )
209  {
210  dashWidthDiv = 1.0;
211  }
212  QVector<qreal> scaledVector;
213  QVector<qreal>::const_iterator it = mCustomDashVector.constBegin();
214  for ( ; it != mCustomDashVector.constEnd(); ++it )
215  {
216  //the dash is specified in terms of pen widths, therefore the division
217  scaledVector << context.renderContext().convertToPainterUnits( ( *it ), mCustomDashPatternUnit, mCustomDashPatternMapUnitScale ) / dashWidthDiv;
218  }
219  mPen.setDashPattern( scaledVector );
220  }
221  else
222  {
223  mPen.setStyle( mPenStyle );
224  }
225  mPen.setJoinStyle( mPenJoinStyle );
226  mPen.setCapStyle( mPenCapStyle );
227 
228  mSelPen = mPen;
229  QColor selColor = context.renderContext().selectionColor();
230  if ( ! SELECTION_IS_OPAQUE )
231  selColor.setAlphaF( context.opacity() );
232  mSelPen.setColor( selColor );
233 }
234 
236 {
237  Q_UNUSED( context )
238 }
239 
240 void QgsSimpleLineSymbolLayer::renderPolygonStroke( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
241 {
242  QPainter *p = context.renderContext().painter();
243  if ( !p )
244  {
245  return;
246  }
247 
248  if ( mDrawInsidePolygon )
249  p->save();
250 
251  switch ( mRingFilter )
252  {
253  case AllRings:
254  case ExteriorRingOnly:
255  {
256  if ( mDrawInsidePolygon )
257  {
258  //only drawing the line on the interior of the polygon, so set clip path for painter
259  QPainterPath clipPath;
260  clipPath.addPolygon( points );
261 
262  if ( rings )
263  {
264  //add polygon rings
265  QList<QPolygonF>::const_iterator it = rings->constBegin();
266  for ( ; it != rings->constEnd(); ++it )
267  {
268  QPolygonF ring = *it;
269  clipPath.addPolygon( ring );
270  }
271  }
272 
273  //use intersect mode, as a clip path may already exist (e.g., for composer maps)
274  p->setClipPath( clipPath, Qt::IntersectClip );
275  }
276 
277  renderPolyline( points, context );
278  }
279  break;
280 
281  case InteriorRingsOnly:
282  break;
283  }
284 
285  if ( rings )
286  {
287  switch ( mRingFilter )
288  {
289  case AllRings:
290  case InteriorRingsOnly:
291  {
292  mOffset = -mOffset; // invert the offset for rings!
293  for ( const QPolygonF &ring : qgis::as_const( *rings ) )
294  renderPolyline( ring, context );
295  mOffset = -mOffset;
296  }
297  break;
298  case ExteriorRingOnly:
299  break;
300  }
301  }
302 
303  if ( mDrawInsidePolygon )
304  {
305  //restore painter to reset clip path
306  p->restore();
307  }
308 
309 }
310 
311 void QgsSimpleLineSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context )
312 {
313  QPainter *p = context.renderContext().painter();
314  if ( !p )
315  {
316  return;
317  }
318 
319  double offset = mOffset;
320  applyDataDefinedSymbology( context, mPen, mSelPen, offset );
321 
322  p->setPen( context.selected() ? mSelPen : mPen );
323  p->setBrush( Qt::NoBrush );
324 
325  // Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #2 points).
326  if ( points.size() <= 2 &&
329  ( p->renderHints() & QPainter::Antialiasing ) )
330  {
331  p->setRenderHint( QPainter::Antialiasing, false );
332 #if 0
333  p->drawPolyline( points );
334 #else
335  QPainterPath path;
336  path.addPolygon( points );
337  p->drawPath( path );
338 #endif
339  p->setRenderHint( QPainter::Antialiasing, true );
340  return;
341  }
342 
343  if ( qgsDoubleNear( offset, 0 ) )
344  {
345 #if 0
346  p->drawPolyline( points );
347 #else
348  QPainterPath path;
349  path.addPolygon( points );
350  p->drawPath( path );
351 #endif
352  }
353  else
354  {
355  double scaledOffset = context.renderContext().convertToPainterUnits( offset, mOffsetUnit, mOffsetMapUnitScale );
356  QList<QPolygonF> mline = ::offsetLine( points, scaledOffset, context.originalGeometryType() != QgsWkbTypes::UnknownGeometry ? context.originalGeometryType() : QgsWkbTypes::LineGeometry );
357  for ( int part = 0; part < mline.count(); ++part )
358  {
359 #if 0
360  p->drawPolyline( mline );
361 #else
362  QPainterPath path;
363  path.addPolygon( mline[ part ] );
364  p->drawPath( path );
365 #endif
366  }
367  }
368 }
369 
371 {
372  QgsStringMap map;
373  map[QStringLiteral( "line_color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
374  map[QStringLiteral( "line_width" )] = QString::number( mWidth );
375  map[QStringLiteral( "line_width_unit" )] = QgsUnitTypes::encodeUnit( mWidthUnit );
376  map[QStringLiteral( "width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mWidthMapUnitScale );
377  map[QStringLiteral( "line_style" )] = QgsSymbolLayerUtils::encodePenStyle( mPenStyle );
378  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
379  map[QStringLiteral( "capstyle" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
380  map[QStringLiteral( "offset" )] = QString::number( mOffset );
381  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
382  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
383  map[QStringLiteral( "use_custom_dash" )] = ( mUseCustomDashPattern ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
384  map[QStringLiteral( "customdash" )] = QgsSymbolLayerUtils::encodeRealVector( mCustomDashVector );
385  map[QStringLiteral( "customdash_unit" )] = QgsUnitTypes::encodeUnit( mCustomDashPatternUnit );
386  map[QStringLiteral( "customdash_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mCustomDashPatternMapUnitScale );
387  map[QStringLiteral( "draw_inside_polygon" )] = ( mDrawInsidePolygon ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
388  map[QStringLiteral( "ring_filter" )] = QString::number( static_cast< int >( mRingFilter ) );
389  return map;
390 }
391 
393 {
395  l->setWidthUnit( mWidthUnit );
399  l->setCustomDashPatternUnit( mCustomDashPatternUnit );
400  l->setCustomDashPatternMapUnitScale( mCustomDashPatternMapUnitScale );
401  l->setOffset( mOffset );
402  l->setPenJoinStyle( mPenJoinStyle );
403  l->setPenCapStyle( mPenCapStyle );
404  l->setUseCustomDashPattern( mUseCustomDashPattern );
405  l->setCustomDashVector( mCustomDashVector );
406  l->setDrawInsidePolygon( mDrawInsidePolygon );
409  copyPaintEffect( l );
410  return l;
411 }
412 
413 void QgsSimpleLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
414 {
415  if ( mPenStyle == Qt::NoPen )
416  return;
417 
418  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:LineSymbolizer" ) );
419  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
420  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
421  element.appendChild( symbolizerElem );
422 
423  // <Geometry>
424  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
425 
426  // <Stroke>
427  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
428  symbolizerElem.appendChild( strokeElem );
429 
430  Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
432  QVector<qreal> customDashVector = QgsSymbolLayerUtils::rescaleUom( mCustomDashVector, mCustomDashPatternUnit, props );
433  QgsSymbolLayerUtils::lineToSld( doc, strokeElem, penStyle, mColor, width,
434  &mPenJoinStyle, &mPenCapStyle, &customDashVector );
435 
436  // <se:PerpendicularOffset>
437  if ( !qgsDoubleNear( mOffset, 0.0 ) )
438  {
439  QDomElement perpOffsetElem = doc.createElement( QStringLiteral( "se:PerpendicularOffset" ) );
441  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
442  symbolizerElem.appendChild( perpOffsetElem );
443  }
444 }
445 
446 QString QgsSimpleLineSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
447 {
448  if ( mUseCustomDashPattern )
449  {
450  return QgsSymbolLayerUtils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor,
451  mPen.color(), mPenJoinStyle,
452  mPenCapStyle, mOffset, &mCustomDashVector );
453  }
454  else
455  {
456  return QgsSymbolLayerUtils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor, mPen.color(), mPenJoinStyle,
457  mPenCapStyle, mOffset );
458  }
459 }
460 
462 {
463  QgsDebugMsg( QStringLiteral( "Entered." ) );
464 
465  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
466  if ( strokeElem.isNull() )
467  return nullptr;
468 
469  Qt::PenStyle penStyle;
470  QColor color;
471  double width;
472  Qt::PenJoinStyle penJoinStyle;
473  Qt::PenCapStyle penCapStyle;
474  QVector<qreal> customDashVector;
475 
476  if ( !QgsSymbolLayerUtils::lineFromSld( strokeElem, penStyle,
477  color, width,
478  &penJoinStyle, &penCapStyle,
479  &customDashVector ) )
480  return nullptr;
481 
482  double offset = 0.0;
483  QDomElement perpOffsetElem = element.firstChildElement( QStringLiteral( "PerpendicularOffset" ) );
484  if ( !perpOffsetElem.isNull() )
485  {
486  bool ok;
487  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
488  if ( ok )
489  offset = d;
490  }
491 
492  QString uom = element.attribute( QStringLiteral( "uom" ) );
493  width = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, width );
494  offset = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset );
495 
496  QgsSimpleLineSymbolLayer *l = new QgsSimpleLineSymbolLayer( color, width, penStyle );
497  l->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
498  l->setOffset( offset );
499  l->setPenJoinStyle( penJoinStyle );
500  l->setPenCapStyle( penCapStyle );
501  l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine );
502  l->setCustomDashVector( customDashVector );
503  return l;
504 }
505 
506 void QgsSimpleLineSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, QPen &pen, QPen &selPen, double &offset )
507 {
508  if ( !dataDefinedProperties().hasActiveProperties() )
509  return; // shortcut
510 
511  //data defined properties
512  bool hasStrokeWidthExpression = false;
514  {
515  context.setOriginalValueVariable( mWidth );
516  double scaledWidth = context.renderContext().convertToPainterUnits(
519  pen.setWidthF( scaledWidth );
520  selPen.setWidthF( scaledWidth );
521  hasStrokeWidthExpression = true;
522  }
523 
524  //color
526  {
529  }
530 
531  //offset
533  {
536  }
537 
538  //dash dot vector
540  {
541  double scaledWidth = context.renderContext().convertToPainterUnits( mWidth, mWidthUnit, mWidthMapUnitScale );
542  double dashWidthDiv = mPen.widthF();
543 
544  if ( hasStrokeWidthExpression )
545  {
546  dashWidthDiv = pen.widthF();
547  scaledWidth = pen.widthF();
548  }
549 
550  //fix dash pattern width in Qt 4.8
551  QStringList versionSplit = QString( qVersion() ).split( '.' );
552  if ( versionSplit.size() > 1
553  && versionSplit.at( 1 ).toInt() >= 8
554  && scaledWidth < 1.0 )
555  {
556  dashWidthDiv = 1.0;
557  }
558 
559  QVector<qreal> dashVector;
561  if ( exprVal.isValid() )
562  {
563  QStringList dashList = exprVal.toString().split( ';' );
564  QStringList::const_iterator dashIt = dashList.constBegin();
565  for ( ; dashIt != dashList.constEnd(); ++dashIt )
566  {
567  dashVector.push_back( context.renderContext().convertToPainterUnits( dashIt->toDouble(), mCustomDashPatternUnit, mCustomDashPatternMapUnitScale ) / dashWidthDiv );
568  }
569  pen.setDashPattern( dashVector );
570  }
571  }
572 
573  //line style
575  {
578  if ( exprVal.isValid() )
579  pen.setStyle( QgsSymbolLayerUtils::decodePenStyle( exprVal.toString() ) );
580  }
581 
582  //join style
584  {
587  if ( exprVal.isValid() )
588  pen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( exprVal.toString() ) );
589  }
590 
591  //cap style
593  {
596  if ( exprVal.isValid() )
597  pen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( exprVal.toString() ) );
598  }
599 }
600 
602 {
603  if ( mDrawInsidePolygon )
604  {
605  //set to clip line to the interior of polygon, so we expect no bleed
606  return 0;
607  }
608  else
609  {
610  return context.convertToPainterUnits( ( mWidth / 2.0 ), mWidthUnit, mWidthMapUnitScale ) +
612  }
613 }
614 
616 {
617  unit = mCustomDashPatternUnit;
618  return mUseCustomDashPattern ? mCustomDashVector : QVector<qreal>();
619 }
620 
622 {
623  return mPenStyle;
624 }
625 
627 {
628  double width = mWidth;
630  {
631  context.setOriginalValueVariable( mWidth );
633  }
634 
637  {
639  }
640  return width;
641 }
642 
644 {
646  {
649  }
650  return mColor;
651 }
652 
654 {
655  Q_UNUSED( e )
656  double offset = mOffset;
657 
659  {
662  }
663 
666  {
668  }
669  return -offset; //direction seems to be inverse to symbology offset
670 }
671 
673 
675 
676 class MyLine
677 {
678  public:
679  MyLine( QPointF p1, QPointF p2 )
680  : mVertical( false )
681  , mIncreasing( false )
682  , mT( 0.0 )
683  , mLength( 0.0 )
684  {
685  if ( p1 == p2 )
686  return; // invalid
687 
688  // tangent and direction
689  if ( qgsDoubleNear( p1.x(), p2.x() ) )
690  {
691  // vertical line - tangent undefined
692  mVertical = true;
693  mIncreasing = ( p2.y() > p1.y() );
694  }
695  else
696  {
697  mVertical = false;
698  mT = ( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
699  mIncreasing = ( p2.x() > p1.x() );
700  }
701 
702  // length
703  double x = ( p2.x() - p1.x() );
704  double y = ( p2.y() - p1.y() );
705  mLength = std::sqrt( x * x + y * y );
706  }
707 
708  // return angle in radians
709  double angle()
710  {
711  double a = ( mVertical ? M_PI_2 : std::atan( mT ) );
712 
713  if ( !mIncreasing )
714  a += M_PI;
715  return a;
716  }
717 
718  // return difference for x,y when going along the line with specified interval
719  QPointF diffForInterval( double interval )
720  {
721  if ( mVertical )
722  return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
723 
724  double alpha = std::atan( mT );
725  double dx = std::cos( alpha ) * interval;
726  double dy = std::sin( alpha ) * interval;
727  return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
728  }
729 
730  double length() { return mLength; }
731 
732  protected:
733  bool mVertical;
734  bool mIncreasing;
735  double mT;
736  double mLength;
737 };
738 
740 
741 //
742 // QgsTemplatedLineSymbolLayerBase
743 //
745  : mRotateSymbols( rotateSymbol )
746  , mInterval( interval )
747 {
748 
749 }
750 
752 {
753  double offset = mOffset;
754 
756  {
759  }
760 
762 
764  {
766  if ( exprVal.isValid() )
767  {
768  QString placementString = exprVal.toString();
769  if ( placementString.compare( QLatin1String( "interval" ), Qt::CaseInsensitive ) == 0 )
770  {
772  }
773  else if ( placementString.compare( QLatin1String( "vertex" ), Qt::CaseInsensitive ) == 0 )
774  {
776  }
777  else if ( placementString.compare( QLatin1String( "lastvertex" ), Qt::CaseInsensitive ) == 0 )
778  {
780  }
781  else if ( placementString.compare( QLatin1String( "firstvertex" ), Qt::CaseInsensitive ) == 0 )
782  {
784  }
785  else if ( placementString.compare( QLatin1String( "centerpoint" ), Qt::CaseInsensitive ) == 0 )
786  {
788  }
789  else if ( placementString.compare( QLatin1String( "curvepoint" ), Qt::CaseInsensitive ) == 0 )
790  {
792  }
793  else
794  {
796  }
797  }
798  }
799 
800 
801  context.renderContext().painter()->save();
802 
803  double averageOver = mAverageAngleLength;
805  {
806  context.setOriginalValueVariable( mAverageAngleLength );
808  }
809  averageOver = context.renderContext().convertToPainterUnits( averageOver, mAverageAngleLengthUnit, mAverageAngleLengthMapUnitScale ) / 2.0;
810 
811  if ( qgsDoubleNear( offset, 0.0 ) )
812  {
813  switch ( placement )
814  {
815  case Interval:
816  renderPolylineInterval( points, context, averageOver );
817  break;
818 
819  case CentralPoint:
820  renderPolylineCentral( points, context, averageOver );
821  break;
822 
823  case Vertex:
824  case LastVertex:
825  case FirstVertex:
826  case CurvePoint:
827  renderPolylineVertex( points, context, placement );
828  break;
829  }
830  }
831  else
832  {
833  context.renderContext().setGeometry( nullptr ); //always use segmented geometry with offset
835 
836  for ( int part = 0; part < mline.count(); ++part )
837  {
838  const QPolygonF &points2 = mline[ part ];
839 
840  switch ( placement )
841  {
842  case Interval:
843  renderPolylineInterval( points2, context, averageOver );
844  break;
845 
846  case CentralPoint:
847  renderPolylineCentral( points2, context, averageOver );
848  break;
849 
850  case Vertex:
851  case LastVertex:
852  case FirstVertex:
853  case CurvePoint:
854  renderPolylineVertex( points2, context, placement );
855  break;
856  }
857  }
858  }
859 
860  context.renderContext().painter()->restore();
861 }
862 
863 void QgsTemplatedLineSymbolLayerBase::renderPolygonStroke( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
864 {
865  const QgsCurvePolygon *curvePolygon = dynamic_cast<const QgsCurvePolygon *>( context.renderContext().geometry() );
866 
867  if ( curvePolygon )
868  {
869  context.renderContext().setGeometry( curvePolygon->exteriorRing() );
870  }
871 
872  switch ( mRingFilter )
873  {
874  case AllRings:
875  case ExteriorRingOnly:
876  renderPolyline( points, context );
877  break;
878  case InteriorRingsOnly:
879  break;
880  }
881 
882  if ( rings )
883  {
884  switch ( mRingFilter )
885  {
886  case AllRings:
887  case InteriorRingsOnly:
888  {
889  mOffset = -mOffset; // invert the offset for rings!
890  for ( int i = 0; i < rings->size(); ++i )
891  {
892  if ( curvePolygon )
893  {
894  context.renderContext().setGeometry( curvePolygon->interiorRing( i ) );
895  }
896  renderPolyline( rings->at( i ), context );
897  }
898  mOffset = -mOffset;
899  }
900  break;
901  case ExteriorRingOnly:
902  break;
903  }
904  }
905 }
906 
908 {
910  if ( intervalUnit() != unit || mOffsetUnit != unit || offsetAlongLineUnit() != unit )
911  {
913  }
914  return unit;
915 }
916 
918 {
920  setIntervalMapUnitScale( scale );
921  mOffsetMapUnitScale = scale;
923 }
924 
926 {
930  {
931  return mOffsetMapUnitScale;
932  }
933  return QgsMapUnitScale();
934 }
935 
937 {
938  QgsStringMap map;
939  map[QStringLiteral( "rotate" )] = ( rotateSymbols() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
940  map[QStringLiteral( "interval" )] = QString::number( interval() );
941  map[QStringLiteral( "offset" )] = QString::number( mOffset );
942  map[QStringLiteral( "offset_along_line" )] = QString::number( offsetAlongLine() );
943  map[QStringLiteral( "offset_along_line_unit" )] = QgsUnitTypes::encodeUnit( offsetAlongLineUnit() );
944  map[QStringLiteral( "offset_along_line_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( offsetAlongLineMapUnitScale() );
945  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
946  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
947  map[QStringLiteral( "interval_unit" )] = QgsUnitTypes::encodeUnit( intervalUnit() );
948  map[QStringLiteral( "interval_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( intervalMapUnitScale() );
949  map[QStringLiteral( "average_angle_length" )] = QString::number( mAverageAngleLength );
950  map[QStringLiteral( "average_angle_unit" )] = QgsUnitTypes::encodeUnit( mAverageAngleLengthUnit );
951  map[QStringLiteral( "average_angle_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mAverageAngleLengthMapUnitScale );
952 
953  switch ( mPlacement )
954  {
955  case Vertex:
956  map[QStringLiteral( "placement" )] = QStringLiteral( "vertex" );
957  break;
958  case LastVertex:
959  map[QStringLiteral( "placement" )] = QStringLiteral( "lastvertex" );
960  break;
961  case FirstVertex:
962  map[QStringLiteral( "placement" )] = QStringLiteral( "firstvertex" );
963  break;
964  case CentralPoint:
965  map[QStringLiteral( "placement" )] = QStringLiteral( "centralpoint" );
966  break;
967  case CurvePoint:
968  map[QStringLiteral( "placement" )] = QStringLiteral( "curvepoint" );
969  break;
970  case Interval:
971  map[QStringLiteral( "placement" )] = QStringLiteral( "interval" );
972  break;
973  }
974 
975  map[QStringLiteral( "ring_filter" )] = QString::number( static_cast< int >( mRingFilter ) );
976  return map;
977 }
978 
980 {
981  destLayer->setSubSymbol( const_cast< QgsTemplatedLineSymbolLayerBase * >( this )->subSymbol()->clone() );
982  destLayer->setOffset( mOffset );
983  destLayer->setPlacement( placement() );
984  destLayer->setOffsetUnit( mOffsetUnit );
986  destLayer->setIntervalUnit( intervalUnit() );
988  destLayer->setOffsetAlongLine( offsetAlongLine() );
991  destLayer->setAverageAngleLength( mAverageAngleLength );
992  destLayer->setAverageAngleUnit( mAverageAngleLengthUnit );
993  destLayer->setAverageAngleMapUnitScale( mAverageAngleLengthMapUnitScale );
994  destLayer->setRingFilter( mRingFilter );
995  copyDataDefinedProperties( destLayer );
996  copyPaintEffect( destLayer );
997 }
998 
1000 {
1001  if ( properties.contains( QStringLiteral( "offset" ) ) )
1002  {
1003  destLayer->setOffset( properties[QStringLiteral( "offset" )].toDouble() );
1004  }
1005  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
1006  {
1007  destLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )] ) );
1008  }
1009  if ( properties.contains( QStringLiteral( "interval_unit" ) ) )
1010  {
1011  destLayer->setIntervalUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "interval_unit" )] ) );
1012  }
1013  if ( properties.contains( QStringLiteral( "offset_along_line" ) ) )
1014  {
1015  destLayer->setOffsetAlongLine( properties[QStringLiteral( "offset_along_line" )].toDouble() );
1016  }
1017  if ( properties.contains( QStringLiteral( "offset_along_line_unit" ) ) )
1018  {
1019  destLayer->setOffsetAlongLineUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_along_line_unit" )] ) );
1020  }
1021  if ( properties.contains( ( QStringLiteral( "offset_along_line_map_unit_scale" ) ) ) )
1022  {
1023  destLayer->setOffsetAlongLineMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_along_line_map_unit_scale" )] ) );
1024  }
1025 
1026  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1027  {
1028  destLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )] ) );
1029  }
1030  if ( properties.contains( QStringLiteral( "interval_map_unit_scale" ) ) )
1031  {
1032  destLayer->setIntervalMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "interval_map_unit_scale" )] ) );
1033  }
1034 
1035  if ( properties.contains( QStringLiteral( "average_angle_length" ) ) )
1036  {
1037  destLayer->setAverageAngleLength( properties[QStringLiteral( "average_angle_length" )].toDouble() );
1038  }
1039  if ( properties.contains( QStringLiteral( "average_angle_unit" ) ) )
1040  {
1041  destLayer->setAverageAngleUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "average_angle_unit" )] ) );
1042  }
1043  if ( properties.contains( ( QStringLiteral( "average_angle_map_unit_scale" ) ) ) )
1044  {
1045  destLayer->setAverageAngleMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "average_angle_map_unit_scale" )] ) );
1046  }
1047 
1048  if ( properties.contains( QStringLiteral( "placement" ) ) )
1049  {
1050  if ( properties[QStringLiteral( "placement" )] == QLatin1String( "vertex" ) )
1052  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "lastvertex" ) )
1054  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "firstvertex" ) )
1056  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "centralpoint" ) )
1058  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "curvepoint" ) )
1060  else
1062  }
1063 
1064  if ( properties.contains( QStringLiteral( "ring_filter" ) ) )
1065  {
1066  destLayer->setRingFilter( static_cast< RenderRingFilter>( properties[QStringLiteral( "ring_filter" )].toInt() ) );
1067  }
1068 
1069  destLayer->restoreOldDataDefinedProperties( properties );
1070 }
1071 
1072 void QgsTemplatedLineSymbolLayerBase::renderPolylineInterval( const QPolygonF &points, QgsSymbolRenderContext &context, double averageOver )
1073 {
1074  if ( points.isEmpty() )
1075  return;
1076 
1077  double lengthLeft = 0; // how much is left until next marker
1078 
1079  QgsRenderContext &rc = context.renderContext();
1080  double interval = mInterval;
1081 
1083  context.renderContext().expressionContext().appendScope( scope );
1084 
1086  {
1087  context.setOriginalValueVariable( mInterval );
1089  }
1090  if ( interval <= 0 )
1091  {
1092  interval = 0.1;
1093  }
1094  double offsetAlongLine = mOffsetAlongLine;
1096  {
1097  context.setOriginalValueVariable( mOffsetAlongLine );
1099  }
1100 
1101  const double painterUnitInterval = rc.convertToPainterUnits( interval, intervalUnit(), intervalMapUnitScale() );
1102 
1103  if ( painterUnitInterval < 0 )
1104  return;
1105 
1106  const double painterUnitOffsetAlongLine = rc.convertToPainterUnits( offsetAlongLine, offsetAlongLineUnit(), offsetAlongLineMapUnitScale() );
1107  lengthLeft = painterUnitInterval - painterUnitOffsetAlongLine;
1108 
1109  if ( averageOver > 0 && !qgsDoubleNear( averageOver, 0.0 ) )
1110  {
1111  QVector< QPointF > angleStartPoints;
1112  QVector< QPointF > symbolPoints;
1113  QVector< QPointF > angleEndPoints;
1114 
1115  // we collect 3 arrays of points. These correspond to
1116  // 1. the actual point at which to render the symbol
1117  // 2. the start point of a line averaging the angle over the desired distance (i.e. -averageOver distance from the points in array 1)
1118  // 3. the end point of a line averaging the angle over the desired distance (i.e. +averageOver distance from the points in array 2)
1119  // it gets quite tricky, because for closed rings we need to trace backwards from the initial point to calculate this
1120  // (or trace past the final point)
1121  collectOffsetPoints( points, symbolPoints, painterUnitInterval, lengthLeft );
1122 
1123  if ( symbolPoints.empty() )
1124  {
1125  // no symbols to draw, shortcut out early
1126  return;
1127  }
1128 
1129  if ( symbolPoints.count() > 1 && symbolPoints.constFirst() == symbolPoints.constLast() )
1130  {
1131  // avoid duplicate points at start and end of closed rings
1132  symbolPoints.pop_back();
1133  }
1134 
1135  angleEndPoints.reserve( symbolPoints.size() );
1136  angleStartPoints.reserve( symbolPoints.size() );
1137  if ( averageOver <= painterUnitOffsetAlongLine )
1138  {
1139  collectOffsetPoints( points, angleStartPoints, painterUnitInterval, lengthLeft + averageOver, 0, symbolPoints.size() );
1140  }
1141  else
1142  {
1143  collectOffsetPoints( points, angleStartPoints, painterUnitInterval, 0, averageOver - painterUnitOffsetAlongLine, symbolPoints.size() );
1144  }
1145  collectOffsetPoints( points, angleEndPoints, painterUnitInterval, lengthLeft - averageOver, 0, symbolPoints.size() );
1146 
1147  int pointNum = 0;
1148  for ( int i = 0; i < symbolPoints.size(); ++ i )
1149  {
1150  if ( context.renderContext().renderingStopped() )
1151  break;
1152 
1153  const QPointF pt = symbolPoints[i];
1154  const QPointF startPt = angleStartPoints[i];
1155  const QPointF endPt = angleEndPoints[i];
1156 
1157  MyLine l( startPt, endPt );
1158  // rotate marker (if desired)
1159  if ( rotateSymbols() )
1160  {
1161  setSymbolLineAngle( l.angle() * 180 / M_PI );
1162  }
1163 
1165  renderSymbol( pt, context.feature(), rc, -1, context.selected() );
1166  }
1167  }
1168  else
1169  {
1170  // not averaging line angle -- always use exact section angle
1171  int pointNum = 0;
1172  QPointF lastPt = points[0];
1173  for ( int i = 1; i < points.count(); ++i )
1174  {
1175  if ( context.renderContext().renderingStopped() )
1176  break;
1177 
1178  const QPointF &pt = points[i];
1179 
1180  if ( lastPt == pt ) // must not be equal!
1181  continue;
1182 
1183  // for each line, find out dx and dy, and length
1184  MyLine l( lastPt, pt );
1185  QPointF diff = l.diffForInterval( painterUnitInterval );
1186 
1187  // if there's some length left from previous line
1188  // use only the rest for the first point in new line segment
1189  double c = 1 - lengthLeft / painterUnitInterval;
1190 
1191  lengthLeft += l.length();
1192 
1193  // rotate marker (if desired)
1194  if ( rotateSymbols() )
1195  {
1196  setSymbolLineAngle( l.angle() * 180 / M_PI );
1197  }
1198 
1199  // while we're not at the end of line segment, draw!
1200  while ( lengthLeft > painterUnitInterval )
1201  {
1202  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
1203  lastPt += c * diff;
1204  lengthLeft -= painterUnitInterval;
1206  renderSymbol( lastPt, context.feature(), rc, -1, context.selected() );
1207  c = 1; // reset c (if wasn't 1 already)
1208  }
1209 
1210  lastPt = pt;
1211  }
1212 
1213  }
1214 
1215  delete context.renderContext().expressionContext().popScope();
1216 }
1217 
1218 static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt )
1219 {
1220  // calc average angle between the previous and next point
1221  double a1 = MyLine( prevPt, pt ).angle();
1222  double a2 = MyLine( pt, nextPt ).angle();
1223  double unitX = std::cos( a1 ) + std::cos( a2 ), unitY = std::sin( a1 ) + std::sin( a2 );
1224 
1225  return std::atan2( unitY, unitX );
1226 }
1227 
1228 void QgsTemplatedLineSymbolLayerBase::renderPolylineVertex( const QPolygonF &points, QgsSymbolRenderContext &context, QgsTemplatedLineSymbolLayerBase::Placement placement )
1229 {
1230  if ( points.isEmpty() )
1231  return;
1232 
1233  QgsRenderContext &rc = context.renderContext();
1234 
1235  double origAngle = symbolAngle();
1236  int i, maxCount;
1237  bool isRing = false;
1238 
1240  context.renderContext().expressionContext().appendScope( scope );
1242 
1243  double offsetAlongLine = mOffsetAlongLine;
1245  {
1246  context.setOriginalValueVariable( mOffsetAlongLine );
1248  }
1249  if ( !qgsDoubleNear( offsetAlongLine, 0.0 ) )
1250  {
1251  //scale offset along line
1252  offsetAlongLine = rc.convertToPainterUnits( offsetAlongLine, offsetAlongLineUnit(), offsetAlongLineMapUnitScale() );
1253  }
1254 
1255  if ( qgsDoubleNear( offsetAlongLine, 0.0 ) && context.renderContext().geometry()
1257  {
1259  const QgsMapToPixel &mtp = context.renderContext().mapToPixel();
1260 
1261  QgsVertexId vId;
1262  QgsPoint vPoint;
1263  double x, y, z;
1264  QPointF mapPoint;
1265  int pointNum = 0;
1266  while ( context.renderContext().geometry()->nextVertex( vId, vPoint ) )
1267  {
1268  if ( context.renderContext().renderingStopped() )
1269  break;
1270 
1272 
1275  {
1276  //transform
1277  x = vPoint.x();
1278  y = vPoint.y();
1279  z = 0.0;
1280  if ( ct.isValid() )
1281  {
1282  ct.transformInPlace( x, y, z );
1283  }
1284  mapPoint.setX( x );
1285  mapPoint.setY( y );
1286  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1287  if ( rotateSymbols() )
1288  {
1289  double angle = context.renderContext().geometry()->vertexAngle( vId );
1290  setSymbolAngle( angle * 180 / M_PI );
1291  }
1292  renderSymbol( mapPoint, context.feature(), rc, -1, context.selected() );
1293  }
1294  }
1295 
1296  delete context.renderContext().expressionContext().popScope();
1297  return;
1298  }
1299 
1300  switch ( placement )
1301  {
1302  case FirstVertex:
1303  {
1304  i = 0;
1305  maxCount = 1;
1306  break;
1307  }
1308 
1309  case LastVertex:
1310  {
1311  i = points.count() - 1;
1312  maxCount = points.count();
1313  break;
1314  }
1315 
1316  case Vertex:
1317  {
1318  i = 0;
1319  maxCount = points.count();
1320  if ( points.first() == points.last() )
1321  isRing = true;
1322  break;
1323  }
1324 
1325  case Interval:
1326  case CentralPoint:
1327  case CurvePoint:
1328  {
1329  delete context.renderContext().expressionContext().popScope();
1330  return;
1331  }
1332  }
1333 
1334  if ( offsetAlongLine > 0 && ( placement == QgsTemplatedLineSymbolLayerBase::FirstVertex || placement == QgsTemplatedLineSymbolLayerBase::LastVertex ) )
1335  {
1336  double distance;
1338  renderOffsetVertexAlongLine( points, i, distance, context );
1339  // restore original rotation
1340  setSymbolAngle( origAngle );
1341 
1342  delete context.renderContext().expressionContext().popScope();
1343  return;
1344  }
1345 
1346  int pointNum = 0;
1347  for ( ; i < maxCount; ++i )
1348  {
1350 
1351  if ( isRing && placement == QgsTemplatedLineSymbolLayerBase::Vertex && i == points.count() - 1 )
1352  {
1353  continue; // don't draw the last marker - it has been drawn already
1354  }
1355  // rotate marker (if desired)
1356  if ( rotateSymbols() )
1357  {
1358  double angle = markerAngle( points, isRing, i );
1359  setSymbolAngle( origAngle + angle * 180 / M_PI );
1360  }
1361 
1362  renderSymbol( points.at( i ), context.feature(), rc, -1, context.selected() );
1363  }
1364 
1365  // restore original rotation
1366  setSymbolAngle( origAngle );
1367 
1368  delete context.renderContext().expressionContext().popScope();
1369 }
1370 
1371 double QgsTemplatedLineSymbolLayerBase::markerAngle( const QPolygonF &points, bool isRing, int vertex )
1372 {
1373  double angle = 0;
1374  const QPointF &pt = points[vertex];
1375 
1376  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1377  {
1378  int prevIndex = vertex - 1;
1379  int nextIndex = vertex + 1;
1380 
1381  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1382  {
1383  prevIndex = points.count() - 2;
1384  nextIndex = 1;
1385  }
1386 
1387  QPointF prevPoint, nextPoint;
1388  while ( prevIndex >= 0 )
1389  {
1390  prevPoint = points[ prevIndex ];
1391  if ( prevPoint != pt )
1392  {
1393  break;
1394  }
1395  --prevIndex;
1396  }
1397 
1398  while ( nextIndex < points.count() )
1399  {
1400  nextPoint = points[ nextIndex ];
1401  if ( nextPoint != pt )
1402  {
1403  break;
1404  }
1405  ++nextIndex;
1406  }
1407 
1408  if ( prevIndex >= 0 && nextIndex < points.count() )
1409  {
1410  angle = _averageAngle( prevPoint, pt, nextPoint );
1411  }
1412  }
1413  else //no ring and vertex is at start / at end
1414  {
1415  if ( vertex == 0 )
1416  {
1417  while ( vertex < points.size() - 1 )
1418  {
1419  const QPointF &nextPt = points[vertex + 1];
1420  if ( pt != nextPt )
1421  {
1422  angle = MyLine( pt, nextPt ).angle();
1423  return angle;
1424  }
1425  ++vertex;
1426  }
1427  }
1428  else
1429  {
1430  // use last segment's angle
1431  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1432  {
1433  const QPointF &prevPt = points[vertex - 1];
1434  if ( pt != prevPt )
1435  {
1436  angle = MyLine( prevPt, pt ).angle();
1437  return angle;
1438  }
1439  --vertex;
1440  }
1441  }
1442  }
1443  return angle;
1444 }
1445 
1446 void QgsTemplatedLineSymbolLayerBase::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolRenderContext &context )
1447 {
1448  if ( points.isEmpty() )
1449  return;
1450 
1451  QgsRenderContext &rc = context.renderContext();
1452  double origAngle = symbolAngle();
1453  if ( qgsDoubleNear( distance, 0.0 ) )
1454  {
1455  // rotate marker (if desired)
1456  if ( rotateSymbols() )
1457  {
1458  bool isRing = false;
1459  if ( points.first() == points.last() )
1460  isRing = true;
1461  double angle = markerAngle( points, isRing, vertex );
1462  setSymbolAngle( origAngle + angle * 180 / M_PI );
1463  }
1464  renderSymbol( points[vertex], context.feature(), rc, -1, context.selected() );
1465  return;
1466  }
1467 
1468  int pointIncrement = distance > 0 ? 1 : -1;
1469  QPointF previousPoint = points[vertex];
1470  int startPoint = distance > 0 ? std::min( vertex + 1, points.count() - 1 ) : std::max( vertex - 1, 0 );
1471  int endPoint = distance > 0 ? points.count() - 1 : 0;
1472  double distanceLeft = std::fabs( distance );
1473 
1474  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1475  {
1476  const QPointF &pt = points[i];
1477 
1478  if ( previousPoint == pt ) // must not be equal!
1479  continue;
1480 
1481  // create line segment
1482  MyLine l( previousPoint, pt );
1483 
1484  if ( distanceLeft < l.length() )
1485  {
1486  //destination point is in current segment
1487  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1488  // rotate marker (if desired)
1489  if ( rotateSymbols() )
1490  {
1491  setSymbolAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1492  }
1493  renderSymbol( markerPoint, context.feature(), rc, -1, context.selected() );
1494  return;
1495  }
1496 
1497  distanceLeft -= l.length();
1498  previousPoint = pt;
1499  }
1500 
1501  //didn't find point
1502 }
1503 
1504 void QgsTemplatedLineSymbolLayerBase::collectOffsetPoints( const QVector<QPointF> &p, QVector<QPointF> &dest, double intervalPainterUnits, double initialOffset, double initialLag, int numberPointsRequired )
1505 {
1506  if ( p.empty() )
1507  return;
1508 
1509  QVector< QPointF > points = p;
1510  const bool closedRing = points.first() == points.last();
1511 
1512  double lengthLeft = initialOffset;
1513 
1514  double initialLagLeft = initialLag > 0 ? -initialLag : 1; // an initialLagLeft of > 0 signifies end of lagging start points
1515  if ( initialLagLeft < 0 && closedRing )
1516  {
1517  // tracking back around the ring from the first point, insert pseudo vertices before the first vertex
1518  QPointF lastPt = points.constLast();
1519  QVector< QPointF > pseudoPoints;
1520  for ( int i = points.count() - 2; i > 0; --i )
1521  {
1522  if ( initialLagLeft >= 0 )
1523  {
1524  break;
1525  }
1526 
1527  const QPointF &pt = points[i];
1528 
1529  if ( lastPt == pt ) // must not be equal!
1530  continue;
1531 
1532  MyLine l( lastPt, pt );
1533  initialLagLeft += l.length();
1534  lastPt = pt;
1535 
1536  pseudoPoints << pt;
1537  }
1538  std::reverse( pseudoPoints.begin(), pseudoPoints.end() );
1539 
1540  points = pseudoPoints;
1541  points.append( p );
1542  }
1543  else
1544  {
1545  while ( initialLagLeft < 0 )
1546  {
1547  dest << points.constFirst();
1548  initialLagLeft += intervalPainterUnits;
1549  }
1550  }
1551  if ( initialLag > 0 )
1552  {
1553  lengthLeft += intervalPainterUnits - initialLagLeft;
1554  }
1555 
1556  QPointF lastPt = points[0];
1557  for ( int i = 1; i < points.count(); ++i )
1558  {
1559  const QPointF &pt = points[i];
1560 
1561  if ( lastPt == pt ) // must not be equal!
1562  {
1563  if ( closedRing && i == points.count() - 1 && numberPointsRequired > 0 && dest.size() < numberPointsRequired )
1564  {
1565  lastPt = points[0];
1566  i = 0;
1567  }
1568  continue;
1569  }
1570 
1571  // for each line, find out dx and dy, and length
1572  MyLine l( lastPt, pt );
1573  QPointF diff = l.diffForInterval( intervalPainterUnits );
1574 
1575  // if there's some length left from previous line
1576  // use only the rest for the first point in new line segment
1577  double c = 1 - lengthLeft / intervalPainterUnits;
1578 
1579  lengthLeft += l.length();
1580 
1581 
1582  while ( lengthLeft > intervalPainterUnits || qgsDoubleNear( lengthLeft, intervalPainterUnits, 0.000000001 ) )
1583  {
1584  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
1585  lastPt += c * diff;
1586  lengthLeft -= intervalPainterUnits;
1587  dest << lastPt;
1588  c = 1; // reset c (if wasn't 1 already)
1589  if ( numberPointsRequired > 0 && dest.size() >= numberPointsRequired )
1590  break;
1591  }
1592  lastPt = pt;
1593 
1594  if ( numberPointsRequired > 0 && dest.size() >= numberPointsRequired )
1595  break;
1596 
1597  // if a closed ring, we keep looping around the ring until we hit the required number of points
1598  if ( closedRing && i == points.count() - 1 && numberPointsRequired > 0 && dest.size() < numberPointsRequired )
1599  {
1600  lastPt = points[0];
1601  i = 0;
1602  }
1603  }
1604 
1605  if ( !closedRing && numberPointsRequired > 0 && dest.size() < numberPointsRequired )
1606  {
1607  // pad with repeating last point to match desired size
1608  while ( dest.size() < numberPointsRequired )
1609  dest << points.constLast();
1610  }
1611 }
1612 
1613 void QgsTemplatedLineSymbolLayerBase::renderPolylineCentral( const QPolygonF &points, QgsSymbolRenderContext &context, double averageAngleOver )
1614 {
1615  if ( !points.isEmpty() )
1616  {
1617  // calc length
1618  qreal length = 0;
1619  QPolygonF::const_iterator it = points.constBegin();
1620  QPointF last = *it;
1621  for ( ++it; it != points.constEnd(); ++it )
1622  {
1623  length += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
1624  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1625  last = *it;
1626  }
1627 
1628  const double midPoint = length / 2;
1629 
1630  QPointF pt;
1631  double thisSymbolAngle = 0;
1632 
1633  if ( averageAngleOver > 0 && !qgsDoubleNear( averageAngleOver, 0.0 ) )
1634  {
1635  QVector< QPointF > angleStartPoints;
1636  QVector< QPointF > symbolPoints;
1637  QVector< QPointF > angleEndPoints;
1638  // collectOffsetPoints will have the first point in the line as the first result -- we don't want this, we need the second
1639  collectOffsetPoints( points, symbolPoints, midPoint, midPoint, 0.0, 2 );
1640  collectOffsetPoints( points, angleStartPoints, midPoint, 0, averageAngleOver, 2 );
1641  collectOffsetPoints( points, angleEndPoints, midPoint, midPoint - averageAngleOver, 0, 2 );
1642 
1643  pt = symbolPoints.at( 1 );
1644  MyLine l( angleStartPoints.at( 1 ), angleEndPoints.at( 1 ) );
1645  thisSymbolAngle = l.angle();
1646  }
1647  else
1648  {
1649  // find the segment where the central point lies
1650  it = points.constBegin();
1651  last = *it;
1652  qreal last_at = 0, next_at = 0;
1653  QPointF next;
1654  int segment = 0;
1655  for ( ++it; it != points.constEnd(); ++it )
1656  {
1657  next = *it;
1658  next_at += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
1659  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1660  if ( next_at >= midPoint )
1661  break; // we have reached the center
1662  last = *it;
1663  last_at = next_at;
1664  segment++;
1665  }
1666 
1667  // find out the central point on segment
1668  MyLine l( last, next ); // for line angle
1669  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1670  pt = last + ( next - last ) * k;
1671  thisSymbolAngle = l.angle();
1672  }
1673 
1674  // draw the marker
1675  double origAngle = symbolAngle();
1676  if ( rotateSymbols() )
1677  setSymbolAngle( origAngle + thisSymbolAngle * 180 / M_PI );
1678  renderSymbol( pt, context.feature(), context.renderContext(), -1, context.selected() );
1679  if ( rotateSymbols() )
1680  setSymbolAngle( origAngle );
1681  }
1682 }
1683 
1685 {
1686  return mMarker.get();
1687 }
1688 
1690 {
1691  if ( !symbol || symbol->type() != QgsSymbol::Marker )
1692  {
1693  delete symbol;
1694  return false;
1695  }
1696 
1697  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
1698  mColor = mMarker->color();
1699  return true;
1700 }
1701 
1702 
1703 
1704 //
1705 // QgsMarkerLineSymbolLayer
1706 //
1707 
1709  : QgsTemplatedLineSymbolLayerBase( rotateMarker, interval )
1710 {
1711  setSubSymbol( new QgsMarkerSymbol() );
1712 }
1713 
1715 {
1716  bool rotate = DEFAULT_MARKERLINE_ROTATE;
1718 
1719  if ( props.contains( QStringLiteral( "interval" ) ) )
1720  interval = props[QStringLiteral( "interval" )].toDouble();
1721  if ( props.contains( QStringLiteral( "rotate" ) ) )
1722  rotate = ( props[QStringLiteral( "rotate" )] == QLatin1String( "1" ) );
1723 
1724  std::unique_ptr< QgsMarkerLineSymbolLayer > x = qgis::make_unique< QgsMarkerLineSymbolLayer >( rotate, interval );
1725  setCommonProperties( x.get(), props );
1726  return x.release();
1727 }
1728 
1730 {
1731  return QStringLiteral( "MarkerLine" );
1732 }
1733 
1735 {
1736  mMarker->setColor( color );
1737  mColor = color;
1738 }
1739 
1741 {
1742  return mMarker ? mMarker->color() : mColor;
1743 }
1744 
1746 {
1747  mMarker->setOpacity( context.opacity() );
1748 
1749  // if being rotated, it gets initialized with every line segment
1750  QgsSymbol::RenderHints hints = nullptr;
1751  if ( rotateSymbols() )
1752  hints |= QgsSymbol::DynamicRotation;
1753  mMarker->setRenderHints( hints );
1754 
1755  mMarker->startRender( context.renderContext(), context.fields() );
1756 }
1757 
1759 {
1760  mMarker->stopRender( context.renderContext() );
1761 }
1762 
1763 
1765 {
1766  std::unique_ptr< QgsMarkerLineSymbolLayer > x = qgis::make_unique< QgsMarkerLineSymbolLayer >( rotateSymbols(), interval() );
1767  copyTemplateSymbolProperties( x.get() );
1768  return x.release();
1769 }
1770 
1771 void QgsMarkerLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
1772 {
1773  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1774  {
1775  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:LineSymbolizer" ) );
1776  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
1777  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
1778  element.appendChild( symbolizerElem );
1779 
1780  // <Geometry>
1781  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
1782 
1783  QString gap;
1784  switch ( placement() )
1785  {
1787  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "firstPoint" ) ) );
1788  break;
1790  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "lastPoint" ) ) );
1791  break;
1793  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "centralPoint" ) ) );
1794  break;
1796  // no way to get line/polygon's vertices, use a VendorOption
1797  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "points" ) ) );
1798  break;
1799  default:
1801  gap = qgsDoubleToString( interval );
1802  break;
1803  }
1804 
1805  if ( !rotateSymbols() )
1806  {
1807  // markers in LineSymbolizer must be drawn following the line orientation,
1808  // use a VendorOption when no marker rotation
1809  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "rotateMarker" ), QStringLiteral( "0" ) ) );
1810  }
1811 
1812  // <Stroke>
1813  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
1814  symbolizerElem.appendChild( strokeElem );
1815 
1816  // <GraphicStroke>
1817  QDomElement graphicStrokeElem = doc.createElement( QStringLiteral( "se:GraphicStroke" ) );
1818  strokeElem.appendChild( graphicStrokeElem );
1819 
1820  QgsSymbolLayer *layer = mMarker->symbolLayer( i );
1821  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
1822  if ( !markerLayer )
1823  {
1824  graphicStrokeElem.appendChild( doc.createComment( QStringLiteral( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() ) ) );
1825  }
1826  else
1827  {
1828  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1829  }
1830 
1831  if ( !gap.isEmpty() )
1832  {
1833  QDomElement gapElem = doc.createElement( QStringLiteral( "se:Gap" ) );
1834  QgsSymbolLayerUtils::createExpressionElement( doc, gapElem, gap );
1835  graphicStrokeElem.appendChild( gapElem );
1836  }
1837 
1838  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1839  {
1840  QDomElement perpOffsetElem = doc.createElement( QStringLiteral( "se:PerpendicularOffset" ) );
1842  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
1843  symbolizerElem.appendChild( perpOffsetElem );
1844  }
1845  }
1846 }
1847 
1849 {
1850  QgsDebugMsg( QStringLiteral( "Entered." ) );
1851 
1852  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
1853  if ( strokeElem.isNull() )
1854  return nullptr;
1855 
1856  QDomElement graphicStrokeElem = strokeElem.firstChildElement( QStringLiteral( "GraphicStroke" ) );
1857  if ( graphicStrokeElem.isNull() )
1858  return nullptr;
1859 
1860  // retrieve vendor options
1861  bool rotateMarker = true;
1863 
1864  QgsStringMap vendorOptions = QgsSymbolLayerUtils::getVendorOptionList( element );
1865  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1866  {
1867  if ( it.key() == QLatin1String( "placement" ) )
1868  {
1869  if ( it.value() == QLatin1String( "points" ) )
1871  else if ( it.value() == QLatin1String( "firstPoint" ) )
1873  else if ( it.value() == QLatin1String( "lastPoint" ) )
1875  else if ( it.value() == QLatin1String( "centralPoint" ) )
1877  }
1878  else if ( it.value() == QLatin1String( "rotateMarker" ) )
1879  {
1880  rotateMarker = it.value() == QLatin1String( "0" );
1881  }
1882  }
1883 
1884  std::unique_ptr< QgsMarkerSymbol > marker;
1885 
1887  if ( l )
1888  {
1889  QgsSymbolLayerList layers;
1890  layers.append( l );
1891  marker.reset( new QgsMarkerSymbol( layers ) );
1892  }
1893 
1894  if ( !marker )
1895  return nullptr;
1896 
1897  double interval = 0.0;
1898  QDomElement gapElem = graphicStrokeElem.firstChildElement( QStringLiteral( "Gap" ) );
1899  if ( !gapElem.isNull() )
1900  {
1901  bool ok;
1902  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1903  if ( ok )
1904  interval = d;
1905  }
1906 
1907  double offset = 0.0;
1908  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( QStringLiteral( "PerpendicularOffset" ) );
1909  if ( !perpOffsetElem.isNull() )
1910  {
1911  bool ok;
1912  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1913  if ( ok )
1914  offset = d;
1915  }
1916 
1917  QString uom = element.attribute( QStringLiteral( "uom" ) );
1918  interval = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, interval );
1919  offset = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset );
1920 
1921  QgsMarkerLineSymbolLayer *x = new QgsMarkerLineSymbolLayer( rotateMarker );
1922  x->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1923  x->setPlacement( placement );
1924  x->setInterval( interval );
1925  x->setSubSymbol( marker.release() );
1926  x->setOffset( offset );
1927  return x;
1928 }
1929 
1931 {
1932  mMarker->setSize( width );
1933 }
1934 
1936 {
1937  if ( key == QgsSymbolLayer::PropertyWidth && mMarker && property )
1938  {
1939  mMarker->setDataDefinedSize( property );
1940  }
1942 }
1943 
1945 {
1946  mMarker->setLineAngle( angle );
1947 }
1948 
1950 {
1951  return mMarker->angle();
1952 }
1953 
1955 {
1956  mMarker->setAngle( angle );
1957 }
1958 
1959 void QgsMarkerLineSymbolLayer::renderSymbol( const QPointF &point, const QgsFeature *feature, QgsRenderContext &context, int layer, bool selected )
1960 {
1961  mMarker->renderPoint( point, feature, context, layer, selected );
1962 }
1963 
1965 {
1966  return mMarker->size();
1967 }
1968 
1970 {
1971  return mMarker->size( context );
1972 }
1973 
1975 {
1977  mMarker->setOutputUnit( unit );
1978  setIntervalUnit( unit );
1979  mOffsetUnit = unit;
1980  setOffsetAlongLineUnit( unit );
1981 }
1982 
1983 
1984 QSet<QString> QgsMarkerLineSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
1985 {
1986  QSet<QString> attr = QgsLineSymbolLayer::usedAttributes( context );
1987  if ( mMarker )
1988  attr.unite( mMarker->usedAttributes( context ) );
1989  return attr;
1990 }
1991 
1993 {
1995  return true;
1996  if ( mMarker && mMarker->hasDataDefinedProperties() )
1997  return true;
1998  return false;
1999 }
2000 
2002 {
2003  return ( mMarker->size( context ) / 2.0 ) +
2005 }
2006 
2007 
2008 //
2009 // QgsHashedLineSymbolLayer
2010 //
2011 
2013  : QgsTemplatedLineSymbolLayerBase( rotateSymbol, interval )
2014 {
2015  setSubSymbol( new QgsLineSymbol() );
2016 }
2017 
2019 {
2020  bool rotate = DEFAULT_MARKERLINE_ROTATE;
2022 
2023  if ( props.contains( QStringLiteral( "interval" ) ) )
2024  interval = props[QStringLiteral( "interval" )].toDouble();
2025  if ( props.contains( QStringLiteral( "rotate" ) ) )
2026  rotate = ( props[QStringLiteral( "rotate" )] == QLatin1String( "1" ) );
2027 
2028  std::unique_ptr< QgsHashedLineSymbolLayer > x = qgis::make_unique< QgsHashedLineSymbolLayer >( rotate, interval );
2029  setCommonProperties( x.get(), props );
2030  if ( props.contains( QStringLiteral( "hash_angle" ) ) )
2031  {
2032  x->setHashAngle( props[QStringLiteral( "hash_angle" )].toDouble() );
2033  }
2034 
2035  if ( props.contains( QStringLiteral( "hash_length" ) ) )
2036  x->setHashLength( props[QStringLiteral( "hash_length" )].toDouble() );
2037 
2038  if ( props.contains( QStringLiteral( "hash_length_unit" ) ) )
2039  x->setHashLengthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "hash_length_unit" )] ) );
2040 
2041  if ( props.contains( QStringLiteral( "hash_length_map_unit_scale" ) ) )
2042  x->setHashLengthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "hash_length_map_unit_scale" )] ) );
2043 
2044  return x.release();
2045 }
2046 
2048 {
2049  return QStringLiteral( "HashLine" );
2050 }
2051 
2053 {
2054  mHashSymbol->setOpacity( context.opacity() );
2055 
2056  // if being rotated, it gets initialized with every line segment
2057  QgsSymbol::RenderHints hints = nullptr;
2058  if ( rotateSymbols() )
2059  hints |= QgsSymbol::DynamicRotation;
2060  mHashSymbol->setRenderHints( hints );
2061 
2062  mHashSymbol->startRender( context.renderContext(), context.fields() );
2063 }
2064 
2066 {
2067  mHashSymbol->stopRender( context.renderContext() );
2068 }
2069 
2071 {
2073  map[ QStringLiteral( "hash_angle" ) ] = QString::number( mHashAngle );
2074 
2075  map[QStringLiteral( "hash_length" )] = QString::number( mHashLength );
2076  map[QStringLiteral( "hash_length_unit" )] = QgsUnitTypes::encodeUnit( mHashLengthUnit );
2077  map[QStringLiteral( "hash_length_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mHashLengthMapUnitScale );
2078 
2079  return map;
2080 }
2081 
2083 {
2084  std::unique_ptr< QgsHashedLineSymbolLayer > x = qgis::make_unique< QgsHashedLineSymbolLayer >( rotateSymbols(), interval() );
2085  copyTemplateSymbolProperties( x.get() );
2086  x->setHashAngle( mHashAngle );
2087  x->setHashLength( mHashLength );
2088  x->setHashLengthUnit( mHashLengthUnit );
2089  x->setHashLengthMapUnitScale( mHashLengthMapUnitScale );
2090  return x.release();
2091 }
2092 
2094 {
2095  mHashSymbol->setColor( color );
2096  mColor = color;
2097 }
2098 
2100 {
2101  return mHashSymbol ? mHashSymbol->color() : mColor;
2102 }
2103 
2105 {
2106  return mHashSymbol.get();
2107 }
2108 
2110 {
2111  if ( !symbol || symbol->type() != QgsSymbol::Line )
2112  {
2113  delete symbol;
2114  return false;
2115  }
2116 
2117  mHashSymbol.reset( static_cast<QgsLineSymbol *>( symbol ) );
2118  mColor = mHashSymbol->color();
2119  return true;
2120 }
2121 
2123 {
2124  mHashLength = width;
2125 }
2126 
2128 {
2129  return mHashLength;
2130 }
2131 
2133 {
2134  return context.convertToPainterUnits( mHashLength, mHashLengthUnit, mHashLengthMapUnitScale );
2135 }
2136 
2138 {
2139  return ( mHashSymbol->width( context ) / 2.0 )
2140  + context.convertToPainterUnits( mHashLength, mHashLengthUnit, mHashLengthMapUnitScale )
2141  + context.convertToPainterUnits( std::fabs( mOffset ), mOffsetUnit, mOffsetMapUnitScale );
2142 }
2143 
2145 {
2147  mHashSymbol->setOutputUnit( unit );
2148  setIntervalUnit( unit );
2149  mOffsetUnit = unit;
2150  setOffsetAlongLineUnit( unit );
2151 }
2152 
2153 QSet<QString> QgsHashedLineSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
2154 {
2155  QSet<QString> attr = QgsLineSymbolLayer::usedAttributes( context );
2156  if ( mHashSymbol )
2157  attr.unite( mHashSymbol->usedAttributes( context ) );
2158  return attr;
2159 }
2160 
2162 {
2164  return true;
2165  if ( mHashSymbol && mHashSymbol->hasDataDefinedProperties() )
2166  return true;
2167  return false;
2168 }
2169 
2171 {
2172  if ( key == QgsSymbolLayer::PropertyWidth && mHashSymbol && property )
2173  {
2174  mHashSymbol->setDataDefinedWidth( property );
2175  }
2177 }
2178 
2180 {
2181  mSymbolLineAngle = angle;
2182 }
2183 
2185 {
2186  return mSymbolAngle;
2187 }
2188 
2190 {
2191  mSymbolAngle = angle;
2192 }
2193 
2194 void QgsHashedLineSymbolLayer::renderSymbol( const QPointF &point, const QgsFeature *feature, QgsRenderContext &context, int layer, bool selected )
2195 {
2196  double lineLength = mHashLength;
2198  {
2199  context.expressionContext().setOriginalValueVariable( mHashLength );
2201  }
2202  const double w = context.convertToPainterUnits( lineLength, mHashLengthUnit, mHashLengthMapUnitScale ) / 2.0;
2203 
2204  double hashAngle = mHashAngle;
2206  {
2207  context.expressionContext().setOriginalValueVariable( mHashAngle );
2209  }
2210 
2211  QgsPointXY center( point );
2212  QgsPointXY start = center.project( w, 180 - ( mSymbolAngle + mSymbolLineAngle + hashAngle ) );
2213  QgsPointXY end = center.project( -w, 180 - ( mSymbolAngle + mSymbolLineAngle + hashAngle ) );
2214 
2215  QPolygonF points;
2216  points << QPointF( start.x(), start.y() ) << QPointF( end.x(), end.y() );
2217 
2218  mHashSymbol->renderPolyline( points, feature, context, layer, selected );
2219 }
2220 
2222 {
2223  return mHashAngle;
2224 }
2225 
2227 {
2228  mHashAngle = angle;
2229 }
2230 
2231 
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
Added in QGIS v2.4.
QgsFields fields() const
Fields of the layer.
Definition: qgssymbol.h:680
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:265
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:119
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:587
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:766
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:966
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...
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:1125
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:617
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:225
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:70
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:574
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:630
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:672
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.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
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:655
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)
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
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.
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:111
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