QGIS API Documentation  3.6.0-Noosa (5873452)
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();
1117  y = vPoint.y();
1118  z = 0.0;
1119  if ( ct.isValid() )
1120  {
1121  ct.transformInPlace( x, y, z );
1122  }
1123  mapPoint.setX( x );
1124  mapPoint.setY( y );
1125  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1126  if ( mRotateMarker )
1127  {
1128  double angle = context.renderContext().geometry()->vertexAngle( vId );
1129  mMarker->setAngle( angle * 180 / M_PI );
1130  }
1131  mMarker->renderPoint( mapPoint, context.feature(), rc, -1, context.selected() );
1132  }
1133  }
1134 
1135  delete context.renderContext().expressionContext().popScope();
1136  return;
1137  }
1138 
1139  if ( placement == FirstVertex )
1140  {
1141  i = 0;
1142  maxCount = 1;
1143  }
1144  else if ( placement == LastVertex )
1145  {
1146  i = points.count() - 1;
1147  maxCount = points.count();
1148  }
1149  else if ( placement == Vertex )
1150  {
1151  i = 0;
1152  maxCount = points.count();
1153  if ( points.first() == points.last() )
1154  isRing = true;
1155  }
1156  else
1157  {
1158  delete context.renderContext().expressionContext().popScope();
1159  return;
1160  }
1161 
1162  if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
1163  {
1164  double distance;
1165  distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine;
1166  renderOffsetVertexAlongLine( points, i, distance, context );
1167  // restore original rotation
1168  mMarker->setAngle( origAngle );
1169 
1170  delete context.renderContext().expressionContext().popScope();
1171  return;
1172  }
1173 
1174  int pointNum = 0;
1175  for ( ; i < maxCount; ++i )
1176  {
1178 
1179  if ( isRing && placement == Vertex && i == points.count() - 1 )
1180  {
1181  continue; // don't draw the last marker - it has been drawn already
1182  }
1183  // rotate marker (if desired)
1184  if ( mRotateMarker )
1185  {
1186  double angle = markerAngle( points, isRing, i );
1187  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1188  }
1189 
1190  mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() );
1191  }
1192 
1193  // restore original rotation
1194  mMarker->setAngle( origAngle );
1195 
1196  delete context.renderContext().expressionContext().popScope();
1197 }
1198 
1199 double QgsMarkerLineSymbolLayer::markerAngle( const QPolygonF &points, bool isRing, int vertex )
1200 {
1201  double angle = 0;
1202  const QPointF &pt = points[vertex];
1203 
1204  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1205  {
1206  int prevIndex = vertex - 1;
1207  int nextIndex = vertex + 1;
1208 
1209  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1210  {
1211  prevIndex = points.count() - 2;
1212  nextIndex = 1;
1213  }
1214 
1215  QPointF prevPoint, nextPoint;
1216  while ( prevIndex >= 0 )
1217  {
1218  prevPoint = points[ prevIndex ];
1219  if ( prevPoint != pt )
1220  {
1221  break;
1222  }
1223  --prevIndex;
1224  }
1225 
1226  while ( nextIndex < points.count() )
1227  {
1228  nextPoint = points[ nextIndex ];
1229  if ( nextPoint != pt )
1230  {
1231  break;
1232  }
1233  ++nextIndex;
1234  }
1235 
1236  if ( prevIndex >= 0 && nextIndex < points.count() )
1237  {
1238  angle = _averageAngle( prevPoint, pt, nextPoint );
1239  }
1240  }
1241  else //no ring and vertex is at start / at end
1242  {
1243  if ( vertex == 0 )
1244  {
1245  while ( vertex < points.size() - 1 )
1246  {
1247  const QPointF &nextPt = points[vertex + 1];
1248  if ( pt != nextPt )
1249  {
1250  angle = MyLine( pt, nextPt ).angle();
1251  return angle;
1252  }
1253  ++vertex;
1254  }
1255  }
1256  else
1257  {
1258  // use last segment's angle
1259  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1260  {
1261  const QPointF &prevPt = points[vertex - 1];
1262  if ( pt != prevPt )
1263  {
1264  angle = MyLine( prevPt, pt ).angle();
1265  return angle;
1266  }
1267  --vertex;
1268  }
1269  }
1270  }
1271  return angle;
1272 }
1273 
1274 void QgsMarkerLineSymbolLayer::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolRenderContext &context )
1275 {
1276  if ( points.isEmpty() )
1277  return;
1278 
1279  QgsRenderContext &rc = context.renderContext();
1280  double origAngle = mMarker->angle();
1281  if ( qgsDoubleNear( distance, 0.0 ) )
1282  {
1283  // rotate marker (if desired)
1284  if ( mRotateMarker )
1285  {
1286  bool isRing = false;
1287  if ( points.first() == points.last() )
1288  isRing = true;
1289  double angle = markerAngle( points, isRing, vertex );
1290  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1291  }
1292  mMarker->renderPoint( points[vertex], context.feature(), rc, -1, context.selected() );
1293  return;
1294  }
1295 
1296  int pointIncrement = distance > 0 ? 1 : -1;
1297  QPointF previousPoint = points[vertex];
1298  int startPoint = distance > 0 ? std::min( vertex + 1, points.count() - 1 ) : std::max( vertex - 1, 0 );
1299  int endPoint = distance > 0 ? points.count() - 1 : 0;
1300  double distanceLeft = std::fabs( distance );
1301 
1302  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1303  {
1304  const QPointF &pt = points[i];
1305 
1306  if ( previousPoint == pt ) // must not be equal!
1307  continue;
1308 
1309  // create line segment
1310  MyLine l( previousPoint, pt );
1311 
1312  if ( distanceLeft < l.length() )
1313  {
1314  //destination point is in current segment
1315  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1316  // rotate marker (if desired)
1317  if ( mRotateMarker )
1318  {
1319  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1320  }
1321  mMarker->renderPoint( markerPoint, context.feature(), rc, -1, context.selected() );
1322  return;
1323  }
1324 
1325  distanceLeft -= l.length();
1326  previousPoint = pt;
1327  }
1328 
1329  //didn't find point
1330 }
1331 
1333 {
1334  if ( !points.isEmpty() )
1335  {
1336  // calc length
1337  qreal length = 0;
1338  QPolygonF::const_iterator it = points.constBegin();
1339  QPointF last = *it;
1340  for ( ++it; it != points.constEnd(); ++it )
1341  {
1342  length += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
1343  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1344  last = *it;
1345  }
1346 
1347  // find the segment where the central point lies
1348  it = points.constBegin();
1349  last = *it;
1350  qreal last_at = 0, next_at = 0;
1351  QPointF next;
1352  int segment = 0;
1353  for ( ++it; it != points.constEnd(); ++it )
1354  {
1355  next = *it;
1356  next_at += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
1357  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1358  if ( next_at >= length / 2 )
1359  break; // we have reached the center
1360  last = *it;
1361  last_at = next_at;
1362  segment++;
1363  }
1364 
1365  // find out the central point on segment
1366  MyLine l( last, next ); // for line angle
1367  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1368  QPointF pt = last + ( next - last ) * k;
1369 
1370  // draw the marker
1371  double origAngle = mMarker->angle();
1372  if ( mRotateMarker )
1373  mMarker->setAngle( origAngle + l.angle() * 180 / M_PI );
1374  mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() );
1375  if ( mRotateMarker )
1376  mMarker->setAngle( origAngle );
1377  }
1378 }
1379 
1380 
1382 {
1383  QgsStringMap map;
1384  map[QStringLiteral( "rotate" )] = ( mRotateMarker ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
1385  map[QStringLiteral( "interval" )] = QString::number( mInterval );
1386  map[QStringLiteral( "offset" )] = QString::number( mOffset );
1387  map[QStringLiteral( "offset_along_line" )] = QString::number( mOffsetAlongLine );
1388  map[QStringLiteral( "offset_along_line_unit" )] = QgsUnitTypes::encodeUnit( mOffsetAlongLineUnit );
1389  map[QStringLiteral( "offset_along_line_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetAlongLineMapUnitScale );
1390  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1391  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1392  map[QStringLiteral( "interval_unit" )] = QgsUnitTypes::encodeUnit( mIntervalUnit );
1393  map[QStringLiteral( "interval_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mIntervalMapUnitScale );
1394  if ( mPlacement == Vertex )
1395  map[QStringLiteral( "placement" )] = QStringLiteral( "vertex" );
1396  else if ( mPlacement == LastVertex )
1397  map[QStringLiteral( "placement" )] = QStringLiteral( "lastvertex" );
1398  else if ( mPlacement == FirstVertex )
1399  map[QStringLiteral( "placement" )] = QStringLiteral( "firstvertex" );
1400  else if ( mPlacement == CentralPoint )
1401  map[QStringLiteral( "placement" )] = QStringLiteral( "centralpoint" );
1402  else if ( mPlacement == CurvePoint )
1403  map[QStringLiteral( "placement" )] = QStringLiteral( "curvepoint" );
1404  else
1405  map[QStringLiteral( "placement" )] = QStringLiteral( "interval" );
1406 
1407  map[QStringLiteral( "ring_filter" )] = QString::number( static_cast< int >( mRingFilter ) );
1408  return map;
1409 }
1410 
1412 {
1413  return mMarker.get();
1414 }
1415 
1417 {
1418  if ( !symbol || symbol->type() != QgsSymbol::Marker )
1419  {
1420  delete symbol;
1421  return false;
1422  }
1423 
1424  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
1425  mColor = mMarker->color();
1426  return true;
1427 }
1428 
1430 {
1431  QgsMarkerLineSymbolLayer *x = new QgsMarkerLineSymbolLayer( mRotateMarker, mInterval );
1432  x->setSubSymbol( mMarker->clone() );
1433  x->setOffset( mOffset );
1434  x->setPlacement( mPlacement );
1435  x->setOffsetUnit( mOffsetUnit );
1437  x->setIntervalUnit( mIntervalUnit );
1438  x->setIntervalMapUnitScale( mIntervalMapUnitScale );
1439  x->setOffsetAlongLine( mOffsetAlongLine );
1440  x->setOffsetAlongLineMapUnitScale( mOffsetAlongLineMapUnitScale );
1441  x->setOffsetAlongLineUnit( mOffsetAlongLineUnit );
1442  x->setRingFilter( mRingFilter );
1444  copyPaintEffect( x );
1445  return x;
1446 }
1447 
1448 void QgsMarkerLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
1449 {
1450  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1451  {
1452  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:LineSymbolizer" ) );
1453  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
1454  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
1455  element.appendChild( symbolizerElem );
1456 
1457  // <Geometry>
1458  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
1459 
1460  QString gap;
1461  switch ( mPlacement )
1462  {
1463  case FirstVertex:
1464  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "firstPoint" ) ) );
1465  break;
1466  case LastVertex:
1467  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "lastPoint" ) ) );
1468  break;
1469  case CentralPoint:
1470  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "centralPoint" ) ) );
1471  break;
1472  case Vertex:
1473  // no way to get line/polygon's vertices, use a VendorOption
1474  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "points" ) ) );
1475  break;
1476  default:
1477  double interval = QgsSymbolLayerUtils::rescaleUom( mInterval, mIntervalUnit, props );
1478  gap = qgsDoubleToString( interval );
1479  break;
1480  }
1481 
1482  if ( !mRotateMarker )
1483  {
1484  // markers in LineSymbolizer must be drawn following the line orientation,
1485  // use a VendorOption when no marker rotation
1486  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "rotateMarker" ), QStringLiteral( "0" ) ) );
1487  }
1488 
1489  // <Stroke>
1490  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
1491  symbolizerElem.appendChild( strokeElem );
1492 
1493  // <GraphicStroke>
1494  QDomElement graphicStrokeElem = doc.createElement( QStringLiteral( "se:GraphicStroke" ) );
1495  strokeElem.appendChild( graphicStrokeElem );
1496 
1497  QgsSymbolLayer *layer = mMarker->symbolLayer( i );
1498  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
1499  if ( !markerLayer )
1500  {
1501  graphicStrokeElem.appendChild( doc.createComment( QStringLiteral( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() ) ) );
1502  }
1503  else
1504  {
1505  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1506  }
1507 
1508  if ( !gap.isEmpty() )
1509  {
1510  QDomElement gapElem = doc.createElement( QStringLiteral( "se:Gap" ) );
1511  QgsSymbolLayerUtils::createExpressionElement( doc, gapElem, gap );
1512  graphicStrokeElem.appendChild( gapElem );
1513  }
1514 
1515  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1516  {
1517  QDomElement perpOffsetElem = doc.createElement( QStringLiteral( "se:PerpendicularOffset" ) );
1519  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
1520  symbolizerElem.appendChild( perpOffsetElem );
1521  }
1522  }
1523 }
1524 
1526 {
1527  QgsDebugMsg( QStringLiteral( "Entered." ) );
1528 
1529  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
1530  if ( strokeElem.isNull() )
1531  return nullptr;
1532 
1533  QDomElement graphicStrokeElem = strokeElem.firstChildElement( QStringLiteral( "GraphicStroke" ) );
1534  if ( graphicStrokeElem.isNull() )
1535  return nullptr;
1536 
1537  // retrieve vendor options
1538  bool rotateMarker = true;
1539  Placement placement = Interval;
1540 
1541  QgsStringMap vendorOptions = QgsSymbolLayerUtils::getVendorOptionList( element );
1542  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1543  {
1544  if ( it.key() == QLatin1String( "placement" ) )
1545  {
1546  if ( it.value() == QLatin1String( "points" ) ) placement = Vertex;
1547  else if ( it.value() == QLatin1String( "firstPoint" ) ) placement = FirstVertex;
1548  else if ( it.value() == QLatin1String( "lastPoint" ) ) placement = LastVertex;
1549  else if ( it.value() == QLatin1String( "centralPoint" ) ) placement = CentralPoint;
1550  }
1551  else if ( it.value() == QLatin1String( "rotateMarker" ) )
1552  {
1553  rotateMarker = it.value() == QLatin1String( "0" );
1554  }
1555  }
1556 
1557  std::unique_ptr< QgsMarkerSymbol > marker;
1558 
1560  if ( l )
1561  {
1562  QgsSymbolLayerList layers;
1563  layers.append( l );
1564  marker.reset( new QgsMarkerSymbol( layers ) );
1565  }
1566 
1567  if ( !marker )
1568  return nullptr;
1569 
1570  double interval = 0.0;
1571  QDomElement gapElem = graphicStrokeElem.firstChildElement( QStringLiteral( "Gap" ) );
1572  if ( !gapElem.isNull() )
1573  {
1574  bool ok;
1575  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1576  if ( ok )
1577  interval = d;
1578  }
1579 
1580  double offset = 0.0;
1581  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( QStringLiteral( "PerpendicularOffset" ) );
1582  if ( !perpOffsetElem.isNull() )
1583  {
1584  bool ok;
1585  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1586  if ( ok )
1587  offset = d;
1588  }
1589 
1590  QString uom = element.attribute( QStringLiteral( "uom" ) );
1591  interval = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, interval );
1592  offset = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset );
1593 
1594  QgsMarkerLineSymbolLayer *x = new QgsMarkerLineSymbolLayer( rotateMarker );
1595  x->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1596  x->setPlacement( placement );
1597  x->setInterval( interval );
1598  x->setSubSymbol( marker.release() );
1599  x->setOffset( offset );
1600  return x;
1601 }
1602 
1604 {
1605  mMarker->setSize( width );
1606 }
1607 
1609 {
1610  if ( key == QgsSymbolLayer::PropertyWidth && mMarker && property )
1611  {
1612  mMarker->setDataDefinedSize( property );
1613  }
1615 }
1616 
1618 {
1619  return mMarker->size();
1620 }
1621 
1623 {
1624  return mMarker->size( context );
1625 }
1626 
1628 {
1630  mMarker->setOutputUnit( unit );
1631  mIntervalUnit = unit;
1632  mOffsetUnit = unit;
1633  mOffsetAlongLineUnit = unit;
1634 }
1635 
1637 {
1639  if ( mIntervalUnit != unit || mOffsetUnit != unit || mOffsetAlongLineUnit != unit )
1640  {
1642  }
1643  return unit;
1644 }
1645 
1647 {
1649  mIntervalMapUnitScale = scale;
1650  mOffsetMapUnitScale = scale;
1651  mOffsetAlongLineMapUnitScale = scale;
1652 }
1653 
1655 {
1656  if ( QgsLineSymbolLayer::mapUnitScale() == mIntervalMapUnitScale &&
1657  mIntervalMapUnitScale == mOffsetMapUnitScale &&
1658  mOffsetMapUnitScale == mOffsetAlongLineMapUnitScale )
1659  {
1660  return mOffsetMapUnitScale;
1661  }
1662  return QgsMapUnitScale();
1663 }
1664 
1665 QSet<QString> QgsMarkerLineSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
1666 {
1667  QSet<QString> attr = QgsLineSymbolLayer::usedAttributes( context );
1668  if ( mMarker )
1669  attr.unite( mMarker->usedAttributes( context ) );
1670  return attr;
1671 }
1672 
1674 {
1676  return true;
1677  if ( mMarker && mMarker->hasDataDefinedProperties() )
1678  return true;
1679  return false;
1680 }
1681 
1683 {
1684  return ( mMarker->size( context ) / 2.0 ) +
1686 }
1687 
1688 
1689 
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:265
Render both exterior and interior rings.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from the curve polygon.
#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:587
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition: qgssymbol.h:104
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
virtual double width() const
Returns the estimated width for the line symbol layer.
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:732
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)
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
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:1089
static QString encodePenStyle(Qt::PenStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Writes the symbol layer definition as a SLD XML element.
static double rescaleUom(double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap &props)
Rescales the given size based on the uomScale found in the props, if any is found, otherwise returns the value un-modified.
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:225
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
Returns the estimated width for the line symbol layer.
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
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
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
Returns the curve polygon&#39;s exterior ring.
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:111
static QColor decodeColor(const QString &str)
virtual void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the layer.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void setPenJoinStyle(Qt::PenJoinStyle style)
void setOffset(double offset)
virtual QString layerType() const =0
Returns a string that represents this layer type.
double x
Definition: qgspoint.h:41