QGIS API Documentation  3.0.2-Girona (307d082)
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;
597 
599  {
600  context.setOriginalValueVariable( mWidth );
603  }
604 
605  return width * e.mapUnitScaleFactor( e.symbologyScale(), widthUnit(), e.mapUnits() );
606 }
607 
609 {
611  {
614  }
615  return mColor;
616 }
617 
619 {
620  Q_UNUSED( e );
621  double offset = mOffset;
622 
624  {
627  }
628  return offset;
629 }
630 
632 
634 
635 class MyLine
636 {
637  public:
638  MyLine( QPointF p1, QPointF p2 )
639  : mVertical( false )
640  , mIncreasing( false )
641  , mT( 0.0 )
642  , mLength( 0.0 )
643  {
644  if ( p1 == p2 )
645  return; // invalid
646 
647  // tangent and direction
648  if ( qgsDoubleNear( p1.x(), p2.x() ) )
649  {
650  // vertical line - tangent undefined
651  mVertical = true;
652  mIncreasing = ( p2.y() > p1.y() );
653  }
654  else
655  {
656  mVertical = false;
657  mT = float( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
658  mIncreasing = ( p2.x() > p1.x() );
659  }
660 
661  // length
662  double x = ( p2.x() - p1.x() );
663  double y = ( p2.y() - p1.y() );
664  mLength = std::sqrt( x * x + y * y );
665  }
666 
667  // return angle in radians
668  double angle()
669  {
670  double a = ( mVertical ? M_PI_2 : std::atan( mT ) );
671 
672  if ( !mIncreasing )
673  a += M_PI;
674  return a;
675  }
676 
677  // return difference for x,y when going along the line with specified interval
678  QPointF diffForInterval( double interval )
679  {
680  if ( mVertical )
681  return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
682 
683  double alpha = std::atan( mT );
684  double dx = std::cos( alpha ) * interval;
685  double dy = std::sin( alpha ) * interval;
686  return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
687  }
688 
689  double length() { return mLength; }
690 
691  protected:
692  bool mVertical;
693  bool mIncreasing;
694  double mT;
695  double mLength;
696 };
697 
699 
700 QgsMarkerLineSymbolLayer::QgsMarkerLineSymbolLayer( bool rotateMarker, double interval )
701 {
702  mRotateMarker = rotateMarker;
703  mInterval = interval;
704  mIntervalUnit = QgsUnitTypes::RenderMillimeters;
705  mMarker = nullptr;
706  mPlacement = Interval;
707  mOffsetAlongLine = 0;
708  mOffsetAlongLineUnit = QgsUnitTypes::RenderMillimeters;
709 
710  setSubSymbol( new QgsMarkerSymbol() );
711 }
712 
714 {
715  bool rotate = DEFAULT_MARKERLINE_ROTATE;
716  double interval = DEFAULT_MARKERLINE_INTERVAL;
717 
718 
719  if ( props.contains( QStringLiteral( "interval" ) ) )
720  interval = props[QStringLiteral( "interval" )].toDouble();
721  if ( props.contains( QStringLiteral( "rotate" ) ) )
722  rotate = ( props[QStringLiteral( "rotate" )] == QLatin1String( "1" ) );
723 
724  QgsMarkerLineSymbolLayer *x = new QgsMarkerLineSymbolLayer( rotate, interval );
725  if ( props.contains( QStringLiteral( "offset" ) ) )
726  {
727  x->setOffset( props[QStringLiteral( "offset" )].toDouble() );
728  }
729  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
730  {
731  x->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
732  }
733  if ( props.contains( QStringLiteral( "interval_unit" ) ) )
734  {
735  x->setIntervalUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "interval_unit" )] ) );
736  }
737  if ( props.contains( QStringLiteral( "offset_along_line" ) ) )
738  {
739  x->setOffsetAlongLine( props[QStringLiteral( "offset_along_line" )].toDouble() );
740  }
741  if ( props.contains( QStringLiteral( "offset_along_line_unit" ) ) )
742  {
743  x->setOffsetAlongLineUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_along_line_unit" )] ) );
744  }
745  if ( props.contains( ( QStringLiteral( "offset_along_line_map_unit_scale" ) ) ) )
746  {
747  x->setOffsetAlongLineMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_along_line_map_unit_scale" )] ) );
748  }
749 
750  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
751  {
752  x->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
753  }
754  if ( props.contains( QStringLiteral( "interval_map_unit_scale" ) ) )
755  {
756  x->setIntervalMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "interval_map_unit_scale" )] ) );
757  }
758 
759  if ( props.contains( QStringLiteral( "placement" ) ) )
760  {
761  if ( props[QStringLiteral( "placement" )] == QLatin1String( "vertex" ) )
762  x->setPlacement( Vertex );
763  else if ( props[QStringLiteral( "placement" )] == QLatin1String( "lastvertex" ) )
764  x->setPlacement( LastVertex );
765  else if ( props[QStringLiteral( "placement" )] == QLatin1String( "firstvertex" ) )
766  x->setPlacement( FirstVertex );
767  else if ( props[QStringLiteral( "placement" )] == QLatin1String( "centralpoint" ) )
768  x->setPlacement( CentralPoint );
769  else if ( props[QStringLiteral( "placement" )] == QLatin1String( "curvepoint" ) )
770  x->setPlacement( CurvePoint );
771  else
772  x->setPlacement( Interval );
773  }
774 
776 
777  return x;
778 }
779 
781 {
782  return QStringLiteral( "MarkerLine" );
783 }
784 
786 {
787  mMarker->setColor( color );
788  mColor = color;
789 }
790 
792 {
793  return mMarker ? mMarker->color() : mColor;
794 }
795 
797 {
798  mMarker->setOpacity( context.opacity() );
799 
800  // if being rotated, it gets initialized with every line segment
801  QgsSymbol::RenderHints hints = nullptr;
802  if ( mRotateMarker )
804  mMarker->setRenderHints( hints );
805 
806  mMarker->startRender( context.renderContext(), context.fields() );
807 }
808 
810 {
811  mMarker->stopRender( context.renderContext() );
812 }
813 
814 void QgsMarkerLineSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context )
815 {
816  double offset = mOffset;
817 
819  {
822  }
823 
824  Placement placement = mPlacement;
825 
827  {
829  if ( exprVal.isValid() )
830  {
831  QString placementString = exprVal.toString();
832  if ( placementString.compare( QLatin1String( "interval" ), Qt::CaseInsensitive ) == 0 )
833  {
834  placement = Interval;
835  }
836  else if ( placementString.compare( QLatin1String( "vertex" ), Qt::CaseInsensitive ) == 0 )
837  {
838  placement = Vertex;
839  }
840  else if ( placementString.compare( QLatin1String( "lastvertex" ), Qt::CaseInsensitive ) == 0 )
841  {
842  placement = LastVertex;
843  }
844  else if ( placementString.compare( QLatin1String( "firstvertex" ), Qt::CaseInsensitive ) == 0 )
845  {
846  placement = FirstVertex;
847  }
848  else if ( placementString.compare( QLatin1String( "centerpoint" ), Qt::CaseInsensitive ) == 0 )
849  {
850  placement = CentralPoint;
851  }
852  else if ( placementString.compare( QLatin1String( "curvepoint" ), Qt::CaseInsensitive ) == 0 )
853  {
854  placement = CurvePoint;
855  }
856  else
857  {
858  placement = Interval;
859  }
860  }
861  }
862 
863 
864  context.renderContext().painter()->save();
865 
866  if ( qgsDoubleNear( offset, 0.0 ) )
867  {
868  if ( placement == Interval )
869  renderPolylineInterval( points, context );
870  else if ( placement == CentralPoint )
871  renderPolylineCentral( points, context );
872  else
873  renderPolylineVertex( points, context, placement );
874  }
875  else
876  {
877  context.renderContext().setGeometry( nullptr ); //always use segmented geometry with offset
879 
880  for ( int part = 0; part < mline.count(); ++part )
881  {
882  const QPolygonF &points2 = mline[ part ];
883 
884  if ( placement == Interval )
885  renderPolylineInterval( points2, context );
886  else if ( placement == CentralPoint )
887  renderPolylineCentral( points2, context );
888  else
889  renderPolylineVertex( points2, context, placement );
890  }
891  }
892 
893  context.renderContext().painter()->restore();
894 }
895 
896 void QgsMarkerLineSymbolLayer::renderPolygonStroke( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context )
897 {
898  const QgsCurvePolygon *curvePolygon = dynamic_cast<const QgsCurvePolygon *>( context.renderContext().geometry() );
899 
900  if ( curvePolygon )
901  {
902  context.renderContext().setGeometry( curvePolygon->exteriorRing() );
903  }
904  renderPolyline( points, context );
905  if ( rings )
906  {
907  mOffset = -mOffset; // invert the offset for rings!
908  for ( int i = 0; i < rings->size(); ++i )
909  {
910  if ( curvePolygon )
911  {
912  context.renderContext().setGeometry( curvePolygon->interiorRing( i ) );
913  }
914  renderPolyline( rings->at( i ), context );
915  }
916  mOffset = -mOffset;
917  }
918 }
919 
921 {
922  if ( points.isEmpty() )
923  return;
924 
925  QPointF lastPt = points[0];
926  double lengthLeft = 0; // how much is left until next marker
927 
928  QgsRenderContext &rc = context.renderContext();
929  double interval = mInterval;
930 
932  context.renderContext().expressionContext().appendScope( scope );
933 
935  {
936  context.setOriginalValueVariable( mInterval );
938  }
939  if ( interval <= 0 )
940  {
941  interval = 0.1;
942  }
943  double offsetAlongLine = mOffsetAlongLine;
945  {
946  context.setOriginalValueVariable( mOffsetAlongLine );
948  }
949 
950  double painterUnitInterval = rc.convertToPainterUnits( interval, mIntervalUnit, mIntervalMapUnitScale );
951  lengthLeft = painterUnitInterval - rc.convertToPainterUnits( offsetAlongLine, mIntervalUnit, mIntervalMapUnitScale );
952 
953  int pointNum = 0;
954  for ( int i = 1; i < points.count(); ++i )
955  {
956  const QPointF &pt = points[i];
957 
958  if ( lastPt == pt ) // must not be equal!
959  continue;
960 
961  // for each line, find out dx and dy, and length
962  MyLine l( lastPt, pt );
963  QPointF diff = l.diffForInterval( painterUnitInterval );
964 
965  // if there's some length left from previous line
966  // use only the rest for the first point in new line segment
967  double c = 1 - lengthLeft / painterUnitInterval;
968 
969  lengthLeft += l.length();
970 
971  // rotate marker (if desired)
972  if ( mRotateMarker )
973  {
974  mMarker->setLineAngle( l.angle() * 180 / M_PI );
975  }
976 
977 
978  // while we're not at the end of line segment, draw!
979  while ( lengthLeft > painterUnitInterval )
980  {
981  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
982  lastPt += c * diff;
983  lengthLeft -= painterUnitInterval;
985  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
986  c = 1; // reset c (if wasn't 1 already)
987  }
988 
989  lastPt = pt;
990  }
991 
992  delete context.renderContext().expressionContext().popScope();
993 }
994 
995 static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt )
996 {
997  // calc average angle between the previous and next point
998  double a1 = MyLine( prevPt, pt ).angle();
999  double a2 = MyLine( pt, nextPt ).angle();
1000  double unitX = std::cos( a1 ) + std::cos( a2 ), unitY = std::sin( a1 ) + std::sin( a2 );
1001 
1002  return std::atan2( unitY, unitX );
1003 }
1004 
1005 void QgsMarkerLineSymbolLayer::renderPolylineVertex( const QPolygonF &points, QgsSymbolRenderContext &context, Placement placement )
1006 {
1007  if ( points.isEmpty() )
1008  return;
1009 
1010  QgsRenderContext &rc = context.renderContext();
1011 
1012  double origAngle = mMarker->angle();
1013  int i, maxCount;
1014  bool isRing = false;
1015 
1017  context.renderContext().expressionContext().appendScope( scope );
1019 
1020  double offsetAlongLine = mOffsetAlongLine;
1022  {
1023  context.setOriginalValueVariable( mOffsetAlongLine );
1025  }
1026  if ( !qgsDoubleNear( offsetAlongLine, 0.0 ) )
1027  {
1028  //scale offset along line
1029  offsetAlongLine = rc.convertToPainterUnits( offsetAlongLine, mOffsetAlongLineUnit, mOffsetAlongLineMapUnitScale );
1030  }
1031 
1032  if ( qgsDoubleNear( offsetAlongLine, 0.0 ) && context.renderContext().geometry()
1033  && context.renderContext().geometry()->hasCurvedSegments() && ( placement == Vertex || placement == CurvePoint ) )
1034  {
1036  const QgsMapToPixel &mtp = context.renderContext().mapToPixel();
1037 
1038  QgsVertexId vId;
1039  QgsPoint vPoint;
1040  double x, y, z;
1041  QPointF mapPoint;
1042  int pointNum = 0;
1043  while ( context.renderContext().geometry()->nextVertex( vId, vPoint ) )
1044  {
1046 
1047  if ( ( placement == Vertex && vId.type == QgsVertexId::SegmentVertex )
1048  || ( placement == CurvePoint && vId.type == QgsVertexId::CurveVertex ) )
1049  {
1050  //transform
1051  x = vPoint.x(), y = vPoint.y();
1052  z = 0.0;
1053  if ( ct.isValid() )
1054  {
1055  ct.transformInPlace( x, y, z );
1056  }
1057  mapPoint.setX( x );
1058  mapPoint.setY( y );
1059  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1060  if ( mRotateMarker )
1061  {
1062  double angle = context.renderContext().geometry()->vertexAngle( vId );
1063  mMarker->setAngle( angle * 180 / M_PI );
1064  }
1065  mMarker->renderPoint( mapPoint, context.feature(), rc, -1, context.selected() );
1066  }
1067  }
1068 
1069  delete context.renderContext().expressionContext().popScope();
1070  return;
1071  }
1072 
1073  if ( placement == FirstVertex )
1074  {
1075  i = 0;
1076  maxCount = 1;
1077  }
1078  else if ( placement == LastVertex )
1079  {
1080  i = points.count() - 1;
1081  maxCount = points.count();
1082  }
1083  else if ( placement == Vertex )
1084  {
1085  i = 0;
1086  maxCount = points.count();
1087  if ( points.first() == points.last() )
1088  isRing = true;
1089  }
1090  else
1091  {
1092  delete context.renderContext().expressionContext().popScope();
1093  return;
1094  }
1095 
1096  if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
1097  {
1098  double distance;
1099  distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine;
1100  renderOffsetVertexAlongLine( points, i, distance, context );
1101  // restore original rotation
1102  mMarker->setAngle( origAngle );
1103 
1104  delete context.renderContext().expressionContext().popScope();
1105  return;
1106  }
1107 
1108  int pointNum = 0;
1109  for ( ; i < maxCount; ++i )
1110  {
1112 
1113  if ( isRing && placement == Vertex && i == points.count() - 1 )
1114  {
1115  continue; // don't draw the last marker - it has been drawn already
1116  }
1117  // rotate marker (if desired)
1118  if ( mRotateMarker )
1119  {
1120  double angle = markerAngle( points, isRing, i );
1121  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1122  }
1123 
1124  mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() );
1125  }
1126 
1127  // restore original rotation
1128  mMarker->setAngle( origAngle );
1129 
1130  delete context.renderContext().expressionContext().popScope();
1131 }
1132 
1133 double QgsMarkerLineSymbolLayer::markerAngle( const QPolygonF &points, bool isRing, int vertex )
1134 {
1135  double angle = 0;
1136  const QPointF &pt = points[vertex];
1137 
1138  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1139  {
1140  int prevIndex = vertex - 1;
1141  int nextIndex = vertex + 1;
1142 
1143  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1144  {
1145  prevIndex = points.count() - 2;
1146  nextIndex = 1;
1147  }
1148 
1149  QPointF prevPoint, nextPoint;
1150  while ( prevIndex >= 0 )
1151  {
1152  prevPoint = points[ prevIndex ];
1153  if ( prevPoint != pt )
1154  {
1155  break;
1156  }
1157  --prevIndex;
1158  }
1159 
1160  while ( nextIndex < points.count() )
1161  {
1162  nextPoint = points[ nextIndex ];
1163  if ( nextPoint != pt )
1164  {
1165  break;
1166  }
1167  ++nextIndex;
1168  }
1169 
1170  if ( prevIndex >= 0 && nextIndex < points.count() )
1171  {
1172  angle = _averageAngle( prevPoint, pt, nextPoint );
1173  }
1174  }
1175  else //no ring and vertex is at start / at end
1176  {
1177  if ( vertex == 0 )
1178  {
1179  while ( vertex < points.size() - 1 )
1180  {
1181  const QPointF &nextPt = points[vertex + 1];
1182  if ( pt != nextPt )
1183  {
1184  angle = MyLine( pt, nextPt ).angle();
1185  return angle;
1186  }
1187  ++vertex;
1188  }
1189  }
1190  else
1191  {
1192  // use last segment's angle
1193  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1194  {
1195  const QPointF &prevPt = points[vertex - 1];
1196  if ( pt != prevPt )
1197  {
1198  angle = MyLine( prevPt, pt ).angle();
1199  return angle;
1200  }
1201  --vertex;
1202  }
1203  }
1204  }
1205  return angle;
1206 }
1207 
1208 void QgsMarkerLineSymbolLayer::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolRenderContext &context )
1209 {
1210  if ( points.isEmpty() )
1211  return;
1212 
1213  QgsRenderContext &rc = context.renderContext();
1214  double origAngle = mMarker->angle();
1215  if ( qgsDoubleNear( distance, 0.0 ) )
1216  {
1217  // rotate marker (if desired)
1218  if ( mRotateMarker )
1219  {
1220  bool isRing = false;
1221  if ( points.first() == points.last() )
1222  isRing = true;
1223  double angle = markerAngle( points, isRing, vertex );
1224  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1225  }
1226  mMarker->renderPoint( points[vertex], context.feature(), rc, -1, context.selected() );
1227  return;
1228  }
1229 
1230  int pointIncrement = distance > 0 ? 1 : -1;
1231  QPointF previousPoint = points[vertex];
1232  int startPoint = distance > 0 ? std::min( vertex + 1, points.count() - 1 ) : std::max( vertex - 1, 0 );
1233  int endPoint = distance > 0 ? points.count() - 1 : 0;
1234  double distanceLeft = std::fabs( distance );
1235 
1236  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1237  {
1238  const QPointF &pt = points[i];
1239 
1240  if ( previousPoint == pt ) // must not be equal!
1241  continue;
1242 
1243  // create line segment
1244  MyLine l( previousPoint, pt );
1245 
1246  if ( distanceLeft < l.length() )
1247  {
1248  //destination point is in current segment
1249  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1250  // rotate marker (if desired)
1251  if ( mRotateMarker )
1252  {
1253  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1254  }
1255  mMarker->renderPoint( markerPoint, context.feature(), rc, -1, context.selected() );
1256  return;
1257  }
1258 
1259  distanceLeft -= l.length();
1260  previousPoint = pt;
1261  }
1262 
1263  //didn't find point
1264 }
1265 
1267 {
1268  if ( !points.isEmpty() )
1269  {
1270  // calc length
1271  qreal length = 0;
1272  QPolygonF::const_iterator it = points.constBegin();
1273  QPointF last = *it;
1274  for ( ++it; it != points.constEnd(); ++it )
1275  {
1276  length += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
1277  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1278  last = *it;
1279  }
1280 
1281  // find the segment where the central point lies
1282  it = points.constBegin();
1283  last = *it;
1284  qreal last_at = 0, next_at = 0;
1285  QPointF next;
1286  int segment = 0;
1287  for ( ++it; it != points.constEnd(); ++it )
1288  {
1289  next = *it;
1290  next_at += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
1291  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1292  if ( next_at >= length / 2 )
1293  break; // we have reached the center
1294  last = *it;
1295  last_at = next_at;
1296  segment++;
1297  }
1298 
1299  // find out the central point on segment
1300  MyLine l( last, next ); // for line angle
1301  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1302  QPointF pt = last + ( next - last ) * k;
1303 
1304  // draw the marker
1305  double origAngle = mMarker->angle();
1306  if ( mRotateMarker )
1307  mMarker->setAngle( origAngle + l.angle() * 180 / M_PI );
1308  mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() );
1309  if ( mRotateMarker )
1310  mMarker->setAngle( origAngle );
1311  }
1312 }
1313 
1314 
1316 {
1317  QgsStringMap map;
1318  map[QStringLiteral( "rotate" )] = ( mRotateMarker ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
1319  map[QStringLiteral( "interval" )] = QString::number( mInterval );
1320  map[QStringLiteral( "offset" )] = QString::number( mOffset );
1321  map[QStringLiteral( "offset_along_line" )] = QString::number( mOffsetAlongLine );
1322  map[QStringLiteral( "offset_along_line_unit" )] = QgsUnitTypes::encodeUnit( mOffsetAlongLineUnit );
1323  map[QStringLiteral( "offset_along_line_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetAlongLineMapUnitScale );
1324  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1325  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1326  map[QStringLiteral( "interval_unit" )] = QgsUnitTypes::encodeUnit( mIntervalUnit );
1327  map[QStringLiteral( "interval_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mIntervalMapUnitScale );
1328  if ( mPlacement == Vertex )
1329  map[QStringLiteral( "placement" )] = QStringLiteral( "vertex" );
1330  else if ( mPlacement == LastVertex )
1331  map[QStringLiteral( "placement" )] = QStringLiteral( "lastvertex" );
1332  else if ( mPlacement == FirstVertex )
1333  map[QStringLiteral( "placement" )] = QStringLiteral( "firstvertex" );
1334  else if ( mPlacement == CentralPoint )
1335  map[QStringLiteral( "placement" )] = QStringLiteral( "centralpoint" );
1336  else if ( mPlacement == CurvePoint )
1337  map[QStringLiteral( "placement" )] = QStringLiteral( "curvepoint" );
1338  else
1339  map[QStringLiteral( "placement" )] = QStringLiteral( "interval" );
1340  return map;
1341 }
1342 
1344 {
1345  return mMarker.get();
1346 }
1347 
1349 {
1350  if ( !symbol || symbol->type() != QgsSymbol::Marker )
1351  {
1352  delete symbol;
1353  return false;
1354  }
1355 
1356  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
1357  mColor = mMarker->color();
1358  return true;
1359 }
1360 
1362 {
1363  QgsMarkerLineSymbolLayer *x = new QgsMarkerLineSymbolLayer( mRotateMarker, mInterval );
1364  x->setSubSymbol( mMarker->clone() );
1365  x->setOffset( mOffset );
1366  x->setPlacement( mPlacement );
1367  x->setOffsetUnit( mOffsetUnit );
1369  x->setIntervalUnit( mIntervalUnit );
1370  x->setIntervalMapUnitScale( mIntervalMapUnitScale );
1371  x->setOffsetAlongLine( mOffsetAlongLine );
1372  x->setOffsetAlongLineMapUnitScale( mOffsetAlongLineMapUnitScale );
1373  x->setOffsetAlongLineUnit( mOffsetAlongLineUnit );
1375  copyPaintEffect( x );
1376  return x;
1377 }
1378 
1379 void QgsMarkerLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
1380 {
1381  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1382  {
1383  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:LineSymbolizer" ) );
1384  if ( !props.value( QStringLiteral( "uom" ), QLatin1String( "" ) ).isEmpty() )
1385  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QLatin1String( "" ) ) );
1386  element.appendChild( symbolizerElem );
1387 
1388  // <Geometry>
1389  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QLatin1String( "" ) ) );
1390 
1391  QString gap;
1392  switch ( mPlacement )
1393  {
1394  case FirstVertex:
1395  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "firstPoint" ) ) );
1396  break;
1397  case LastVertex:
1398  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "lastPoint" ) ) );
1399  break;
1400  case CentralPoint:
1401  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "centralPoint" ) ) );
1402  break;
1403  case Vertex:
1404  // no way to get line/polygon's vertices, use a VendorOption
1405  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "points" ) ) );
1406  break;
1407  default:
1408  double interval = QgsSymbolLayerUtils::rescaleUom( mInterval, mIntervalUnit, props );
1409  gap = qgsDoubleToString( interval );
1410  break;
1411  }
1412 
1413  if ( !mRotateMarker )
1414  {
1415  // markers in LineSymbolizer must be drawn following the line orientation,
1416  // use a VendorOption when no marker rotation
1417  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "rotateMarker" ), QStringLiteral( "0" ) ) );
1418  }
1419 
1420  // <Stroke>
1421  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
1422  symbolizerElem.appendChild( strokeElem );
1423 
1424  // <GraphicStroke>
1425  QDomElement graphicStrokeElem = doc.createElement( QStringLiteral( "se:GraphicStroke" ) );
1426  strokeElem.appendChild( graphicStrokeElem );
1427 
1428  QgsSymbolLayer *layer = mMarker->symbolLayer( i );
1429  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
1430  if ( !markerLayer )
1431  {
1432  graphicStrokeElem.appendChild( doc.createComment( QStringLiteral( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() ) ) );
1433  }
1434  else
1435  {
1436  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1437  }
1438 
1439  if ( !gap.isEmpty() )
1440  {
1441  QDomElement gapElem = doc.createElement( QStringLiteral( "se:Gap" ) );
1442  QgsSymbolLayerUtils::createExpressionElement( doc, gapElem, gap );
1443  graphicStrokeElem.appendChild( gapElem );
1444  }
1445 
1446  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1447  {
1448  QDomElement perpOffsetElem = doc.createElement( QStringLiteral( "se:PerpendicularOffset" ) );
1450  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
1451  symbolizerElem.appendChild( perpOffsetElem );
1452  }
1453  }
1454 }
1455 
1457 {
1458  QgsDebugMsg( "Entered." );
1459 
1460  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
1461  if ( strokeElem.isNull() )
1462  return nullptr;
1463 
1464  QDomElement graphicStrokeElem = strokeElem.firstChildElement( QStringLiteral( "GraphicStroke" ) );
1465  if ( graphicStrokeElem.isNull() )
1466  return nullptr;
1467 
1468  // retrieve vendor options
1469  bool rotateMarker = true;
1470  Placement placement = Interval;
1471 
1472  QgsStringMap vendorOptions = QgsSymbolLayerUtils::getVendorOptionList( element );
1473  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1474  {
1475  if ( it.key() == QLatin1String( "placement" ) )
1476  {
1477  if ( it.value() == QLatin1String( "points" ) ) placement = Vertex;
1478  else if ( it.value() == QLatin1String( "firstPoint" ) ) placement = FirstVertex;
1479  else if ( it.value() == QLatin1String( "lastPoint" ) ) placement = LastVertex;
1480  else if ( it.value() == QLatin1String( "centralPoint" ) ) placement = CentralPoint;
1481  }
1482  else if ( it.value() == QLatin1String( "rotateMarker" ) )
1483  {
1484  rotateMarker = it.value() == QLatin1String( "0" );
1485  }
1486  }
1487 
1488  std::unique_ptr< QgsMarkerSymbol > marker;
1489 
1491  if ( l )
1492  {
1493  QgsSymbolLayerList layers;
1494  layers.append( l );
1495  marker.reset( new QgsMarkerSymbol( layers ) );
1496  }
1497 
1498  if ( !marker )
1499  return nullptr;
1500 
1501  double interval = 0.0;
1502  QDomElement gapElem = graphicStrokeElem.firstChildElement( QStringLiteral( "Gap" ) );
1503  if ( !gapElem.isNull() )
1504  {
1505  bool ok;
1506  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1507  if ( ok )
1508  interval = d;
1509  }
1510 
1511  double offset = 0.0;
1512  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( QStringLiteral( "PerpendicularOffset" ) );
1513  if ( !perpOffsetElem.isNull() )
1514  {
1515  bool ok;
1516  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1517  if ( ok )
1518  offset = d;
1519  }
1520 
1521  QString uom = element.attribute( QStringLiteral( "uom" ) );
1522  interval = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, interval );
1523  offset = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset );
1524 
1525  QgsMarkerLineSymbolLayer *x = new QgsMarkerLineSymbolLayer( rotateMarker );
1526  x->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1527  x->setPlacement( placement );
1528  x->setInterval( interval );
1529  x->setSubSymbol( marker.release() );
1530  x->setOffset( offset );
1531  return x;
1532 }
1533 
1535 {
1536  mMarker->setSize( width );
1537 }
1538 
1540 {
1541  if ( key == QgsSymbolLayer::PropertyWidth && mMarker && property )
1542  {
1543  mMarker->setDataDefinedSize( property );
1544  }
1546 }
1547 
1549 {
1550  return mMarker->size();
1551 }
1552 
1554 {
1556  mMarker->setOutputUnit( unit );
1557  mIntervalUnit = unit;
1558  mOffsetUnit = unit;
1559  mOffsetAlongLineUnit = unit;
1560 }
1561 
1563 {
1565  if ( mIntervalUnit != unit || mOffsetUnit != unit || mOffsetAlongLineUnit != unit )
1566  {
1568  }
1569  return unit;
1570 }
1571 
1573 {
1575  mIntervalMapUnitScale = scale;
1576  mOffsetMapUnitScale = scale;
1577  mOffsetAlongLineMapUnitScale = scale;
1578 }
1579 
1581 {
1582  if ( QgsLineSymbolLayer::mapUnitScale() == mIntervalMapUnitScale &&
1583  mIntervalMapUnitScale == mOffsetMapUnitScale &&
1584  mOffsetMapUnitScale == mOffsetAlongLineMapUnitScale )
1585  {
1586  return mOffsetMapUnitScale;
1587  }
1588  return QgsMapUnitScale();
1589 }
1590 
1591 QSet<QString> QgsMarkerLineSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
1592 {
1593  QSet<QString> attr = QgsLineSymbolLayer::usedAttributes( context );
1594  if ( mMarker )
1595  attr.unite( mMarker->usedAttributes( context ) );
1596  return attr;
1597 }
1598 
1600 {
1601  return context.convertToPainterUnits( ( mMarker->size() / 2.0 ), mMarker->sizeUnit(), mMarker->sizeMapUnitScale() ) +
1603 }
1604 
1605 
1606 
const QgsCurve * interiorRing(int i) const
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
get 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
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
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits)
Calculates a scaling factor to convert from map units to a specified symbol unit. ...
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
static QgsSymbolLayer * createFromSld(QDomElement &element)
#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
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:479
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition: qgssymbol.h:104
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:251
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)
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:1055
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)
set 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
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.
A store for object properties.
Definition: qgsproperty.h:229
QgsMapUnitScale mWidthMapUnitScale
const QgsCurve * exteriorRing() const
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.
double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
get 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)
QVector< qreal > dxfCustomDashPattern(QgsUnitTypes::RenderUnit &unit) const override
get 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 transformInPlace(double &x, double &y, double &z, TransformDirection direction=ForwardTransform) const
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
bool setSubSymbol(QgsSymbol *symbol) override
set layer&#39;s subsymbol. takes ownership of the passed symbol
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
get color
Qt::PenStyle dxfPenStyle() const override
get 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