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