QGIS API Documentation  3.4.3-Madeira (2f64a3c)
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 && !qgsDoubleNear( scaledWidth, 0 ) )
198  {
199  mPen.setStyle( Qt::CustomDashLine );
200 
201  //scale pattern vector
202  double dashWidthDiv = scaledWidth;
203  //fix dash pattern width in Qt 4.8
204  QStringList versionSplit = QString( qVersion() ).split( '.' );
205  if ( versionSplit.size() > 1
206  && versionSplit.at( 1 ).toInt() >= 8
207  && scaledWidth < 1.0 )
208  {
209  dashWidthDiv = 1.0;
210  }
211  QVector<qreal> scaledVector;
212  QVector<qreal>::const_iterator it = mCustomDashVector.constBegin();
213  for ( ; it != mCustomDashVector.constEnd(); ++it )
214  {
215  //the dash is specified in terms of pen widths, therefore the division
216  scaledVector << context.renderContext().convertToPainterUnits( ( *it ), mCustomDashPatternUnit, mCustomDashPatternMapUnitScale ) / dashWidthDiv;
217  }
218  mPen.setDashPattern( scaledVector );
219  }
220  else
221  {
222  mPen.setStyle( mPenStyle );
223  }
224  mPen.setJoinStyle( mPenJoinStyle );
225  mPen.setCapStyle( mPenCapStyle );
226 
227  mSelPen = mPen;
228  QColor selColor = context.renderContext().selectionColor();
229  if ( ! SELECTION_IS_OPAQUE )
230  selColor.setAlphaF( context.opacity() );
231  mSelPen.setColor( selColor );
232 }
233 
235 {
236  Q_UNUSED( context );
237 }
238 
239 void QgsSimpleLineSymbolLayer::renderPolygonStroke( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
240 {
241  QPainter *p = context.renderContext().painter();
242  if ( !p )
243  {
244  return;
245  }
246 
247  if ( mDrawInsidePolygon )
248  p->save();
249 
250  switch ( mRingFilter )
251  {
252  case AllRings:
253  case ExteriorRingOnly:
254  {
255  if ( mDrawInsidePolygon )
256  {
257  //only drawing the line on the interior of the polygon, so set clip path for painter
258  QPainterPath clipPath;
259  clipPath.addPolygon( points );
260 
261  if ( rings )
262  {
263  //add polygon rings
264  QList<QPolygonF>::const_iterator it = rings->constBegin();
265  for ( ; it != rings->constEnd(); ++it )
266  {
267  QPolygonF ring = *it;
268  clipPath.addPolygon( ring );
269  }
270  }
271 
272  //use intersect mode, as a clip path may already exist (e.g., for composer maps)
273  p->setClipPath( clipPath, Qt::IntersectClip );
274  }
275 
276  renderPolyline( points, context );
277  }
278  break;
279 
280  case InteriorRingsOnly:
281  break;
282  }
283 
284  if ( rings )
285  {
286  switch ( mRingFilter )
287  {
288  case AllRings:
289  case InteriorRingsOnly:
290  {
291  mOffset = -mOffset; // invert the offset for rings!
292  for ( const QPolygonF &ring : qgis::as_const( *rings ) )
293  renderPolyline( ring, context );
294  mOffset = -mOffset;
295  }
296  break;
297  case ExteriorRingOnly:
298  break;
299  }
300  }
301 
302  if ( mDrawInsidePolygon )
303  {
304  //restore painter to reset clip path
305  p->restore();
306  }
307 
308 }
309 
310 void QgsSimpleLineSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context )
311 {
312  QPainter *p = context.renderContext().painter();
313  if ( !p )
314  {
315  return;
316  }
317 
318  double offset = mOffset;
319  applyDataDefinedSymbology( context, mPen, mSelPen, offset );
320 
321  p->setPen( context.selected() ? mSelPen : mPen );
322  p->setBrush( Qt::NoBrush );
323 
324  // Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #2 points).
325  if ( points.size() <= 2 &&
328  ( p->renderHints() & QPainter::Antialiasing ) )
329  {
330  p->setRenderHint( QPainter::Antialiasing, false );
331 #if 0
332  p->drawPolyline( points );
333 #else
334  QPainterPath path;
335  path.addPolygon( points );
336  p->drawPath( path );
337 #endif
338  p->setRenderHint( QPainter::Antialiasing, true );
339  return;
340  }
341 
342  if ( qgsDoubleNear( offset, 0 ) )
343  {
344 #if 0
345  p->drawPolyline( points );
346 #else
347  QPainterPath path;
348  path.addPolygon( points );
349  p->drawPath( path );
350 #endif
351  }
352  else
353  {
354  double scaledOffset = context.renderContext().convertToPainterUnits( offset, mOffsetUnit, mOffsetMapUnitScale );
355  QList<QPolygonF> mline = ::offsetLine( points, scaledOffset, context.originalGeometryType() != QgsWkbTypes::UnknownGeometry ? context.originalGeometryType() : QgsWkbTypes::LineGeometry );
356  for ( int part = 0; part < mline.count(); ++part )
357  {
358 #if 0
359  p->drawPolyline( mline );
360 #else
361  QPainterPath path;
362  path.addPolygon( mline[ part ] );
363  p->drawPath( path );
364 #endif
365  }
366  }
367 }
368 
370 {
371  QgsStringMap map;
372  map[QStringLiteral( "line_color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
373  map[QStringLiteral( "line_width" )] = QString::number( mWidth );
374  map[QStringLiteral( "line_width_unit" )] = QgsUnitTypes::encodeUnit( mWidthUnit );
375  map[QStringLiteral( "width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mWidthMapUnitScale );
376  map[QStringLiteral( "line_style" )] = QgsSymbolLayerUtils::encodePenStyle( mPenStyle );
377  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
378  map[QStringLiteral( "capstyle" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
379  map[QStringLiteral( "offset" )] = QString::number( mOffset );
380  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
381  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
382  map[QStringLiteral( "use_custom_dash" )] = ( mUseCustomDashPattern ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
383  map[QStringLiteral( "customdash" )] = QgsSymbolLayerUtils::encodeRealVector( mCustomDashVector );
384  map[QStringLiteral( "customdash_unit" )] = QgsUnitTypes::encodeUnit( mCustomDashPatternUnit );
385  map[QStringLiteral( "customdash_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mCustomDashPatternMapUnitScale );
386  map[QStringLiteral( "draw_inside_polygon" )] = ( mDrawInsidePolygon ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
387  map[QStringLiteral( "ring_filter" )] = QString::number( static_cast< int >( mRingFilter ) );
388  return map;
389 }
390 
392 {
394  l->setWidthUnit( mWidthUnit );
400  l->setOffset( mOffset );
408  copyPaintEffect( l );
409  return l;
410 }
411 
412 void QgsSimpleLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
413 {
414  if ( mPenStyle == Qt::NoPen )
415  return;
416 
417  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:LineSymbolizer" ) );
418  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
419  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
420  element.appendChild( symbolizerElem );
421 
422  // <Geometry>
423  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
424 
425  // <Stroke>
426  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
427  symbolizerElem.appendChild( strokeElem );
428 
429  Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
432  QgsSymbolLayerUtils::lineToSld( doc, strokeElem, penStyle, mColor, width,
433  &mPenJoinStyle, &mPenCapStyle, &customDashVector );
434 
435  // <se:PerpendicularOffset>
436  if ( !qgsDoubleNear( mOffset, 0.0 ) )
437  {
438  QDomElement perpOffsetElem = doc.createElement( QStringLiteral( "se:PerpendicularOffset" ) );
440  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
441  symbolizerElem.appendChild( perpOffsetElem );
442  }
443 }
444 
445 QString QgsSimpleLineSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
446 {
447  if ( mUseCustomDashPattern )
448  {
449  return QgsSymbolLayerUtils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor,
450  mPen.color(), mPenJoinStyle,
452  }
453  else
454  {
455  return QgsSymbolLayerUtils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor, mPen.color(), mPenJoinStyle,
457  }
458 }
459 
461 {
462  QgsDebugMsg( QStringLiteral( "Entered." ) );
463 
464  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
465  if ( strokeElem.isNull() )
466  return nullptr;
467 
468  Qt::PenStyle penStyle;
469  QColor color;
470  double width;
471  Qt::PenJoinStyle penJoinStyle;
472  Qt::PenCapStyle penCapStyle;
473  QVector<qreal> customDashVector;
474 
475  if ( !QgsSymbolLayerUtils::lineFromSld( strokeElem, penStyle,
476  color, width,
477  &penJoinStyle, &penCapStyle,
478  &customDashVector ) )
479  return nullptr;
480 
481  double offset = 0.0;
482  QDomElement perpOffsetElem = element.firstChildElement( QStringLiteral( "PerpendicularOffset" ) );
483  if ( !perpOffsetElem.isNull() )
484  {
485  bool ok;
486  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
487  if ( ok )
488  offset = d;
489  }
490 
491  QString uom = element.attribute( QStringLiteral( "uom" ) );
492  width = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, width );
493  offset = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset );
494 
495  QgsSimpleLineSymbolLayer *l = new QgsSimpleLineSymbolLayer( color, width, penStyle );
496  l->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
497  l->setOffset( offset );
498  l->setPenJoinStyle( penJoinStyle );
499  l->setPenCapStyle( penCapStyle );
500  l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine );
501  l->setCustomDashVector( customDashVector );
502  return l;
503 }
504 
505 void QgsSimpleLineSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, QPen &pen, QPen &selPen, double &offset )
506 {
507  if ( !dataDefinedProperties().hasActiveProperties() )
508  return; // shortcut
509 
510  //data defined properties
511  bool hasStrokeWidthExpression = false;
513  {
514  context.setOriginalValueVariable( mWidth );
515  double scaledWidth = context.renderContext().convertToPainterUnits(
518  pen.setWidthF( scaledWidth );
519  selPen.setWidthF( scaledWidth );
520  hasStrokeWidthExpression = true;
521  }
522 
523  //color
525  {
528  }
529 
530  //offset
532  {
535  }
536 
537  //dash dot vector
539  {
540  double scaledWidth = context.renderContext().convertToPainterUnits( mWidth, mWidthUnit, mWidthMapUnitScale );
541  double dashWidthDiv = mPen.widthF();
542 
543  if ( hasStrokeWidthExpression )
544  {
545  dashWidthDiv = pen.widthF();
546  scaledWidth = pen.widthF();
547  }
548 
549  //fix dash pattern width in Qt 4.8
550  QStringList versionSplit = QString( qVersion() ).split( '.' );
551  if ( versionSplit.size() > 1
552  && versionSplit.at( 1 ).toInt() >= 8
553  && scaledWidth < 1.0 )
554  {
555  dashWidthDiv = 1.0;
556  }
557 
558  QVector<qreal> dashVector;
560  if ( exprVal.isValid() )
561  {
562  QStringList dashList = exprVal.toString().split( ';' );
563  QStringList::const_iterator dashIt = dashList.constBegin();
564  for ( ; dashIt != dashList.constEnd(); ++dashIt )
565  {
566  dashVector.push_back( context.renderContext().convertToPainterUnits( dashIt->toDouble(), mCustomDashPatternUnit, mCustomDashPatternMapUnitScale ) / dashWidthDiv );
567  }
568  pen.setDashPattern( dashVector );
569  }
570  }
571 
572  //line style
574  {
577  if ( exprVal.isValid() )
578  pen.setStyle( QgsSymbolLayerUtils::decodePenStyle( exprVal.toString() ) );
579  }
580 
581  //join style
583  {
586  if ( exprVal.isValid() )
587  pen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( exprVal.toString() ) );
588  }
589 
590  //cap style
592  {
595  if ( exprVal.isValid() )
596  pen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( exprVal.toString() ) );
597  }
598 }
599 
601 {
602  if ( mDrawInsidePolygon )
603  {
604  //set to clip line to the interior of polygon, so we expect no bleed
605  return 0;
606  }
607  else
608  {
609  return context.convertToPainterUnits( ( mWidth / 2.0 ), mWidthUnit, mWidthMapUnitScale ) +
611  }
612 }
613 
615 {
616  unit = mCustomDashPatternUnit;
617  return mUseCustomDashPattern ? mCustomDashVector : QVector<qreal>();
618 }
619 
621 {
622  return mPenStyle;
623 }
624 
626 {
627  double width = mWidth;
629  {
630  context.setOriginalValueVariable( mWidth );
632  }
633 
636  {
638  }
639  return width;
640 }
641 
643 {
645  {
648  }
649  return mColor;
650 }
651 
653 {
654  Q_UNUSED( e );
655  double offset = mOffset;
656 
658  {
661  }
662 
665  {
667  }
668  return -offset; //direction seems to be inverse to symbology offset
669 }
670 
672 
674 
675 class MyLine
676 {
677  public:
678  MyLine( QPointF p1, QPointF p2 )
679  : mVertical( false )
680  , mIncreasing( false )
681  , mT( 0.0 )
682  , mLength( 0.0 )
683  {
684  if ( p1 == p2 )
685  return; // invalid
686 
687  // tangent and direction
688  if ( qgsDoubleNear( p1.x(), p2.x() ) )
689  {
690  // vertical line - tangent undefined
691  mVertical = true;
692  mIncreasing = ( p2.y() > p1.y() );
693  }
694  else
695  {
696  mVertical = false;
697  mT = float( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
698  mIncreasing = ( p2.x() > p1.x() );
699  }
700 
701  // length
702  double x = ( p2.x() - p1.x() );
703  double y = ( p2.y() - p1.y() );
704  mLength = std::sqrt( x * x + y * y );
705  }
706 
707  // return angle in radians
708  double angle()
709  {
710  double a = ( mVertical ? M_PI_2 : std::atan( mT ) );
711 
712  if ( !mIncreasing )
713  a += M_PI;
714  return a;
715  }
716 
717  // return difference for x,y when going along the line with specified interval
718  QPointF diffForInterval( double interval )
719  {
720  if ( mVertical )
721  return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
722 
723  double alpha = std::atan( mT );
724  double dx = std::cos( alpha ) * interval;
725  double dy = std::sin( alpha ) * interval;
726  return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
727  }
728 
729  double length() { return mLength; }
730 
731  protected:
732  bool mVertical;
733  bool mIncreasing;
734  double mT;
735  double mLength;
736 };
737 
739 
740 QgsMarkerLineSymbolLayer::QgsMarkerLineSymbolLayer( bool rotateMarker, double interval )
741 {
742  mRotateMarker = rotateMarker;
743  mInterval = interval;
744  mIntervalUnit = QgsUnitTypes::RenderMillimeters;
745  mMarker = nullptr;
746  mPlacement = Interval;
747  mOffsetAlongLine = 0;
748  mOffsetAlongLineUnit = QgsUnitTypes::RenderMillimeters;
749 
750  setSubSymbol( new QgsMarkerSymbol() );
751 }
752 
754 {
755  bool rotate = DEFAULT_MARKERLINE_ROTATE;
756  double interval = DEFAULT_MARKERLINE_INTERVAL;
757 
758 
759  if ( props.contains( QStringLiteral( "interval" ) ) )
760  interval = props[QStringLiteral( "interval" )].toDouble();
761  if ( props.contains( QStringLiteral( "rotate" ) ) )
762  rotate = ( props[QStringLiteral( "rotate" )] == QLatin1String( "1" ) );
763 
764  QgsMarkerLineSymbolLayer *x = new QgsMarkerLineSymbolLayer( rotate, interval );
765  if ( props.contains( QStringLiteral( "offset" ) ) )
766  {
767  x->setOffset( props[QStringLiteral( "offset" )].toDouble() );
768  }
769  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
770  {
771  x->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
772  }
773  if ( props.contains( QStringLiteral( "interval_unit" ) ) )
774  {
775  x->setIntervalUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "interval_unit" )] ) );
776  }
777  if ( props.contains( QStringLiteral( "offset_along_line" ) ) )
778  {
779  x->setOffsetAlongLine( props[QStringLiteral( "offset_along_line" )].toDouble() );
780  }
781  if ( props.contains( QStringLiteral( "offset_along_line_unit" ) ) )
782  {
783  x->setOffsetAlongLineUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_along_line_unit" )] ) );
784  }
785  if ( props.contains( ( QStringLiteral( "offset_along_line_map_unit_scale" ) ) ) )
786  {
787  x->setOffsetAlongLineMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_along_line_map_unit_scale" )] ) );
788  }
789 
790  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
791  {
792  x->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
793  }
794  if ( props.contains( QStringLiteral( "interval_map_unit_scale" ) ) )
795  {
796  x->setIntervalMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "interval_map_unit_scale" )] ) );
797  }
798 
799  if ( props.contains( QStringLiteral( "placement" ) ) )
800  {
801  if ( props[QStringLiteral( "placement" )] == QLatin1String( "vertex" ) )
802  x->setPlacement( Vertex );
803  else if ( props[QStringLiteral( "placement" )] == QLatin1String( "lastvertex" ) )
804  x->setPlacement( LastVertex );
805  else if ( props[QStringLiteral( "placement" )] == QLatin1String( "firstvertex" ) )
806  x->setPlacement( FirstVertex );
807  else if ( props[QStringLiteral( "placement" )] == QLatin1String( "centralpoint" ) )
808  x->setPlacement( CentralPoint );
809  else if ( props[QStringLiteral( "placement" )] == QLatin1String( "curvepoint" ) )
810  x->setPlacement( CurvePoint );
811  else
812  x->setPlacement( Interval );
813  }
814 
815  if ( props.contains( QStringLiteral( "ring_filter" ) ) )
816  {
817  x->setRingFilter( static_cast< RenderRingFilter>( props[QStringLiteral( "ring_filter" )].toInt() ) );
818  }
819 
821 
822  return x;
823 }
824 
826 {
827  return QStringLiteral( "MarkerLine" );
828 }
829 
831 {
832  mMarker->setColor( color );
833  mColor = color;
834 }
835 
837 {
838  return mMarker ? mMarker->color() : mColor;
839 }
840 
842 {
843  mMarker->setOpacity( context.opacity() );
844 
845  // if being rotated, it gets initialized with every line segment
846  QgsSymbol::RenderHints hints = nullptr;
847  if ( mRotateMarker )
849  mMarker->setRenderHints( hints );
850 
851  mMarker->startRender( context.renderContext(), context.fields() );
852 }
853 
855 {
856  mMarker->stopRender( context.renderContext() );
857 }
858 
859 void QgsMarkerLineSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context )
860 {
861  double offset = mOffset;
862 
864  {
867  }
868 
869  Placement placement = mPlacement;
870 
872  {
874  if ( exprVal.isValid() )
875  {
876  QString placementString = exprVal.toString();
877  if ( placementString.compare( QLatin1String( "interval" ), Qt::CaseInsensitive ) == 0 )
878  {
879  placement = Interval;
880  }
881  else if ( placementString.compare( QLatin1String( "vertex" ), Qt::CaseInsensitive ) == 0 )
882  {
883  placement = Vertex;
884  }
885  else if ( placementString.compare( QLatin1String( "lastvertex" ), Qt::CaseInsensitive ) == 0 )
886  {
887  placement = LastVertex;
888  }
889  else if ( placementString.compare( QLatin1String( "firstvertex" ), Qt::CaseInsensitive ) == 0 )
890  {
891  placement = FirstVertex;
892  }
893  else if ( placementString.compare( QLatin1String( "centerpoint" ), Qt::CaseInsensitive ) == 0 )
894  {
895  placement = CentralPoint;
896  }
897  else if ( placementString.compare( QLatin1String( "curvepoint" ), Qt::CaseInsensitive ) == 0 )
898  {
899  placement = CurvePoint;
900  }
901  else
902  {
903  placement = Interval;
904  }
905  }
906  }
907 
908 
909  context.renderContext().painter()->save();
910 
911  if ( qgsDoubleNear( offset, 0.0 ) )
912  {
913  if ( placement == Interval )
914  renderPolylineInterval( points, context );
915  else if ( placement == CentralPoint )
916  renderPolylineCentral( points, context );
917  else
918  renderPolylineVertex( points, context, placement );
919  }
920  else
921  {
922  context.renderContext().setGeometry( nullptr ); //always use segmented geometry with offset
924 
925  for ( int part = 0; part < mline.count(); ++part )
926  {
927  const QPolygonF &points2 = mline[ part ];
928 
929  if ( placement == Interval )
930  renderPolylineInterval( points2, context );
931  else if ( placement == CentralPoint )
932  renderPolylineCentral( points2, context );
933  else
934  renderPolylineVertex( points2, context, placement );
935  }
936  }
937 
938  context.renderContext().painter()->restore();
939 }
940 
941 void QgsMarkerLineSymbolLayer::renderPolygonStroke( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
942 {
943  const QgsCurvePolygon *curvePolygon = dynamic_cast<const QgsCurvePolygon *>( context.renderContext().geometry() );
944 
945  if ( curvePolygon )
946  {
947  context.renderContext().setGeometry( curvePolygon->exteriorRing() );
948  }
949 
950  switch ( mRingFilter )
951  {
952  case AllRings:
953  case ExteriorRingOnly:
954  renderPolyline( points, context );
955  break;
956  case InteriorRingsOnly:
957  break;
958  }
959 
960  if ( rings )
961  {
962  switch ( mRingFilter )
963  {
964  case AllRings:
965  case InteriorRingsOnly:
966  {
967  mOffset = -mOffset; // invert the offset for rings!
968  for ( int i = 0; i < rings->size(); ++i )
969  {
970  if ( curvePolygon )
971  {
972  context.renderContext().setGeometry( curvePolygon->interiorRing( i ) );
973  }
974  renderPolyline( rings->at( i ), context );
975  }
976  mOffset = -mOffset;
977  }
978  break;
979  case ExteriorRingOnly:
980  break;
981  }
982  }
983 }
984 
986 {
987  if ( points.isEmpty() )
988  return;
989 
990  QPointF lastPt = points[0];
991  double lengthLeft = 0; // how much is left until next marker
992 
993  QgsRenderContext &rc = context.renderContext();
994  double interval = mInterval;
995 
997  context.renderContext().expressionContext().appendScope( scope );
998 
1000  {
1001  context.setOriginalValueVariable( mInterval );
1003  }
1004  if ( interval <= 0 )
1005  {
1006  interval = 0.1;
1007  }
1008  double offsetAlongLine = mOffsetAlongLine;
1010  {
1011  context.setOriginalValueVariable( mOffsetAlongLine );
1013  }
1014 
1015  double painterUnitInterval = rc.convertToPainterUnits( interval, mIntervalUnit, mIntervalMapUnitScale );
1016  lengthLeft = painterUnitInterval - rc.convertToPainterUnits( offsetAlongLine, mIntervalUnit, mIntervalMapUnitScale );
1017 
1018  int pointNum = 0;
1019  for ( int i = 1; i < points.count(); ++i )
1020  {
1021  const QPointF &pt = points[i];
1022 
1023  if ( lastPt == pt ) // must not be equal!
1024  continue;
1025 
1026  // for each line, find out dx and dy, and length
1027  MyLine l( lastPt, pt );
1028  QPointF diff = l.diffForInterval( painterUnitInterval );
1029 
1030  // if there's some length left from previous line
1031  // use only the rest for the first point in new line segment
1032  double c = 1 - lengthLeft / painterUnitInterval;
1033 
1034  lengthLeft += l.length();
1035 
1036  // rotate marker (if desired)
1037  if ( mRotateMarker )
1038  {
1039  mMarker->setLineAngle( l.angle() * 180 / M_PI );
1040  }
1041 
1042 
1043  // while we're not at the end of line segment, draw!
1044  while ( lengthLeft > painterUnitInterval )
1045  {
1046  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
1047  lastPt += c * diff;
1048  lengthLeft -= painterUnitInterval;
1050  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
1051  c = 1; // reset c (if wasn't 1 already)
1052  }
1053 
1054  lastPt = pt;
1055  }
1056 
1057  delete context.renderContext().expressionContext().popScope();
1058 }
1059 
1060 static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt )
1061 {
1062  // calc average angle between the previous and next point
1063  double a1 = MyLine( prevPt, pt ).angle();
1064  double a2 = MyLine( pt, nextPt ).angle();
1065  double unitX = std::cos( a1 ) + std::cos( a2 ), unitY = std::sin( a1 ) + std::sin( a2 );
1066 
1067  return std::atan2( unitY, unitX );
1068 }
1069 
1070 void QgsMarkerLineSymbolLayer::renderPolylineVertex( const QPolygonF &points, QgsSymbolRenderContext &context, Placement placement )
1071 {
1072  if ( points.isEmpty() )
1073  return;
1074 
1075  QgsRenderContext &rc = context.renderContext();
1076 
1077  double origAngle = mMarker->angle();
1078  int i, maxCount;
1079  bool isRing = false;
1080 
1082  context.renderContext().expressionContext().appendScope( scope );
1084 
1085  double offsetAlongLine = mOffsetAlongLine;
1087  {
1088  context.setOriginalValueVariable( mOffsetAlongLine );
1090  }
1091  if ( !qgsDoubleNear( offsetAlongLine, 0.0 ) )
1092  {
1093  //scale offset along line
1094  offsetAlongLine = rc.convertToPainterUnits( offsetAlongLine, mOffsetAlongLineUnit, mOffsetAlongLineMapUnitScale );
1095  }
1096 
1097  if ( qgsDoubleNear( offsetAlongLine, 0.0 ) && context.renderContext().geometry()
1098  && context.renderContext().geometry()->hasCurvedSegments() && ( placement == Vertex || placement == CurvePoint ) )
1099  {
1101  const QgsMapToPixel &mtp = context.renderContext().mapToPixel();
1102 
1103  QgsVertexId vId;
1104  QgsPoint vPoint;
1105  double x, y, z;
1106  QPointF mapPoint;
1107  int pointNum = 0;
1108  while ( context.renderContext().geometry()->nextVertex( vId, vPoint ) )
1109  {
1111 
1112  if ( ( placement == Vertex && vId.type == QgsVertexId::SegmentVertex )
1113  || ( placement == CurvePoint && vId.type == QgsVertexId::CurveVertex ) )
1114  {
1115  //transform
1116  x = vPoint.x(), y = vPoint.y();
1117  z = 0.0;
1118  if ( ct.isValid() )
1119  {
1120  ct.transformInPlace( x, y, z );
1121  }
1122  mapPoint.setX( x );
1123  mapPoint.setY( y );
1124  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1125  if ( mRotateMarker )
1126  {
1127  double angle = context.renderContext().geometry()->vertexAngle( vId );
1128  mMarker->setAngle( angle * 180 / M_PI );
1129  }
1130  mMarker->renderPoint( mapPoint, context.feature(), rc, -1, context.selected() );
1131  }
1132  }
1133 
1134  delete context.renderContext().expressionContext().popScope();
1135  return;
1136  }
1137 
1138  if ( placement == FirstVertex )
1139  {
1140  i = 0;
1141  maxCount = 1;
1142  }
1143  else if ( placement == LastVertex )
1144  {
1145  i = points.count() - 1;
1146  maxCount = points.count();
1147  }
1148  else if ( placement == Vertex )
1149  {
1150  i = 0;
1151  maxCount = points.count();
1152  if ( points.first() == points.last() )
1153  isRing = true;
1154  }
1155  else
1156  {
1157  delete context.renderContext().expressionContext().popScope();
1158  return;
1159  }
1160 
1161  if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
1162  {
1163  double distance;
1164  distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine;
1165  renderOffsetVertexAlongLine( points, i, distance, context );
1166  // restore original rotation
1167  mMarker->setAngle( origAngle );
1168 
1169  delete context.renderContext().expressionContext().popScope();
1170  return;
1171  }
1172 
1173  int pointNum = 0;
1174  for ( ; i < maxCount; ++i )
1175  {
1177 
1178  if ( isRing && placement == Vertex && i == points.count() - 1 )
1179  {
1180  continue; // don't draw the last marker - it has been drawn already
1181  }
1182  // rotate marker (if desired)
1183  if ( mRotateMarker )
1184  {
1185  double angle = markerAngle( points, isRing, i );
1186  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1187  }
1188 
1189  mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() );
1190  }
1191 
1192  // restore original rotation
1193  mMarker->setAngle( origAngle );
1194 
1195  delete context.renderContext().expressionContext().popScope();
1196 }
1197 
1198 double QgsMarkerLineSymbolLayer::markerAngle( const QPolygonF &points, bool isRing, int vertex )
1199 {
1200  double angle = 0;
1201  const QPointF &pt = points[vertex];
1202 
1203  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1204  {
1205  int prevIndex = vertex - 1;
1206  int nextIndex = vertex + 1;
1207 
1208  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1209  {
1210  prevIndex = points.count() - 2;
1211  nextIndex = 1;
1212  }
1213 
1214  QPointF prevPoint, nextPoint;
1215  while ( prevIndex >= 0 )
1216  {
1217  prevPoint = points[ prevIndex ];
1218  if ( prevPoint != pt )
1219  {
1220  break;
1221  }
1222  --prevIndex;
1223  }
1224 
1225  while ( nextIndex < points.count() )
1226  {
1227  nextPoint = points[ nextIndex ];
1228  if ( nextPoint != pt )
1229  {
1230  break;
1231  }
1232  ++nextIndex;
1233  }
1234 
1235  if ( prevIndex >= 0 && nextIndex < points.count() )
1236  {
1237  angle = _averageAngle( prevPoint, pt, nextPoint );
1238  }
1239  }
1240  else //no ring and vertex is at start / at end
1241  {
1242  if ( vertex == 0 )
1243  {
1244  while ( vertex < points.size() - 1 )
1245  {
1246  const QPointF &nextPt = points[vertex + 1];
1247  if ( pt != nextPt )
1248  {
1249  angle = MyLine( pt, nextPt ).angle();
1250  return angle;
1251  }
1252  ++vertex;
1253  }
1254  }
1255  else
1256  {
1257  // use last segment's angle
1258  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1259  {
1260  const QPointF &prevPt = points[vertex - 1];
1261  if ( pt != prevPt )
1262  {
1263  angle = MyLine( prevPt, pt ).angle();
1264  return angle;
1265  }
1266  --vertex;
1267  }
1268  }
1269  }
1270  return angle;
1271 }
1272 
1273 void QgsMarkerLineSymbolLayer::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolRenderContext &context )
1274 {
1275  if ( points.isEmpty() )
1276  return;
1277 
1278  QgsRenderContext &rc = context.renderContext();
1279  double origAngle = mMarker->angle();
1280  if ( qgsDoubleNear( distance, 0.0 ) )
1281  {
1282  // rotate marker (if desired)
1283  if ( mRotateMarker )
1284  {
1285  bool isRing = false;
1286  if ( points.first() == points.last() )
1287  isRing = true;
1288  double angle = markerAngle( points, isRing, vertex );
1289  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1290  }
1291  mMarker->renderPoint( points[vertex], context.feature(), rc, -1, context.selected() );
1292  return;
1293  }
1294 
1295  int pointIncrement = distance > 0 ? 1 : -1;
1296  QPointF previousPoint = points[vertex];
1297  int startPoint = distance > 0 ? std::min( vertex + 1, points.count() - 1 ) : std::max( vertex - 1, 0 );
1298  int endPoint = distance > 0 ? points.count() - 1 : 0;
1299  double distanceLeft = std::fabs( distance );
1300 
1301  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1302  {
1303  const QPointF &pt = points[i];
1304 
1305  if ( previousPoint == pt ) // must not be equal!
1306  continue;
1307 
1308  // create line segment
1309  MyLine l( previousPoint, pt );
1310 
1311  if ( distanceLeft < l.length() )
1312  {
1313  //destination point is in current segment
1314  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1315  // rotate marker (if desired)
1316  if ( mRotateMarker )
1317  {
1318  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1319  }
1320  mMarker->renderPoint( markerPoint, context.feature(), rc, -1, context.selected() );
1321  return;
1322  }
1323 
1324  distanceLeft -= l.length();
1325  previousPoint = pt;
1326  }
1327 
1328  //didn't find point
1329 }
1330 
1332 {
1333  if ( !points.isEmpty() )
1334  {
1335  // calc length
1336  qreal length = 0;
1337  QPolygonF::const_iterator it = points.constBegin();
1338  QPointF last = *it;
1339  for ( ++it; it != points.constEnd(); ++it )
1340  {
1341  length += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
1342  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1343  last = *it;
1344  }
1345 
1346  // find the segment where the central point lies
1347  it = points.constBegin();
1348  last = *it;
1349  qreal last_at = 0, next_at = 0;
1350  QPointF next;
1351  int segment = 0;
1352  for ( ++it; it != points.constEnd(); ++it )
1353  {
1354  next = *it;
1355  next_at += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
1356  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1357  if ( next_at >= length / 2 )
1358  break; // we have reached the center
1359  last = *it;
1360  last_at = next_at;
1361  segment++;
1362  }
1363 
1364  // find out the central point on segment
1365  MyLine l( last, next ); // for line angle
1366  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1367  QPointF pt = last + ( next - last ) * k;
1368 
1369  // draw the marker
1370  double origAngle = mMarker->angle();
1371  if ( mRotateMarker )
1372  mMarker->setAngle( origAngle + l.angle() * 180 / M_PI );
1373  mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() );
1374  if ( mRotateMarker )
1375  mMarker->setAngle( origAngle );
1376  }
1377 }
1378 
1379 
1381 {
1382  QgsStringMap map;
1383  map[QStringLiteral( "rotate" )] = ( mRotateMarker ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
1384  map[QStringLiteral( "interval" )] = QString::number( mInterval );
1385  map[QStringLiteral( "offset" )] = QString::number( mOffset );
1386  map[QStringLiteral( "offset_along_line" )] = QString::number( mOffsetAlongLine );
1387  map[QStringLiteral( "offset_along_line_unit" )] = QgsUnitTypes::encodeUnit( mOffsetAlongLineUnit );
1388  map[QStringLiteral( "offset_along_line_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetAlongLineMapUnitScale );
1389  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1390  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1391  map[QStringLiteral( "interval_unit" )] = QgsUnitTypes::encodeUnit( mIntervalUnit );
1392  map[QStringLiteral( "interval_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mIntervalMapUnitScale );
1393  if ( mPlacement == Vertex )
1394  map[QStringLiteral( "placement" )] = QStringLiteral( "vertex" );
1395  else if ( mPlacement == LastVertex )
1396  map[QStringLiteral( "placement" )] = QStringLiteral( "lastvertex" );
1397  else if ( mPlacement == FirstVertex )
1398  map[QStringLiteral( "placement" )] = QStringLiteral( "firstvertex" );
1399  else if ( mPlacement == CentralPoint )
1400  map[QStringLiteral( "placement" )] = QStringLiteral( "centralpoint" );
1401  else if ( mPlacement == CurvePoint )
1402  map[QStringLiteral( "placement" )] = QStringLiteral( "curvepoint" );
1403  else
1404  map[QStringLiteral( "placement" )] = QStringLiteral( "interval" );
1405 
1406  map[QStringLiteral( "ring_filter" )] = QString::number( static_cast< int >( mRingFilter ) );
1407  return map;
1408 }
1409 
1411 {
1412  return mMarker.get();
1413 }
1414 
1416 {
1417  if ( !symbol || symbol->type() != QgsSymbol::Marker )
1418  {
1419  delete symbol;
1420  return false;
1421  }
1422 
1423  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
1424  mColor = mMarker->color();
1425  return true;
1426 }
1427 
1429 {
1430  QgsMarkerLineSymbolLayer *x = new QgsMarkerLineSymbolLayer( mRotateMarker, mInterval );
1431  x->setSubSymbol( mMarker->clone() );
1432  x->setOffset( mOffset );
1433  x->setPlacement( mPlacement );
1434  x->setOffsetUnit( mOffsetUnit );
1436  x->setIntervalUnit( mIntervalUnit );
1437  x->setIntervalMapUnitScale( mIntervalMapUnitScale );
1438  x->setOffsetAlongLine( mOffsetAlongLine );
1439  x->setOffsetAlongLineMapUnitScale( mOffsetAlongLineMapUnitScale );
1440  x->setOffsetAlongLineUnit( mOffsetAlongLineUnit );
1441  x->setRingFilter( mRingFilter );
1443  copyPaintEffect( x );
1444  return x;
1445 }
1446 
1447 void QgsMarkerLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
1448 {
1449  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1450  {
1451  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:LineSymbolizer" ) );
1452  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
1453  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
1454  element.appendChild( symbolizerElem );
1455 
1456  // <Geometry>
1457  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
1458 
1459  QString gap;
1460  switch ( mPlacement )
1461  {
1462  case FirstVertex:
1463  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "firstPoint" ) ) );
1464  break;
1465  case LastVertex:
1466  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "lastPoint" ) ) );
1467  break;
1468  case CentralPoint:
1469  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "centralPoint" ) ) );
1470  break;
1471  case Vertex:
1472  // no way to get line/polygon's vertices, use a VendorOption
1473  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "points" ) ) );
1474  break;
1475  default:
1476  double interval = QgsSymbolLayerUtils::rescaleUom( mInterval, mIntervalUnit, props );
1477  gap = qgsDoubleToString( interval );
1478  break;
1479  }
1480 
1481  if ( !mRotateMarker )
1482  {
1483  // markers in LineSymbolizer must be drawn following the line orientation,
1484  // use a VendorOption when no marker rotation
1485  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "rotateMarker" ), QStringLiteral( "0" ) ) );
1486  }
1487 
1488  // <Stroke>
1489  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
1490  symbolizerElem.appendChild( strokeElem );
1491 
1492  // <GraphicStroke>
1493  QDomElement graphicStrokeElem = doc.createElement( QStringLiteral( "se:GraphicStroke" ) );
1494  strokeElem.appendChild( graphicStrokeElem );
1495 
1496  QgsSymbolLayer *layer = mMarker->symbolLayer( i );
1497  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
1498  if ( !markerLayer )
1499  {
1500  graphicStrokeElem.appendChild( doc.createComment( QStringLiteral( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() ) ) );
1501  }
1502  else
1503  {
1504  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1505  }
1506 
1507  if ( !gap.isEmpty() )
1508  {
1509  QDomElement gapElem = doc.createElement( QStringLiteral( "se:Gap" ) );
1510  QgsSymbolLayerUtils::createExpressionElement( doc, gapElem, gap );
1511  graphicStrokeElem.appendChild( gapElem );
1512  }
1513 
1514  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1515  {
1516  QDomElement perpOffsetElem = doc.createElement( QStringLiteral( "se:PerpendicularOffset" ) );
1518  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
1519  symbolizerElem.appendChild( perpOffsetElem );
1520  }
1521  }
1522 }
1523 
1525 {
1526  QgsDebugMsg( QStringLiteral( "Entered." ) );
1527 
1528  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
1529  if ( strokeElem.isNull() )
1530  return nullptr;
1531 
1532  QDomElement graphicStrokeElem = strokeElem.firstChildElement( QStringLiteral( "GraphicStroke" ) );
1533  if ( graphicStrokeElem.isNull() )
1534  return nullptr;
1535 
1536  // retrieve vendor options
1537  bool rotateMarker = true;
1538  Placement placement = Interval;
1539 
1540  QgsStringMap vendorOptions = QgsSymbolLayerUtils::getVendorOptionList( element );
1541  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1542  {
1543  if ( it.key() == QLatin1String( "placement" ) )
1544  {
1545  if ( it.value() == QLatin1String( "points" ) ) placement = Vertex;
1546  else if ( it.value() == QLatin1String( "firstPoint" ) ) placement = FirstVertex;
1547  else if ( it.value() == QLatin1String( "lastPoint" ) ) placement = LastVertex;
1548  else if ( it.value() == QLatin1String( "centralPoint" ) ) placement = CentralPoint;
1549  }
1550  else if ( it.value() == QLatin1String( "rotateMarker" ) )
1551  {
1552  rotateMarker = it.value() == QLatin1String( "0" );
1553  }
1554  }
1555 
1556  std::unique_ptr< QgsMarkerSymbol > marker;
1557 
1559  if ( l )
1560  {
1561  QgsSymbolLayerList layers;
1562  layers.append( l );
1563  marker.reset( new QgsMarkerSymbol( layers ) );
1564  }
1565 
1566  if ( !marker )
1567  return nullptr;
1568 
1569  double interval = 0.0;
1570  QDomElement gapElem = graphicStrokeElem.firstChildElement( QStringLiteral( "Gap" ) );
1571  if ( !gapElem.isNull() )
1572  {
1573  bool ok;
1574  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1575  if ( ok )
1576  interval = d;
1577  }
1578 
1579  double offset = 0.0;
1580  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( QStringLiteral( "PerpendicularOffset" ) );
1581  if ( !perpOffsetElem.isNull() )
1582  {
1583  bool ok;
1584  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1585  if ( ok )
1586  offset = d;
1587  }
1588 
1589  QString uom = element.attribute( QStringLiteral( "uom" ) );
1590  interval = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, interval );
1591  offset = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset );
1592 
1593  QgsMarkerLineSymbolLayer *x = new QgsMarkerLineSymbolLayer( rotateMarker );
1594  x->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1595  x->setPlacement( placement );
1596  x->setInterval( interval );
1597  x->setSubSymbol( marker.release() );
1598  x->setOffset( offset );
1599  return x;
1600 }
1601 
1603 {
1604  mMarker->setSize( width );
1605 }
1606 
1608 {
1609  if ( key == QgsSymbolLayer::PropertyWidth && mMarker && property )
1610  {
1611  mMarker->setDataDefinedSize( property );
1612  }
1614 }
1615 
1617 {
1618  return mMarker->size();
1619 }
1620 
1622 {
1624  mMarker->setOutputUnit( unit );
1625  mIntervalUnit = unit;
1626  mOffsetUnit = unit;
1627  mOffsetAlongLineUnit = unit;
1628 }
1629 
1631 {
1633  if ( mIntervalUnit != unit || mOffsetUnit != unit || mOffsetAlongLineUnit != unit )
1634  {
1636  }
1637  return unit;
1638 }
1639 
1641 {
1643  mIntervalMapUnitScale = scale;
1644  mOffsetMapUnitScale = scale;
1645  mOffsetAlongLineMapUnitScale = scale;
1646 }
1647 
1649 {
1650  if ( QgsLineSymbolLayer::mapUnitScale() == mIntervalMapUnitScale &&
1651  mIntervalMapUnitScale == mOffsetMapUnitScale &&
1652  mOffsetMapUnitScale == mOffsetAlongLineMapUnitScale )
1653  {
1654  return mOffsetMapUnitScale;
1655  }
1656  return QgsMapUnitScale();
1657 }
1658 
1659 QSet<QString> QgsMarkerLineSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
1660 {
1661  QSet<QString> attr = QgsLineSymbolLayer::usedAttributes( context );
1662  if ( mMarker )
1663  attr.unite( mMarker->usedAttributes( context ) );
1664  return attr;
1665 }
1666 
1668 {
1669  return context.convertToPainterUnits( ( mMarker->size() / 2.0 ), mMarker->sizeUnit(), mMarker->sizeMapUnitScale() ) +
1671 }
1672 
1673 
1674 
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.
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...
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
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:150
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
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
const QgsVectorSimplifyMethod & vectorSimplifyMethod() const
Added in QGIS v2.4.
QgsFields fields() const
Fields of the layer.
Definition: qgssymbol.h:653
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)
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
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.
const QgsCurve * interiorRing(int i) const
#define DEFAULT_MARKERLINE_INTERVAL
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
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::PenJoinStyle mPenJoinStyle
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units for the line&#39;s offset.
virtual bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const =0
Returns next vertex id and coordinates.
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:570
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.
virtual double width() const
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.
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
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:1097
static QString encodePenStyle(Qt::PenStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:36
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.
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.
void setPlacement(Placement p)
The placement of the markers.
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.
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
static QgsStringMap getVendorOptionList(QDomElement &element)
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:602
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: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...
#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
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.
float threshold() const
Gets the simplification threshold of the vector layer managed.
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)
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
Qt::PenJoinStyle penJoinStyle() const
double offset() const
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.
double mapUnitsPerPixel() const
Returns current map units per pixel.
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.
bool selected() const
Definition: qgssymbol.h:611
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
void setMapUnitScale(const QgsMapUnitScale &scale) override
double width() const override
void renderPolylineVertex(const QPolygonF &points, QgsSymbolRenderContext &context, Placement placement=Vertex)
void setOffsetAlongLineUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit used for calculating the offset along line for markers.
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:645
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...
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
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.
Contains information about the context of a rendering operation.
Abstract base class for marker symbol layers.
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.
QPainter * painter()
Returns the destination QPainter for the render operation.
const QgsMapToPixel & mapToPixel() const
double markerAngle(const QPolygonF &points, bool isRing, int vertex)
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)
void setWidthMapUnitScale(const QgsMapUnitScale &scale)
Qt::PenStyle penStyle() const
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
Current feature being rendered - may be null.
Definition: qgssymbol.h:628
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.
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)
void setCustomDashVector(const QVector< qreal > &vector)
QVector< qreal > mCustomDashVector
Vector with an even number of entries for the.
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
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 setOffsetAlongLine(double offsetAlongLine)
Sets the the offset along the line for the marker placement.
static QString encodePenCapStyle(Qt::PenCapStyle style)
QgsExpressionContextScope * popScope()
Removes the last scope from the expression context and return it.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Create a new MarkerLineSymbolLayerV2 from SLD.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
const QgsCurve * exteriorRing() const
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.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
Qt::PenCapStyle penCapStyle() const
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
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 copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void setPenJoinStyle(Qt::PenJoinStyle style)
void setOffset(double offset)
virtual QString layerType() const =0
Returns a string that represents this layer type.
double x
Definition: qgspoint.h:41