QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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;
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;
67 }
68 
70 {
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 );
401  l->setOffset( mOffset );
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;
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,
453  }
454  else
455  {
456  return QgsSymbolLayerUtils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor, mPen.color(), mPenJoinStyle,
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 = float( 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 QgsMarkerLineSymbolLayer::QgsMarkerLineSymbolLayer( bool rotateMarker, double interval )
742 {
743  mRotateMarker = rotateMarker;
744  mInterval = interval;
745  mIntervalUnit = QgsUnitTypes::RenderMillimeters;
746  mMarker = nullptr;
747  mPlacement = Interval;
748  mOffsetAlongLine = 0;
749  mOffsetAlongLineUnit = QgsUnitTypes::RenderMillimeters;
750 
751  setSubSymbol( new QgsMarkerSymbol() );
752 }
753 
755 {
756  bool rotate = DEFAULT_MARKERLINE_ROTATE;
757  double interval = DEFAULT_MARKERLINE_INTERVAL;
758 
759 
760  if ( props.contains( QStringLiteral( "interval" ) ) )
761  interval = props[QStringLiteral( "interval" )].toDouble();
762  if ( props.contains( QStringLiteral( "rotate" ) ) )
763  rotate = ( props[QStringLiteral( "rotate" )] == QLatin1String( "1" ) );
764 
765  QgsMarkerLineSymbolLayer *x = new QgsMarkerLineSymbolLayer( rotate, interval );
766  if ( props.contains( QStringLiteral( "offset" ) ) )
767  {
768  x->setOffset( props[QStringLiteral( "offset" )].toDouble() );
769  }
770  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
771  {
772  x->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
773  }
774  if ( props.contains( QStringLiteral( "interval_unit" ) ) )
775  {
776  x->setIntervalUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "interval_unit" )] ) );
777  }
778  if ( props.contains( QStringLiteral( "offset_along_line" ) ) )
779  {
780  x->setOffsetAlongLine( props[QStringLiteral( "offset_along_line" )].toDouble() );
781  }
782  if ( props.contains( QStringLiteral( "offset_along_line_unit" ) ) )
783  {
784  x->setOffsetAlongLineUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_along_line_unit" )] ) );
785  }
786  if ( props.contains( ( QStringLiteral( "offset_along_line_map_unit_scale" ) ) ) )
787  {
788  x->setOffsetAlongLineMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_along_line_map_unit_scale" )] ) );
789  }
790 
791  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
792  {
793  x->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
794  }
795  if ( props.contains( QStringLiteral( "interval_map_unit_scale" ) ) )
796  {
797  x->setIntervalMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "interval_map_unit_scale" )] ) );
798  }
799 
800  if ( props.contains( QStringLiteral( "placement" ) ) )
801  {
802  if ( props[QStringLiteral( "placement" )] == QLatin1String( "vertex" ) )
803  x->setPlacement( Vertex );
804  else if ( props[QStringLiteral( "placement" )] == QLatin1String( "lastvertex" ) )
805  x->setPlacement( LastVertex );
806  else if ( props[QStringLiteral( "placement" )] == QLatin1String( "firstvertex" ) )
807  x->setPlacement( FirstVertex );
808  else if ( props[QStringLiteral( "placement" )] == QLatin1String( "centralpoint" ) )
809  x->setPlacement( CentralPoint );
810  else if ( props[QStringLiteral( "placement" )] == QLatin1String( "curvepoint" ) )
811  x->setPlacement( CurvePoint );
812  else
813  x->setPlacement( Interval );
814  }
815 
816  if ( props.contains( QStringLiteral( "ring_filter" ) ) )
817  {
818  x->setRingFilter( static_cast< RenderRingFilter>( props[QStringLiteral( "ring_filter" )].toInt() ) );
819  }
820 
822 
823  return x;
824 }
825 
827 {
828  return QStringLiteral( "MarkerLine" );
829 }
830 
832 {
833  mMarker->setColor( color );
834  mColor = color;
835 }
836 
838 {
839  return mMarker ? mMarker->color() : mColor;
840 }
841 
843 {
844  mMarker->setOpacity( context.opacity() );
845 
846  // if being rotated, it gets initialized with every line segment
847  QgsSymbol::RenderHints hints = nullptr;
848  if ( mRotateMarker )
850  mMarker->setRenderHints( hints );
851 
852  mMarker->startRender( context.renderContext(), context.fields() );
853 }
854 
856 {
857  mMarker->stopRender( context.renderContext() );
858 }
859 
860 void QgsMarkerLineSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context )
861 {
862  double offset = mOffset;
863 
865  {
868  }
869 
870  Placement placement = mPlacement;
871 
873  {
875  if ( exprVal.isValid() )
876  {
877  QString placementString = exprVal.toString();
878  if ( placementString.compare( QLatin1String( "interval" ), Qt::CaseInsensitive ) == 0 )
879  {
880  placement = Interval;
881  }
882  else if ( placementString.compare( QLatin1String( "vertex" ), Qt::CaseInsensitive ) == 0 )
883  {
884  placement = Vertex;
885  }
886  else if ( placementString.compare( QLatin1String( "lastvertex" ), Qt::CaseInsensitive ) == 0 )
887  {
888  placement = LastVertex;
889  }
890  else if ( placementString.compare( QLatin1String( "firstvertex" ), Qt::CaseInsensitive ) == 0 )
891  {
892  placement = FirstVertex;
893  }
894  else if ( placementString.compare( QLatin1String( "centerpoint" ), Qt::CaseInsensitive ) == 0 )
895  {
896  placement = CentralPoint;
897  }
898  else if ( placementString.compare( QLatin1String( "curvepoint" ), Qt::CaseInsensitive ) == 0 )
899  {
900  placement = CurvePoint;
901  }
902  else
903  {
904  placement = Interval;
905  }
906  }
907  }
908 
909 
910  context.renderContext().painter()->save();
911 
912  if ( qgsDoubleNear( offset, 0.0 ) )
913  {
914  if ( placement == Interval )
915  renderPolylineInterval( points, context );
916  else if ( placement == CentralPoint )
917  renderPolylineCentral( points, context );
918  else
919  renderPolylineVertex( points, context, placement );
920  }
921  else
922  {
923  context.renderContext().setGeometry( nullptr ); //always use segmented geometry with offset
925 
926  for ( int part = 0; part < mline.count(); ++part )
927  {
928  const QPolygonF &points2 = mline[ part ];
929 
930  if ( placement == Interval )
931  renderPolylineInterval( points2, context );
932  else if ( placement == CentralPoint )
933  renderPolylineCentral( points2, context );
934  else
935  renderPolylineVertex( points2, context, placement );
936  }
937  }
938 
939  context.renderContext().painter()->restore();
940 }
941 
942 void QgsMarkerLineSymbolLayer::renderPolygonStroke( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
943 {
944  const QgsCurvePolygon *curvePolygon = dynamic_cast<const QgsCurvePolygon *>( context.renderContext().geometry() );
945 
946  if ( curvePolygon )
947  {
948  context.renderContext().setGeometry( curvePolygon->exteriorRing() );
949  }
950 
951  switch ( mRingFilter )
952  {
953  case AllRings:
954  case ExteriorRingOnly:
955  renderPolyline( points, context );
956  break;
957  case InteriorRingsOnly:
958  break;
959  }
960 
961  if ( rings )
962  {
963  switch ( mRingFilter )
964  {
965  case AllRings:
966  case InteriorRingsOnly:
967  {
968  mOffset = -mOffset; // invert the offset for rings!
969  for ( int i = 0; i < rings->size(); ++i )
970  {
971  if ( curvePolygon )
972  {
973  context.renderContext().setGeometry( curvePolygon->interiorRing( i ) );
974  }
975  renderPolyline( rings->at( i ), context );
976  }
977  mOffset = -mOffset;
978  }
979  break;
980  case ExteriorRingOnly:
981  break;
982  }
983  }
984 }
985 
987 {
988  if ( points.isEmpty() )
989  return;
990 
991  QPointF lastPt = points[0];
992  double lengthLeft = 0; // how much is left until next marker
993 
994  QgsRenderContext &rc = context.renderContext();
995  double interval = mInterval;
996 
998  context.renderContext().expressionContext().appendScope( scope );
999 
1001  {
1002  context.setOriginalValueVariable( mInterval );
1004  }
1005  if ( interval <= 0 )
1006  {
1007  interval = 0.1;
1008  }
1009  double offsetAlongLine = mOffsetAlongLine;
1011  {
1012  context.setOriginalValueVariable( mOffsetAlongLine );
1014  }
1015 
1016  double painterUnitInterval = rc.convertToPainterUnits( interval, mIntervalUnit, mIntervalMapUnitScale );
1017  lengthLeft = painterUnitInterval - rc.convertToPainterUnits( offsetAlongLine, mOffsetAlongLineUnit, mOffsetAlongLineMapUnitScale );
1018 
1019  int pointNum = 0;
1020  for ( int i = 1; i < points.count(); ++i )
1021  {
1022  if ( context.renderContext().renderingStopped() )
1023  break;
1024 
1025  const QPointF &pt = points[i];
1026 
1027  if ( lastPt == pt ) // must not be equal!
1028  continue;
1029 
1030  // for each line, find out dx and dy, and length
1031  MyLine l( lastPt, pt );
1032  QPointF diff = l.diffForInterval( painterUnitInterval );
1033 
1034  // if there's some length left from previous line
1035  // use only the rest for the first point in new line segment
1036  double c = 1 - lengthLeft / painterUnitInterval;
1037 
1038  lengthLeft += l.length();
1039 
1040  // rotate marker (if desired)
1041  if ( mRotateMarker )
1042  {
1043  mMarker->setLineAngle( l.angle() * 180 / M_PI );
1044  }
1045 
1046 
1047  // while we're not at the end of line segment, draw!
1048  while ( lengthLeft > painterUnitInterval )
1049  {
1050  if ( context.renderContext().renderingStopped() )
1051  break;
1052 
1053  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
1054  lastPt += c * diff;
1055  lengthLeft -= painterUnitInterval;
1057  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
1058  c = 1; // reset c (if wasn't 1 already)
1059  }
1060 
1061  lastPt = pt;
1062  }
1063 
1064  delete context.renderContext().expressionContext().popScope();
1065 }
1066 
1067 static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt )
1068 {
1069  // calc average angle between the previous and next point
1070  double a1 = MyLine( prevPt, pt ).angle();
1071  double a2 = MyLine( pt, nextPt ).angle();
1072  double unitX = std::cos( a1 ) + std::cos( a2 ), unitY = std::sin( a1 ) + std::sin( a2 );
1073 
1074  return std::atan2( unitY, unitX );
1075 }
1076 
1077 void QgsMarkerLineSymbolLayer::renderPolylineVertex( const QPolygonF &points, QgsSymbolRenderContext &context, Placement placement )
1078 {
1079  if ( points.isEmpty() )
1080  return;
1081 
1082  QgsRenderContext &rc = context.renderContext();
1083 
1084  double origAngle = mMarker->angle();
1085  int i, maxCount;
1086  bool isRing = false;
1087 
1089  context.renderContext().expressionContext().appendScope( scope );
1091 
1092  double offsetAlongLine = mOffsetAlongLine;
1094  {
1095  context.setOriginalValueVariable( mOffsetAlongLine );
1097  }
1098  if ( !qgsDoubleNear( offsetAlongLine, 0.0 ) )
1099  {
1100  //scale offset along line
1101  offsetAlongLine = rc.convertToPainterUnits( offsetAlongLine, mOffsetAlongLineUnit, mOffsetAlongLineMapUnitScale );
1102  }
1103 
1104  if ( qgsDoubleNear( offsetAlongLine, 0.0 ) && context.renderContext().geometry()
1105  && context.renderContext().geometry()->hasCurvedSegments() && ( placement == Vertex || placement == CurvePoint ) )
1106  {
1108  const QgsMapToPixel &mtp = context.renderContext().mapToPixel();
1109 
1110  QgsVertexId vId;
1111  QgsPoint vPoint;
1112  double x, y, z;
1113  QPointF mapPoint;
1114  int pointNum = 0;
1115  while ( context.renderContext().geometry()->nextVertex( vId, vPoint ) )
1116  {
1117  if ( context.renderContext().renderingStopped() )
1118  break;
1119 
1121 
1122  if ( ( placement == Vertex && vId.type == QgsVertexId::SegmentVertex )
1123  || ( placement == CurvePoint && vId.type == QgsVertexId::CurveVertex ) )
1124  {
1125  //transform
1126  x = vPoint.x();
1127  y = vPoint.y();
1128  z = 0.0;
1129  if ( ct.isValid() )
1130  {
1131  ct.transformInPlace( x, y, z );
1132  }
1133  mapPoint.setX( x );
1134  mapPoint.setY( y );
1135  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1136  if ( mRotateMarker )
1137  {
1138  double angle = context.renderContext().geometry()->vertexAngle( vId );
1139  mMarker->setAngle( angle * 180 / M_PI );
1140  }
1141  mMarker->renderPoint( mapPoint, context.feature(), rc, -1, context.selected() );
1142  }
1143  }
1144 
1145  delete context.renderContext().expressionContext().popScope();
1146  return;
1147  }
1148 
1149  if ( placement == FirstVertex )
1150  {
1151  i = 0;
1152  maxCount = 1;
1153  }
1154  else if ( placement == LastVertex )
1155  {
1156  i = points.count() - 1;
1157  maxCount = points.count();
1158  }
1159  else if ( placement == Vertex )
1160  {
1161  i = 0;
1162  maxCount = points.count();
1163  if ( points.first() == points.last() )
1164  isRing = true;
1165  }
1166  else
1167  {
1168  delete context.renderContext().expressionContext().popScope();
1169  return;
1170  }
1171 
1172  if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
1173  {
1174  double distance;
1175  distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine;
1176  renderOffsetVertexAlongLine( points, i, distance, context );
1177  // restore original rotation
1178  mMarker->setAngle( origAngle );
1179 
1180  delete context.renderContext().expressionContext().popScope();
1181  return;
1182  }
1183 
1184  int pointNum = 0;
1185  for ( ; i < maxCount; ++i )
1186  {
1188 
1189  if ( isRing && placement == Vertex && i == points.count() - 1 )
1190  {
1191  continue; // don't draw the last marker - it has been drawn already
1192  }
1193  // rotate marker (if desired)
1194  if ( mRotateMarker )
1195  {
1196  double angle = markerAngle( points, isRing, i );
1197  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1198  }
1199 
1200  mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() );
1201  }
1202 
1203  // restore original rotation
1204  mMarker->setAngle( origAngle );
1205 
1206  delete context.renderContext().expressionContext().popScope();
1207 }
1208 
1209 double QgsMarkerLineSymbolLayer::markerAngle( const QPolygonF &points, bool isRing, int vertex )
1210 {
1211  double angle = 0;
1212  const QPointF &pt = points[vertex];
1213 
1214  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1215  {
1216  int prevIndex = vertex - 1;
1217  int nextIndex = vertex + 1;
1218 
1219  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1220  {
1221  prevIndex = points.count() - 2;
1222  nextIndex = 1;
1223  }
1224 
1225  QPointF prevPoint, nextPoint;
1226  while ( prevIndex >= 0 )
1227  {
1228  prevPoint = points[ prevIndex ];
1229  if ( prevPoint != pt )
1230  {
1231  break;
1232  }
1233  --prevIndex;
1234  }
1235 
1236  while ( nextIndex < points.count() )
1237  {
1238  nextPoint = points[ nextIndex ];
1239  if ( nextPoint != pt )
1240  {
1241  break;
1242  }
1243  ++nextIndex;
1244  }
1245 
1246  if ( prevIndex >= 0 && nextIndex < points.count() )
1247  {
1248  angle = _averageAngle( prevPoint, pt, nextPoint );
1249  }
1250  }
1251  else //no ring and vertex is at start / at end
1252  {
1253  if ( vertex == 0 )
1254  {
1255  while ( vertex < points.size() - 1 )
1256  {
1257  const QPointF &nextPt = points[vertex + 1];
1258  if ( pt != nextPt )
1259  {
1260  angle = MyLine( pt, nextPt ).angle();
1261  return angle;
1262  }
1263  ++vertex;
1264  }
1265  }
1266  else
1267  {
1268  // use last segment's angle
1269  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1270  {
1271  const QPointF &prevPt = points[vertex - 1];
1272  if ( pt != prevPt )
1273  {
1274  angle = MyLine( prevPt, pt ).angle();
1275  return angle;
1276  }
1277  --vertex;
1278  }
1279  }
1280  }
1281  return angle;
1282 }
1283 
1284 void QgsMarkerLineSymbolLayer::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolRenderContext &context )
1285 {
1286  if ( points.isEmpty() )
1287  return;
1288 
1289  QgsRenderContext &rc = context.renderContext();
1290  double origAngle = mMarker->angle();
1291  if ( qgsDoubleNear( distance, 0.0 ) )
1292  {
1293  // rotate marker (if desired)
1294  if ( mRotateMarker )
1295  {
1296  bool isRing = false;
1297  if ( points.first() == points.last() )
1298  isRing = true;
1299  double angle = markerAngle( points, isRing, vertex );
1300  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1301  }
1302  mMarker->renderPoint( points[vertex], context.feature(), rc, -1, context.selected() );
1303  return;
1304  }
1305 
1306  int pointIncrement = distance > 0 ? 1 : -1;
1307  QPointF previousPoint = points[vertex];
1308  int startPoint = distance > 0 ? std::min( vertex + 1, points.count() - 1 ) : std::max( vertex - 1, 0 );
1309  int endPoint = distance > 0 ? points.count() - 1 : 0;
1310  double distanceLeft = std::fabs( distance );
1311 
1312  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1313  {
1314  const QPointF &pt = points[i];
1315 
1316  if ( previousPoint == pt ) // must not be equal!
1317  continue;
1318 
1319  // create line segment
1320  MyLine l( previousPoint, pt );
1321 
1322  if ( distanceLeft < l.length() )
1323  {
1324  //destination point is in current segment
1325  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1326  // rotate marker (if desired)
1327  if ( mRotateMarker )
1328  {
1329  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1330  }
1331  mMarker->renderPoint( markerPoint, context.feature(), rc, -1, context.selected() );
1332  return;
1333  }
1334 
1335  distanceLeft -= l.length();
1336  previousPoint = pt;
1337  }
1338 
1339  //didn't find point
1340 }
1341 
1343 {
1344  if ( !points.isEmpty() )
1345  {
1346  // calc length
1347  qreal length = 0;
1348  QPolygonF::const_iterator it = points.constBegin();
1349  QPointF last = *it;
1350  for ( ++it; it != points.constEnd(); ++it )
1351  {
1352  length += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
1353  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1354  last = *it;
1355  }
1356 
1357  // find the segment where the central point lies
1358  it = points.constBegin();
1359  last = *it;
1360  qreal last_at = 0, next_at = 0;
1361  QPointF next;
1362  int segment = 0;
1363  for ( ++it; it != points.constEnd(); ++it )
1364  {
1365  next = *it;
1366  next_at += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
1367  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1368  if ( next_at >= length / 2 )
1369  break; // we have reached the center
1370  last = *it;
1371  last_at = next_at;
1372  segment++;
1373  }
1374 
1375  // find out the central point on segment
1376  MyLine l( last, next ); // for line angle
1377  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1378  QPointF pt = last + ( next - last ) * k;
1379 
1380  // draw the marker
1381  double origAngle = mMarker->angle();
1382  if ( mRotateMarker )
1383  mMarker->setAngle( origAngle + l.angle() * 180 / M_PI );
1384  mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() );
1385  if ( mRotateMarker )
1386  mMarker->setAngle( origAngle );
1387  }
1388 }
1389 
1390 
1392 {
1393  QgsStringMap map;
1394  map[QStringLiteral( "rotate" )] = ( mRotateMarker ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
1395  map[QStringLiteral( "interval" )] = QString::number( mInterval );
1396  map[QStringLiteral( "offset" )] = QString::number( mOffset );
1397  map[QStringLiteral( "offset_along_line" )] = QString::number( mOffsetAlongLine );
1398  map[QStringLiteral( "offset_along_line_unit" )] = QgsUnitTypes::encodeUnit( mOffsetAlongLineUnit );
1399  map[QStringLiteral( "offset_along_line_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetAlongLineMapUnitScale );
1400  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1401  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1402  map[QStringLiteral( "interval_unit" )] = QgsUnitTypes::encodeUnit( mIntervalUnit );
1403  map[QStringLiteral( "interval_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mIntervalMapUnitScale );
1404  if ( mPlacement == Vertex )
1405  map[QStringLiteral( "placement" )] = QStringLiteral( "vertex" );
1406  else if ( mPlacement == LastVertex )
1407  map[QStringLiteral( "placement" )] = QStringLiteral( "lastvertex" );
1408  else if ( mPlacement == FirstVertex )
1409  map[QStringLiteral( "placement" )] = QStringLiteral( "firstvertex" );
1410  else if ( mPlacement == CentralPoint )
1411  map[QStringLiteral( "placement" )] = QStringLiteral( "centralpoint" );
1412  else if ( mPlacement == CurvePoint )
1413  map[QStringLiteral( "placement" )] = QStringLiteral( "curvepoint" );
1414  else
1415  map[QStringLiteral( "placement" )] = QStringLiteral( "interval" );
1416 
1417  map[QStringLiteral( "ring_filter" )] = QString::number( static_cast< int >( mRingFilter ) );
1418  return map;
1419 }
1420 
1422 {
1423  return mMarker.get();
1424 }
1425 
1427 {
1428  if ( !symbol || symbol->type() != QgsSymbol::Marker )
1429  {
1430  delete symbol;
1431  return false;
1432  }
1433 
1434  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
1435  mColor = mMarker->color();
1436  return true;
1437 }
1438 
1440 {
1441  QgsMarkerLineSymbolLayer *x = new QgsMarkerLineSymbolLayer( mRotateMarker, mInterval );
1442  x->setSubSymbol( mMarker->clone() );
1443  x->setOffset( mOffset );
1444  x->setPlacement( mPlacement );
1445  x->setOffsetUnit( mOffsetUnit );
1447  x->setIntervalUnit( mIntervalUnit );
1448  x->setIntervalMapUnitScale( mIntervalMapUnitScale );
1449  x->setOffsetAlongLine( mOffsetAlongLine );
1450  x->setOffsetAlongLineMapUnitScale( mOffsetAlongLineMapUnitScale );
1451  x->setOffsetAlongLineUnit( mOffsetAlongLineUnit );
1452  x->setRingFilter( mRingFilter );
1454  copyPaintEffect( x );
1455  return x;
1456 }
1457 
1458 void QgsMarkerLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
1459 {
1460  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1461  {
1462  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:LineSymbolizer" ) );
1463  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
1464  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
1465  element.appendChild( symbolizerElem );
1466 
1467  // <Geometry>
1468  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
1469 
1470  QString gap;
1471  switch ( mPlacement )
1472  {
1473  case FirstVertex:
1474  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "firstPoint" ) ) );
1475  break;
1476  case LastVertex:
1477  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "lastPoint" ) ) );
1478  break;
1479  case CentralPoint:
1480  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "centralPoint" ) ) );
1481  break;
1482  case Vertex:
1483  // no way to get line/polygon's vertices, use a VendorOption
1484  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "points" ) ) );
1485  break;
1486  default:
1487  double interval = QgsSymbolLayerUtils::rescaleUom( mInterval, mIntervalUnit, props );
1488  gap = qgsDoubleToString( interval );
1489  break;
1490  }
1491 
1492  if ( !mRotateMarker )
1493  {
1494  // markers in LineSymbolizer must be drawn following the line orientation,
1495  // use a VendorOption when no marker rotation
1496  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "rotateMarker" ), QStringLiteral( "0" ) ) );
1497  }
1498 
1499  // <Stroke>
1500  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
1501  symbolizerElem.appendChild( strokeElem );
1502 
1503  // <GraphicStroke>
1504  QDomElement graphicStrokeElem = doc.createElement( QStringLiteral( "se:GraphicStroke" ) );
1505  strokeElem.appendChild( graphicStrokeElem );
1506 
1507  QgsSymbolLayer *layer = mMarker->symbolLayer( i );
1508  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
1509  if ( !markerLayer )
1510  {
1511  graphicStrokeElem.appendChild( doc.createComment( QStringLiteral( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() ) ) );
1512  }
1513  else
1514  {
1515  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1516  }
1517 
1518  if ( !gap.isEmpty() )
1519  {
1520  QDomElement gapElem = doc.createElement( QStringLiteral( "se:Gap" ) );
1521  QgsSymbolLayerUtils::createExpressionElement( doc, gapElem, gap );
1522  graphicStrokeElem.appendChild( gapElem );
1523  }
1524 
1525  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1526  {
1527  QDomElement perpOffsetElem = doc.createElement( QStringLiteral( "se:PerpendicularOffset" ) );
1529  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
1530  symbolizerElem.appendChild( perpOffsetElem );
1531  }
1532  }
1533 }
1534 
1536 {
1537  QgsDebugMsg( QStringLiteral( "Entered." ) );
1538 
1539  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
1540  if ( strokeElem.isNull() )
1541  return nullptr;
1542 
1543  QDomElement graphicStrokeElem = strokeElem.firstChildElement( QStringLiteral( "GraphicStroke" ) );
1544  if ( graphicStrokeElem.isNull() )
1545  return nullptr;
1546 
1547  // retrieve vendor options
1548  bool rotateMarker = true;
1549  Placement placement = Interval;
1550 
1551  QgsStringMap vendorOptions = QgsSymbolLayerUtils::getVendorOptionList( element );
1552  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1553  {
1554  if ( it.key() == QLatin1String( "placement" ) )
1555  {
1556  if ( it.value() == QLatin1String( "points" ) ) placement = Vertex;
1557  else if ( it.value() == QLatin1String( "firstPoint" ) ) placement = FirstVertex;
1558  else if ( it.value() == QLatin1String( "lastPoint" ) ) placement = LastVertex;
1559  else if ( it.value() == QLatin1String( "centralPoint" ) ) placement = CentralPoint;
1560  }
1561  else if ( it.value() == QLatin1String( "rotateMarker" ) )
1562  {
1563  rotateMarker = it.value() == QLatin1String( "0" );
1564  }
1565  }
1566 
1567  std::unique_ptr< QgsMarkerSymbol > marker;
1568 
1570  if ( l )
1571  {
1572  QgsSymbolLayerList layers;
1573  layers.append( l );
1574  marker.reset( new QgsMarkerSymbol( layers ) );
1575  }
1576 
1577  if ( !marker )
1578  return nullptr;
1579 
1580  double interval = 0.0;
1581  QDomElement gapElem = graphicStrokeElem.firstChildElement( QStringLiteral( "Gap" ) );
1582  if ( !gapElem.isNull() )
1583  {
1584  bool ok;
1585  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1586  if ( ok )
1587  interval = d;
1588  }
1589 
1590  double offset = 0.0;
1591  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( QStringLiteral( "PerpendicularOffset" ) );
1592  if ( !perpOffsetElem.isNull() )
1593  {
1594  bool ok;
1595  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1596  if ( ok )
1597  offset = d;
1598  }
1599 
1600  QString uom = element.attribute( QStringLiteral( "uom" ) );
1601  interval = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, interval );
1602  offset = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset );
1603 
1604  QgsMarkerLineSymbolLayer *x = new QgsMarkerLineSymbolLayer( rotateMarker );
1605  x->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1606  x->setPlacement( placement );
1607  x->setInterval( interval );
1608  x->setSubSymbol( marker.release() );
1609  x->setOffset( offset );
1610  return x;
1611 }
1612 
1614 {
1615  mMarker->setSize( width );
1616 }
1617 
1619 {
1620  if ( key == QgsSymbolLayer::PropertyWidth && mMarker && property )
1621  {
1622  mMarker->setDataDefinedSize( property );
1623  }
1625 }
1626 
1628 {
1629  return mMarker->size();
1630 }
1631 
1633 {
1634  return mMarker->size( context );
1635 }
1636 
1638 {
1640  mMarker->setOutputUnit( unit );
1641  mIntervalUnit = unit;
1642  mOffsetUnit = unit;
1643  mOffsetAlongLineUnit = unit;
1644 }
1645 
1647 {
1649  if ( mIntervalUnit != unit || mOffsetUnit != unit || mOffsetAlongLineUnit != unit )
1650  {
1652  }
1653  return unit;
1654 }
1655 
1657 {
1659  mIntervalMapUnitScale = scale;
1660  mOffsetMapUnitScale = scale;
1661  mOffsetAlongLineMapUnitScale = scale;
1662 }
1663 
1665 {
1666  if ( QgsLineSymbolLayer::mapUnitScale() == mIntervalMapUnitScale &&
1667  mIntervalMapUnitScale == mOffsetMapUnitScale &&
1668  mOffsetMapUnitScale == mOffsetAlongLineMapUnitScale )
1669  {
1670  return mOffsetMapUnitScale;
1671  }
1672  return QgsMapUnitScale();
1673 }
1674 
1675 QSet<QString> QgsMarkerLineSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
1676 {
1677  QSet<QString> attr = QgsLineSymbolLayer::usedAttributes( context );
1678  if ( mMarker )
1679  attr.unite( mMarker->usedAttributes( context ) );
1680  return attr;
1681 }
1682 
1684 {
1686  return true;
1687  if ( mMarker && mMarker->hasDataDefinedProperties() )
1688  return true;
1689  return false;
1690 }
1691 
1693 {
1694  return ( mMarker->size( context ) / 2.0 ) +
1696 }
1697 
1698 
1699 
QgsUnitTypes::RenderUnit widthUnit() const
Returns the units for the line&#39;s width.
void renderPolygonStroke(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
const QgsCurve * exteriorRing() const
Returns the curve polygon&#39;s exterior ring.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
Single variable definition for use within a QgsExpressionContextScope.
void setMapUnitScale(const QgsMapUnitScale &scale) override
double dxfOffset(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets offset.
double y
Definition: qgspoint.h:42
float threshold() const
Gets the simplification threshold of the vector layer managed.
void setMapUnitScale(const QgsMapUnitScale &scale) override
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
void renderPolygonStroke(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolRenderContext &context) override
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
static const QString EXPR_GEOMETRY_POINT_COUNT
Inbuilt variable name for point count variable.
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:150
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
void setUseCustomDashPattern(bool b)
virtual bool hasCurvedSegments() const
Returns true if the geometry contains curved segments.
QColor selectionColor() const
Returns the color to use when rendering selected features.
const QgsVectorSimplifyMethod & vectorSimplifyMethod() const
Added in QGIS v2.4.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsWkbTypes::GeometryType originalGeometryType() const
Returns the geometry type for the original feature geometry being rendered.
Definition: qgssymbol.h:645
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
static QgsSymbolLayer * createFromSld(QDomElement &element)
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:278
Render both exterior and interior rings.
#define DEFAULT_MARKERLINE_INTERVAL
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
Curve polygon geometry type.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsUnitTypes::RenderUnit mOffsetUnit
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
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.
Qt::PenStyle penStyle() const
Qt::PenJoinStyle mPenJoinStyle
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
virtual bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const =0
Returns next vertex id and coordinates.
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Writes the symbol layer definition as a SLD XML element.
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:577
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.
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:732
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
void setWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the line&#39;s width.
void setWidth(double width) override
void renderPolylineCentral(const QPolygonF &points, QgsSymbolRenderContext &context)
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
void setInterval(double interval)
Sets the interval between individual markers.
static QVector< qreal > decodeRealVector(const QString &s)
void setCustomDashPatternUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for lengths used in the custom dash pattern.
static QString encodeColor(const QColor &color)
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
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:1091
static QString encodePenStyle(Qt::PenStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:36
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.
void transformInPlace(double &x, double &y) const
Transform device coordinates to map coordinates.
Placement
Defines how/where the marker should be placed on the line.
QgsMapUnitScale mapUnitScale() const override
virtual bool setSubSymbol(QgsSymbol *symbol)
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
void setPlacement(Placement p)
The placement of the markers.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void renderPolylineInterval(const QPolygonF &points, QgsSymbolRenderContext &context)
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Create a new MarkerLineSymbolLayerV2.
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;...
bool renderingStopped() const
Returns TRUE if the rendering operation has been stopped and any ongoing rendering should be canceled...
static QgsStringMap getVendorOptionList(QDomElement &element)
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
Render the interior rings only.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:238
void renderPolyline(const QPolygonF &points, QgsSymbolRenderContext &context) override
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer&#39;s property collection, used for data defined overrides...
QVector< qreal > customDashVector() const
#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
static QgsSymbolLayer * createMarkerLayerFromSld(QDomElement &element)
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
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:51
#define DEFAULT_SIMPLELINE_COLOR
QgsSimpleLineSymbolLayer(const QColor &color=DEFAULT_SIMPLELINE_COLOR, double width=DEFAULT_SIMPLELINE_WIDTH, Qt::PenStyle penStyle=DEFAULT_SIMPLELINE_PENSTYLE)
double mapUnitsPerPixel() const
Returns current map units per pixel.
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
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)
Single scope for storing variables and functions for use within a QgsExpressionContext.
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:572
void setOffsetAlongLineMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale used for calculating the offset in map units along line for markers...
void setIntervalMapUnitScale(const QgsMapUnitScale &scale)
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.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
void setMapUnitScale(const QgsMapUnitScale &scale) override
double width() const override
Returns the estimated width for the line symbol layer.
void renderPolylineVertex(const QPolygonF &points, QgsSymbolRenderContext &context, Placement placement=Vertex)
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...
void setOffsetAlongLineUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit used for calculating the offset along line for markers.
Qt::PenJoinStyle penJoinStyle() const
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
QgsFields fields() const
Fields of the layer.
Definition: qgssymbol.h:653
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
void setPenCapStyle(Qt::PenCapStyle style)
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...
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:602
double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets line width.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
#define DEFAULT_SIMPLELINE_WIDTH
virtual double width() const
Returns the estimated width for the line symbol layer.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
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...
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)
Contains information about the context of a rendering operation.
Abstract base class for marker symbol layers.
void startRender(QgsSymbolRenderContext &context) override
QgsSimpleLineSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QPainter * painter()
Returns the destination QPainter for the render operation.
double markerAngle(const QPolygonF &points, bool isRing, int vertex)
RenderRingFilter mRingFilter
void startRender(QgsSymbolRenderContext &context) override
Struct for storing maximum and minimum scales for measurements in map units.
void setCustomDashPatternMapUnitScale(const QgsMapUnitScale &scale)
const QgsAbstractGeometry * geometry() const
Returns pointer to the unsegmentized geometry.
void setWidthMapUnitScale(const QgsMapUnitScale &scale)
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
QgsUnitTypes::RenderUnit mWidthUnit
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
void setIntervalUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the interval between markers.
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units 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.
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbol.h:628
SimplifyHints simplifyHints() const
Gets the simplification hints of the vector layer managed.
SymbolType type() const
Returns the symbol&#39;s type.
Definition: qgssymbol.h:120
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)
void setCustomDashVector(const QVector< qreal > &vector)
QVector< qreal > mCustomDashVector
Vector with an even number of entries for the.
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
QgsUnitTypes::RenderUnit mCustomDashPatternUnit
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)
QgsMapUnitScale mCustomDashPatternMapUnitScale
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
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)
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units for the line&#39;s offset.
bool selected() const
Definition: qgssymbol.h:611
void setOffsetAlongLine(double offsetAlongLine)
Sets the the offset along the line for the marker placement.
double offset() const
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
static QString encodePenCapStyle(Qt::PenCapStyle style)
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
Qt::PenCapStyle penCapStyle() const
static QgsSymbolLayer * createFromSld(QDomElement &element)
Create a new MarkerLineSymbolLayerV2 from SLD.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
QVector< qreal > dxfCustomDashPattern(QgsUnitTypes::RenderUnit &unit) const override
Gets dash pattern.
QgsMapUnitScale mapUnitScale() const override
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.
virtual QColor color() const
The fill color.
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.
QColor dxfColor(QgsSymbolRenderContext &context) const override
Gets color.
Qt::PenStyle dxfPenStyle() const override
Gets pen style.
#define DEFAULT_MARKERLINE_ROTATE
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
QgsPropertyCollection mDataDefinedProperties
QColor color() const override
The fill color.
QgsMapUnitScale mapUnitScale() const override
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:110
static QColor decodeColor(const QString &str)
virtual void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the layer.
void setPenJoinStyle(Qt::PenJoinStyle style)
void setOffset(double offset)
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
virtual QString layerType() const =0
Returns a string that represents this layer type.
double x
Definition: qgspoint.h:41