QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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"
29 
30 #include <algorithm>
31 #include <QPainter>
32 #include <QDomDocument>
33 #include <QDomElement>
34 
35 #include <cmath>
36 
37 QgsSimpleLineSymbolLayer::QgsSimpleLineSymbolLayer( const QColor &color, double width, Qt::PenStyle penStyle )
38  : mPenStyle( penStyle )
39 {
40  mColor = color;
41  mWidth = width;
42  mCustomDashVector << 5 << 2;
43 }
44 
46 {
48  mWidthUnit = unit;
49  mOffsetUnit = unit;
50  mCustomDashPatternUnit = unit;
51 }
52 
54 {
56  if ( mWidthUnit != unit || mOffsetUnit != unit || mCustomDashPatternUnit != unit )
57  {
59  }
60  return unit;
61 }
62 
64 {
67 }
68 
70 {
72  mWidthMapUnitScale = scale;
73  mOffsetMapUnitScale = scale;
74  mCustomDashPatternMapUnitScale = scale;
75 }
76 
78 {
81  mOffsetMapUnitScale == mCustomDashPatternMapUnitScale )
82  {
83  return mWidthMapUnitScale;
84  }
85  return QgsMapUnitScale();
86 }
87 
89 {
93 
94  if ( props.contains( QStringLiteral( "line_color" ) ) )
95  {
96  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() );
97  }
98  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
99  {
100  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() );
101  }
102  else if ( props.contains( QStringLiteral( "color" ) ) )
103  {
104  //pre 2.5 projects used "color"
105  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
106  }
107  if ( props.contains( QStringLiteral( "line_width" ) ) )
108  {
109  width = props[QStringLiteral( "line_width" )].toDouble();
110  }
111  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
112  {
113  width = props[QStringLiteral( "outline_width" )].toDouble();
114  }
115  else if ( props.contains( QStringLiteral( "width" ) ) )
116  {
117  //pre 2.5 projects used "width"
118  width = props[QStringLiteral( "width" )].toDouble();
119  }
120  if ( props.contains( QStringLiteral( "line_style" ) ) )
121  {
122  penStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() );
123  }
124  else if ( props.contains( QStringLiteral( "outline_style" ) ) )
125  {
126  penStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() );
127  }
128  else if ( props.contains( QStringLiteral( "penstyle" ) ) )
129  {
130  penStyle = QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "penstyle" )].toString() );
131  }
132 
134  if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
135  {
136  l->setWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
137  }
138  else if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
139  {
140  l->setWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
141  }
142  else if ( props.contains( QStringLiteral( "width_unit" ) ) )
143  {
144  //pre 2.5 projects used "width_unit"
145  l->setWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "width_unit" )].toString() ) );
146  }
147  if ( props.contains( QStringLiteral( "width_map_unit_scale" ) ) )
148  l->setWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "width_map_unit_scale" )].toString() ) );
149  if ( props.contains( QStringLiteral( "offset" ) ) )
150  l->setOffset( props[QStringLiteral( "offset" )].toDouble() );
151  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
152  l->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
153  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
154  l->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
155  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
156  l->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
157  if ( props.contains( QStringLiteral( "capstyle" ) ) )
158  l->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "capstyle" )].toString() ) );
159 
160  if ( props.contains( QStringLiteral( "use_custom_dash" ) ) )
161  {
162  l->setUseCustomDashPattern( props[QStringLiteral( "use_custom_dash" )].toInt() );
163  }
164  if ( props.contains( QStringLiteral( "customdash" ) ) )
165  {
166  l->setCustomDashVector( QgsSymbolLayerUtils::decodeRealVector( props[QStringLiteral( "customdash" )].toString() ) );
167  }
168  if ( props.contains( QStringLiteral( "customdash_unit" ) ) )
169  {
170  l->setCustomDashPatternUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "customdash_unit" )].toString() ) );
171  }
172  if ( props.contains( QStringLiteral( "customdash_map_unit_scale" ) ) )
173  {
174  l->setCustomDashPatternMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "customdash_map_unit_scale" )].toString() ) );
175  }
176 
177  if ( props.contains( QStringLiteral( "draw_inside_polygon" ) ) )
178  {
179  l->setDrawInsidePolygon( props[QStringLiteral( "draw_inside_polygon" )].toInt() );
180  }
181 
182  if ( props.contains( QStringLiteral( "ring_filter" ) ) )
183  {
184  l->setRingFilter( static_cast< RenderRingFilter>( props[QStringLiteral( "ring_filter" )].toInt() ) );
185  }
186 
187  if ( props.contains( QStringLiteral( "dash_pattern_offset" ) ) )
188  l->setDashPatternOffset( props[QStringLiteral( "dash_pattern_offset" )].toDouble() );
189  if ( props.contains( QStringLiteral( "dash_pattern_offset_unit" ) ) )
190  l->setDashPatternOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "dash_pattern_offset_unit" )].toString() ) );
191  if ( props.contains( QStringLiteral( "dash_pattern_offset_map_unit_scale" ) ) )
192  l->setDashPatternOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "dash_pattern_offset_map_unit_scale" )].toString() ) );
193 
194  if ( props.contains( QStringLiteral( "align_dash_pattern" ) ) )
195  l->setAlignDashPattern( props[ QStringLiteral( "align_dash_pattern" )].toInt() );
196 
197  if ( props.contains( QStringLiteral( "tweak_dash_pattern_on_corners" ) ) )
198  l->setTweakDashPatternOnCorners( props[ QStringLiteral( "tweak_dash_pattern_on_corners" )].toInt() );
199 
201 
202  return l;
203 }
204 
205 
207 {
208  return QStringLiteral( "SimpleLine" );
209 }
210 
212 {
213  QColor penColor = mColor;
214  penColor.setAlphaF( mColor.alphaF() * context.opacity() );
215  mPen.setColor( penColor );
216  double scaledWidth = context.renderContext().convertToPainterUnits( mWidth, mWidthUnit, mWidthMapUnitScale );
217  mPen.setWidthF( scaledWidth );
218 
219  //note that Qt seems to have issues with scaling dash patterns with very small pen widths.
220  //treating the pen as having no less than a 1 pixel size avoids the worst of these issues
221  const double dashWidthDiv = std::max( 1.0, scaledWidth );
222  if ( mUseCustomDashPattern )
223  {
224  mPen.setStyle( Qt::CustomDashLine );
225 
226  //scale pattern vector
227 
228  QVector<qreal> scaledVector;
229  QVector<qreal>::const_iterator it = mCustomDashVector.constBegin();
230  for ( ; it != mCustomDashVector.constEnd(); ++it )
231  {
232  //the dash is specified in terms of pen widths, therefore the division
233  scaledVector << context.renderContext().convertToPainterUnits( ( *it ), mCustomDashPatternUnit, mCustomDashPatternMapUnitScale ) / dashWidthDiv;
234  }
235  mPen.setDashPattern( scaledVector );
236  }
237  else
238  {
239  mPen.setStyle( mPenStyle );
240  }
241 
242  if ( mDashPatternOffset && mPen.style() != Qt::SolidLine )
243  {
244  mPen.setDashOffset( context.renderContext().convertToPainterUnits( mDashPatternOffset, mDashPatternOffsetUnit, mDashPatternOffsetMapUnitScale ) / dashWidthDiv ) ;
245  }
246 
247  mPen.setJoinStyle( mPenJoinStyle );
248  mPen.setCapStyle( mPenCapStyle );
249 
250  mSelPen = mPen;
251  QColor selColor = context.renderContext().selectionColor();
252  if ( ! SELECTION_IS_OPAQUE )
253  selColor.setAlphaF( context.opacity() );
254  mSelPen.setColor( selColor );
255 }
256 
258 {
259  Q_UNUSED( context )
260 }
261 
262 void QgsSimpleLineSymbolLayer::renderPolygonStroke( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
263 {
264  QPainter *p = context.renderContext().painter();
265  if ( !p )
266  {
267  return;
268  }
269 
270  if ( mDrawInsidePolygon )
271  p->save();
272 
273  switch ( mRingFilter )
274  {
275  case AllRings:
276  case ExteriorRingOnly:
277  {
278  if ( mDrawInsidePolygon )
279  {
280  //only drawing the line on the interior of the polygon, so set clip path for painter
281  QPainterPath clipPath;
282  clipPath.addPolygon( points );
283 
284  if ( rings )
285  {
286  //add polygon rings
287  for ( auto it = rings->constBegin(); it != rings->constEnd(); ++it )
288  {
289  QPolygonF ring = *it;
290  clipPath.addPolygon( ring );
291  }
292  }
293 
294  //use intersect mode, as a clip path may already exist (e.g., for composer maps)
295  p->setClipPath( clipPath, Qt::IntersectClip );
296  }
297 
298  renderPolyline( points, context );
299  }
300  break;
301 
302  case InteriorRingsOnly:
303  break;
304  }
305 
306  if ( rings )
307  {
308  switch ( mRingFilter )
309  {
310  case AllRings:
311  case InteriorRingsOnly:
312  {
313  mOffset = -mOffset; // invert the offset for rings!
314  for ( const QPolygonF &ring : qgis::as_const( *rings ) )
315  renderPolyline( ring, context );
316  mOffset = -mOffset;
317  }
318  break;
319  case ExteriorRingOnly:
320  break;
321  }
322  }
323 
324  if ( mDrawInsidePolygon )
325  {
326  //restore painter to reset clip path
327  p->restore();
328  }
329 
330 }
331 
332 void QgsSimpleLineSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context )
333 {
334  QPainter *p = context.renderContext().painter();
335  if ( !p )
336  {
337  return;
338  }
339 
340  QColor penColor = mColor;
341  penColor.setAlphaF( mColor.alphaF() * context.opacity() );
342  mPen.setColor( penColor );
343 
344  double offset = mOffset;
345  applyDataDefinedSymbology( context, mPen, mSelPen, offset );
346 
347  const QPen pen = context.selected() ? mSelPen : mPen;
348  p->setBrush( Qt::NoBrush );
349 
350  // Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #2 points).
351  std::unique_ptr< QgsScopedQPainterState > painterState;
352  if ( points.size() <= 2 &&
355  ( p->renderHints() & QPainter::Antialiasing ) )
356  {
357  painterState = qgis::make_unique< QgsScopedQPainterState >( p );
358  p->setRenderHint( QPainter::Antialiasing, false );
359  }
360 
361  const bool applyPatternTweaks = mAlignDashPattern
362  && ( pen.style() != Qt::SolidLine || !pen.dashPattern().empty() )
363  && pen.dashOffset() == 0;
364 
365  if ( qgsDoubleNear( offset, 0 ) )
366  {
367  if ( applyPatternTweaks )
368  {
369  drawPathWithDashPatternTweaks( p, points, pen );
370  }
371  else
372  {
373  p->setPen( pen );
374  QPainterPath path;
375  path.addPolygon( points );
376  p->drawPath( path );
377  }
378  }
379  else
380  {
381  double scaledOffset = context.renderContext().convertToPainterUnits( offset, mOffsetUnit, mOffsetMapUnitScale );
383  {
384  // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
385  // and clamp it to a reasonable range. It's the best we can do in this situation!
386  scaledOffset = std::min( std::max( context.renderContext().convertToPainterUnits( offset, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
387  }
388 
389  QList<QPolygonF> mline = ::offsetLine( points, scaledOffset, context.originalGeometryType() != QgsWkbTypes::UnknownGeometry ? context.originalGeometryType() : QgsWkbTypes::LineGeometry );
390  for ( const QPolygonF &part : mline )
391  {
392  if ( applyPatternTweaks )
393  {
394  drawPathWithDashPatternTweaks( p, part, pen );
395  }
396  else
397  {
398  p->setPen( pen );
399  QPainterPath path;
400  path.addPolygon( part );
401  p->drawPath( path );
402  }
403  }
404  }
405 }
406 
408 {
409  QVariantMap map;
410  map[QStringLiteral( "line_color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
411  map[QStringLiteral( "line_width" )] = QString::number( mWidth );
412  map[QStringLiteral( "line_width_unit" )] = QgsUnitTypes::encodeUnit( mWidthUnit );
413  map[QStringLiteral( "width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mWidthMapUnitScale );
414  map[QStringLiteral( "line_style" )] = QgsSymbolLayerUtils::encodePenStyle( mPenStyle );
415  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
416  map[QStringLiteral( "capstyle" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
417  map[QStringLiteral( "offset" )] = QString::number( mOffset );
418  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
419  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
420  map[QStringLiteral( "use_custom_dash" )] = ( mUseCustomDashPattern ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
421  map[QStringLiteral( "customdash" )] = QgsSymbolLayerUtils::encodeRealVector( mCustomDashVector );
422  map[QStringLiteral( "customdash_unit" )] = QgsUnitTypes::encodeUnit( mCustomDashPatternUnit );
423  map[QStringLiteral( "customdash_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mCustomDashPatternMapUnitScale );
424  map[QStringLiteral( "dash_pattern_offset" )] = QString::number( mDashPatternOffset );
425  map[QStringLiteral( "dash_pattern_offset_unit" )] = QgsUnitTypes::encodeUnit( mDashPatternOffsetUnit );
426  map[QStringLiteral( "dash_pattern_offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mDashPatternOffsetMapUnitScale );
427  map[QStringLiteral( "draw_inside_polygon" )] = ( mDrawInsidePolygon ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
428  map[QStringLiteral( "ring_filter" )] = QString::number( static_cast< int >( mRingFilter ) );
429  map[QStringLiteral( "align_dash_pattern" )] = mAlignDashPattern ? QStringLiteral( "1" ) : QStringLiteral( "0" );
430  map[QStringLiteral( "tweak_dash_pattern_on_corners" )] = mPatternCartographicTweakOnSharpCorners ? QStringLiteral( "1" ) : QStringLiteral( "0" );
431  return map;
432 }
433 
435 {
437  l->setWidthUnit( mWidthUnit );
441  l->setCustomDashPatternUnit( mCustomDashPatternUnit );
442  l->setCustomDashPatternMapUnitScale( mCustomDashPatternMapUnitScale );
443  l->setOffset( mOffset );
444  l->setPenJoinStyle( mPenJoinStyle );
445  l->setPenCapStyle( mPenCapStyle );
446  l->setUseCustomDashPattern( mUseCustomDashPattern );
447  l->setCustomDashVector( mCustomDashVector );
448  l->setDrawInsidePolygon( mDrawInsidePolygon );
450  l->setDashPatternOffset( mDashPatternOffset );
451  l->setDashPatternOffsetUnit( mDashPatternOffsetUnit );
452  l->setDashPatternOffsetMapUnitScale( mDashPatternOffsetMapUnitScale );
453  l->setAlignDashPattern( mAlignDashPattern );
454  l->setTweakDashPatternOnCorners( mPatternCartographicTweakOnSharpCorners );
455 
457  copyPaintEffect( l );
458  return l;
459 }
460 
461 void QgsSimpleLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
462 {
463  if ( mPenStyle == Qt::NoPen )
464  return;
465 
466  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:LineSymbolizer" ) );
467  if ( !props.value( QStringLiteral( "uom" ), QString() ).toString().isEmpty() )
468  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ).toString() );
469  element.appendChild( symbolizerElem );
470 
471  // <Geometry>
472  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ).toString() );
473 
474  // <Stroke>
475  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
476  symbolizerElem.appendChild( strokeElem );
477 
478  Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
480  QVector<qreal> customDashVector = QgsSymbolLayerUtils::rescaleUom( mCustomDashVector, mCustomDashPatternUnit, props );
482  &mPenJoinStyle, &mPenCapStyle, &customDashVector );
483 
484  // <se:PerpendicularOffset>
485  if ( !qgsDoubleNear( mOffset, 0.0 ) )
486  {
487  QDomElement perpOffsetElem = doc.createElement( QStringLiteral( "se:PerpendicularOffset" ) );
489  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
490  symbolizerElem.appendChild( perpOffsetElem );
491  }
492 }
493 
494 QString QgsSimpleLineSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
495 {
496  if ( mUseCustomDashPattern )
497  {
498  return QgsSymbolLayerUtils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor,
499  mPen.color(), mPenJoinStyle,
500  mPenCapStyle, mOffset, &mCustomDashVector );
501  }
502  else
503  {
504  return QgsSymbolLayerUtils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor, mPen.color(), mPenJoinStyle,
505  mPenCapStyle, mOffset );
506  }
507 }
508 
510 {
511  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
512 
513  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
514  if ( strokeElem.isNull() )
515  return nullptr;
516 
517  Qt::PenStyle penStyle;
518  QColor color;
519  double width;
520  Qt::PenJoinStyle penJoinStyle;
521  Qt::PenCapStyle penCapStyle;
522  QVector<qreal> customDashVector;
523 
524  if ( !QgsSymbolLayerUtils::lineFromSld( strokeElem, penStyle,
525  color, width,
527  &customDashVector ) )
528  return nullptr;
529 
530  double offset = 0.0;
531  QDomElement perpOffsetElem = element.firstChildElement( QStringLiteral( "PerpendicularOffset" ) );
532  if ( !perpOffsetElem.isNull() )
533  {
534  bool ok;
535  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
536  if ( ok )
537  offset = d;
538  }
539 
540  QString uom = element.attribute( QStringLiteral( "uom" ) );
543 
545  l->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
546  l->setOffset( offset );
549  l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine );
551  return l;
552 }
553 
554 void QgsSimpleLineSymbolLayer::applyDataDefinedSymbology( QgsSymbolRenderContext &context, QPen &pen, QPen &selPen, double &offset )
555 {
556  if ( !dataDefinedProperties().hasActiveProperties() )
557  return; // shortcut
558 
559  //data defined properties
560  bool hasStrokeWidthExpression = false;
562  {
563  context.setOriginalValueVariable( mWidth );
564  double scaledWidth = context.renderContext().convertToPainterUnits(
567  pen.setWidthF( scaledWidth );
568  selPen.setWidthF( scaledWidth );
569  hasStrokeWidthExpression = true;
570  }
571 
572  //color
574  {
576 
578  penColor.setAlphaF( context.opacity() * penColor.alphaF() );
579  pen.setColor( penColor );
580  }
581 
582  //offset
584  {
587  }
588 
589  //dash dot vector
590 
591  //note that Qt seems to have issues with scaling dash patterns with very small pen widths.
592  //treating the pen as having no less than a 1 pixel size avoids the worst of these issues
593  const double dashWidthDiv = std::max( hasStrokeWidthExpression ? pen.widthF() : mPen.widthF(), 1.0 );
594 
596  {
597  QVector<qreal> dashVector;
599  if ( exprVal.isValid() )
600  {
601  QStringList dashList = exprVal.toString().split( ';' );
602  QStringList::const_iterator dashIt = dashList.constBegin();
603  for ( ; dashIt != dashList.constEnd(); ++dashIt )
604  {
605  dashVector.push_back( context.renderContext().convertToPainterUnits( dashIt->toDouble(), mCustomDashPatternUnit, mCustomDashPatternMapUnitScale ) / dashWidthDiv );
606  }
607  pen.setDashPattern( dashVector );
608  }
609  }
610  else if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyStrokeWidth ) && mUseCustomDashPattern )
611  {
612  //re-scale pattern vector after data defined pen width was applied
613 
614  QVector<qreal> scaledVector;
615  for ( double v : mCustomDashVector )
616  {
617  //the dash is specified in terms of pen widths, therefore the division
618  scaledVector << context.renderContext().convertToPainterUnits( v, mCustomDashPatternUnit, mCustomDashPatternMapUnitScale ) / dashWidthDiv;
619  }
620  mPen.setDashPattern( scaledVector );
621  }
622 
623  // dash pattern offset
624  double patternOffset = mDashPatternOffset;
625  if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyDashPatternOffset ) && pen.style() != Qt::SolidLine )
626  {
627  context.setOriginalValueVariable( mDashPatternOffset );
629  pen.setDashOffset( context.renderContext().convertToPainterUnits( patternOffset, mDashPatternOffsetUnit, mDashPatternOffsetMapUnitScale ) / dashWidthDiv );
630  }
631 
632  //line style
634  {
637  if ( exprVal.isValid() )
638  pen.setStyle( QgsSymbolLayerUtils::decodePenStyle( exprVal.toString() ) );
639  }
640 
641  //join style
643  {
646  if ( exprVal.isValid() )
647  pen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( exprVal.toString() ) );
648  }
649 
650  //cap style
652  {
655  if ( exprVal.isValid() )
656  pen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( exprVal.toString() ) );
657  }
658 }
659 
660 void QgsSimpleLineSymbolLayer::drawPathWithDashPatternTweaks( QPainter *painter, const QPolygonF &points, QPen pen ) const
661 {
662  if ( pen.dashPattern().empty() || points.size() < 2 )
663  return;
664 
665  QVector< qreal > sourcePattern = pen.dashPattern();
666  const double dashWidthDiv = std::max( 1.0001, pen.widthF() );
667  // back to painter units
668  for ( int i = 0; i < sourcePattern.size(); ++ i )
669  sourcePattern[i] *= pen.widthF();
670 
671  if ( pen.widthF() <= 1.0 )
672  pen.setWidthF( 1.0001 );
673 
674  QVector< qreal > buffer;
675  QPolygonF bufferedPoints;
676  QPolygonF previousSegmentBuffer;
677  // we iterate through the line points, building a custom dash pattern and adding it to the buffer
678  // as soon as we hit a sharp bend, we scale the buffered pattern in order to nicely place a dash component over the bend
679  // and then append the buffer to the output pattern.
680 
681  auto ptIt = points.constBegin();
682  double totalBufferLength = 0;
683  int patternIndex = 0;
684  double currentRemainingDashLength = 0;
685  double currentRemainingGapLength = 0;
686 
687  auto compressPattern = []( const QVector< qreal > &buffer ) -> QVector< qreal >
688  {
689  QVector< qreal > result;
690  result.reserve( buffer.size() );
691  for ( auto it = buffer.begin(); it != buffer.end(); )
692  {
693  qreal dash = *it++;
694  qreal gap = *it++;
695  while ( dash == 0 && !result.empty() )
696  {
697  result.last() += gap;
698 
699  if ( it == buffer.end() )
700  return result;
701  dash = *it++;
702  gap = *it++;
703  }
704  while ( gap == 0 && it != buffer.end() )
705  {
706  dash += *it++;
707  gap = *it++;
708  }
709  result << dash << gap;
710  }
711  return result;
712  };
713 
714  double currentBufferLineLength = 0;
715  auto flushBuffer = [pen, painter, &buffer, &bufferedPoints, &previousSegmentBuffer, &currentRemainingDashLength, &currentRemainingGapLength, &currentBufferLineLength, &totalBufferLength,
716  dashWidthDiv, &compressPattern]( QPointF * nextPoint )
717  {
718  if ( buffer.empty() || bufferedPoints.size() < 2 )
719  {
720  return;
721  }
722 
723  if ( currentRemainingDashLength )
724  {
725  // ended midway through a dash -- we want to finish this off
726  buffer << currentRemainingDashLength << 0.0;
727  totalBufferLength += currentRemainingDashLength;
728  }
729  QVector< qreal > compressed = compressPattern( buffer );
730  if ( !currentRemainingDashLength )
731  {
732  // ended midway through a gap -- we don't want this, we want to end at previous dash
733  totalBufferLength -= compressed.last();
734  compressed.last() = 0;
735  }
736 
737  // rescale buffer for final bit of line -- we want to end at the end of a dash, not a gap
738  const double scaleFactor = currentBufferLineLength / totalBufferLength;
739 
740  bool shouldFlushPreviousSegmentBuffer = false;
741 
742  if ( !previousSegmentBuffer.empty() )
743  {
744  // add first dash from current buffer
745  QPolygonF firstDashSubstring = QgsSymbolLayerUtils::polylineSubstring( bufferedPoints, 0, compressed.first() * scaleFactor );
746  if ( !firstDashSubstring.empty() )
747  QgsSymbolLayerUtils::appendPolyline( previousSegmentBuffer, firstDashSubstring );
748 
749  // then we skip over the first dash and gap for this segment
750  bufferedPoints = QgsSymbolLayerUtils::polylineSubstring( bufferedPoints, ( compressed.first() + compressed.at( 1 ) ) * scaleFactor, 0 );
751 
752  compressed = compressed.mid( 2 );
753  shouldFlushPreviousSegmentBuffer = !compressed.empty();
754  }
755 
756  if ( !previousSegmentBuffer.empty() && ( shouldFlushPreviousSegmentBuffer || !nextPoint ) )
757  {
758  QPen adjustedPen = pen;
759  adjustedPen.setStyle( Qt::SolidLine );
760  painter->setPen( adjustedPen );
761  QPainterPath path;
762  path.addPolygon( previousSegmentBuffer );
763  painter->drawPath( path );
764  previousSegmentBuffer.clear();
765  }
766 
767  double finalDash = 0;
768  if ( nextPoint )
769  {
770  // sharp bend:
771  // 1. rewind buffered points line by final dash and gap length
772  // (later) 2. draw the bend with a solid line of length 2 * final dash size
773 
774  if ( !compressed.empty() )
775  {
776  finalDash = compressed.at( compressed.size() - 2 );
777  const double finalGap = compressed.size() > 2 ? compressed.at( compressed.size() - 3 ) : 0;
778 
779  const QPolygonF thisPoints = bufferedPoints;
780  bufferedPoints = QgsSymbolLayerUtils::polylineSubstring( thisPoints, 0, -( finalDash + finalGap ) * scaleFactor );
781  previousSegmentBuffer = QgsSymbolLayerUtils::polylineSubstring( thisPoints, - finalDash * scaleFactor, 0 );
782  }
783  else
784  {
785  previousSegmentBuffer << bufferedPoints;
786  }
787  }
788 
789  currentBufferLineLength = 0;
790  currentRemainingDashLength = 0;
791  currentRemainingGapLength = 0;
792  totalBufferLength = 0;
793  buffer.clear();
794 
795  if ( !bufferedPoints.empty() && ( !compressed.empty() || !nextPoint ) )
796  {
797  QPen adjustedPen = pen;
798  if ( !compressed.empty() )
799  {
800  // maximum size of dash pattern is 32 elements
801  compressed = compressed.mid( 0, 32 );
802  std::for_each( compressed.begin(), compressed.end(), [scaleFactor, dashWidthDiv]( qreal & element ) { element *= scaleFactor / dashWidthDiv; } );
803  adjustedPen.setDashPattern( compressed );
804  }
805  else
806  {
807  adjustedPen.setStyle( Qt::SolidLine );
808  }
809 
810  painter->setPen( adjustedPen );
811  QPainterPath path;
812  path.addPolygon( bufferedPoints );
813  painter->drawPath( path );
814  }
815 
816  bufferedPoints.clear();
817  };
818 
819  QPointF p1;
820  QPointF p2 = *ptIt;
821  ptIt++;
822  bufferedPoints << p2;
823  for ( ; ptIt != points.constEnd(); ++ptIt )
824  {
825  p1 = *ptIt;
826  if ( qgsDoubleNear( p1.y(), p2.y() ) && qgsDoubleNear( p1.x(), p2.x() ) )
827  {
828  continue;
829  }
830 
831  double remainingSegmentDistance = std::sqrt( std::pow( p2.x() - p1.x(), 2.0 ) + std::pow( p2.y() - p1.y(), 2.0 ) );
832  currentBufferLineLength += remainingSegmentDistance;
833  while ( true )
834  {
835  // handle currentRemainingDashLength/currentRemainingGapLength
836  if ( currentRemainingDashLength > 0 )
837  {
838  // bit more of dash to insert
839  if ( remainingSegmentDistance >= currentRemainingDashLength )
840  {
841  // all of dash fits in
842  buffer << currentRemainingDashLength << 0.0;
843  totalBufferLength += currentRemainingDashLength;
844  remainingSegmentDistance -= currentRemainingDashLength;
845  patternIndex++;
846  currentRemainingDashLength = 0.0;
847  currentRemainingGapLength = sourcePattern.at( patternIndex );
848  }
849  else
850  {
851  // only part of remaining dash fits in
852  buffer << remainingSegmentDistance << 0.0;
853  totalBufferLength += remainingSegmentDistance;
854  currentRemainingDashLength -= remainingSegmentDistance;
855  break;
856  }
857  }
858  if ( currentRemainingGapLength > 0 )
859  {
860  // bit more of gap to insert
861  if ( remainingSegmentDistance >= currentRemainingGapLength )
862  {
863  // all of gap fits in
864  buffer << 0.0 << currentRemainingGapLength;
865  totalBufferLength += currentRemainingGapLength;
866  remainingSegmentDistance -= currentRemainingGapLength;
867  currentRemainingGapLength = 0.0;
868  patternIndex++;
869  }
870  else
871  {
872  // only part of remaining gap fits in
873  buffer << 0.0 << remainingSegmentDistance;
874  totalBufferLength += remainingSegmentDistance;
875  currentRemainingGapLength -= remainingSegmentDistance;
876  break;
877  }
878  }
879 
880  if ( patternIndex >= sourcePattern.size() )
881  patternIndex = 0;
882 
883  const double nextPatternDashLength = sourcePattern.at( patternIndex );
884  const double nextPatternGapLength = sourcePattern.at( patternIndex + 1 );
885  if ( nextPatternDashLength + nextPatternGapLength <= remainingSegmentDistance )
886  {
887  buffer << nextPatternDashLength << nextPatternGapLength;
888  remainingSegmentDistance -= nextPatternDashLength + nextPatternGapLength;
889  totalBufferLength += nextPatternDashLength + nextPatternGapLength;
890  patternIndex += 2;
891  }
892  else if ( nextPatternDashLength <= remainingSegmentDistance )
893  {
894  // can fit in "dash", but not "gap"
895  buffer << nextPatternDashLength << remainingSegmentDistance - nextPatternDashLength;
896  totalBufferLength += remainingSegmentDistance;
897  currentRemainingGapLength = nextPatternGapLength - ( remainingSegmentDistance - nextPatternDashLength );
898  currentRemainingDashLength = 0;
899  patternIndex++;
900  break;
901  }
902  else
903  {
904  // can't fit in "dash"
905  buffer << remainingSegmentDistance << 0.0;
906  totalBufferLength += remainingSegmentDistance;
907  currentRemainingGapLength = 0;
908  currentRemainingDashLength = nextPatternDashLength - remainingSegmentDistance;
909  break;
910  }
911  }
912 
913  bufferedPoints << p1;
914  if ( mPatternCartographicTweakOnSharpCorners && ptIt + 1 != points.constEnd() )
915  {
916  QPointF nextPoint = *( ptIt + 1 );
917 
918  // extreme angles form more than 45 degree angle at a node
919  if ( QgsSymbolLayerUtils::isSharpCorner( p2, p1, nextPoint ) )
920  {
921  // extreme angle. Rescale buffer and flush
922  flushBuffer( &nextPoint );
923  bufferedPoints << p1;
924  // restart the line with the full length of the most recent dash element -- see
925  // "Cartographic Generalization" (Swiss Society of Cartography) p33, example #8
926  if ( patternIndex % 2 == 1 )
927  {
928  patternIndex--;
929  }
930  currentRemainingDashLength = sourcePattern.at( patternIndex );
931  }
932  }
933 
934  p2 = p1;
935  }
936 
937  flushBuffer( nullptr );
938  if ( !previousSegmentBuffer.empty() )
939  {
940  QPen adjustedPen = pen;
941  adjustedPen.setStyle( Qt::SolidLine );
942  painter->setPen( adjustedPen );
943  QPainterPath path;
944  path.addPolygon( previousSegmentBuffer );
945  painter->drawPath( path );
946  previousSegmentBuffer.clear();
947  }
948 }
949 
951 {
952  if ( mDrawInsidePolygon )
953  {
954  //set to clip line to the interior of polygon, so we expect no bleed
955  return 0;
956  }
957  else
958  {
959  return context.convertToPainterUnits( ( mWidth / 2.0 ), mWidthUnit, mWidthMapUnitScale ) +
961  }
962 }
963 
965 {
966  unit = mCustomDashPatternUnit;
967  return mUseCustomDashPattern ? mCustomDashVector : QVector<qreal>();
968 }
969 
971 {
972  return mPenStyle;
973 }
974 
976 {
977  double width = mWidth;
979  {
980  context.setOriginalValueVariable( mWidth );
982  }
983 
986  {
988  }
989  return width;
990 }
991 
993 {
995  {
998  }
999  return mColor;
1000 }
1001 
1003 {
1004  return mPenStyle != Qt::SolidLine || mUseCustomDashPattern;
1005 }
1006 
1008 {
1009  return mAlignDashPattern;
1010 }
1011 
1013 {
1014  mAlignDashPattern = enabled;
1015 }
1016 
1018 {
1019  return mPatternCartographicTweakOnSharpCorners;
1020 }
1021 
1023 {
1024  mPatternCartographicTweakOnSharpCorners = enabled;
1025 }
1026 
1028 {
1029  Q_UNUSED( e )
1030  double offset = mOffset;
1031 
1033  {
1034  context.setOriginalValueVariable( mOffset );
1036  }
1037 
1040  {
1042  }
1043  return -offset; //direction seems to be inverse to symbology offset
1044 }
1045 
1047 
1049 
1050 class MyLine
1051 {
1052  public:
1053  MyLine( QPointF p1, QPointF p2 )
1054  : mVertical( false )
1055  , mIncreasing( false )
1056  , mT( 0.0 )
1057  , mLength( 0.0 )
1058  {
1059  if ( p1 == p2 )
1060  return; // invalid
1061 
1062  // tangent and direction
1063  if ( qgsDoubleNear( p1.x(), p2.x() ) )
1064  {
1065  // vertical line - tangent undefined
1066  mVertical = true;
1067  mIncreasing = ( p2.y() > p1.y() );
1068  }
1069  else
1070  {
1071  mVertical = false;
1072  mT = ( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
1073  mIncreasing = ( p2.x() > p1.x() );
1074  }
1075 
1076  // length
1077  double x = ( p2.x() - p1.x() );
1078  double y = ( p2.y() - p1.y() );
1079  mLength = std::sqrt( x * x + y * y );
1080  }
1081 
1082  // return angle in radians
1083  double angle()
1084  {
1085  double a = ( mVertical ? M_PI_2 : std::atan( mT ) );
1086 
1087  if ( !mIncreasing )
1088  a += M_PI;
1089  return a;
1090  }
1091 
1092  // return difference for x,y when going along the line with specified interval
1093  QPointF diffForInterval( double interval )
1094  {
1095  if ( mVertical )
1096  return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
1097 
1098  double alpha = std::atan( mT );
1099  double dx = std::cos( alpha ) * interval;
1100  double dy = std::sin( alpha ) * interval;
1101  return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
1102  }
1103 
1104  double length() { return mLength; }
1105 
1106  protected:
1107  bool mVertical;
1108  bool mIncreasing;
1109  double mT;
1110  double mLength;
1111 };
1112 
1114 
1115 //
1116 // QgsTemplatedLineSymbolLayerBase
1117 //
1119  : mRotateSymbols( rotateSymbol )
1120  , mInterval( interval )
1121 {
1122 
1123 }
1124 
1126 {
1127  double offset = mOffset;
1128 
1130  {
1131  context.setOriginalValueVariable( mOffset );
1133  }
1134 
1136 
1138  {
1140  if ( exprVal.isValid() )
1141  {
1142  QString placementString = exprVal.toString();
1143  if ( placementString.compare( QLatin1String( "interval" ), Qt::CaseInsensitive ) == 0 )
1144  {
1146  }
1147  else if ( placementString.compare( QLatin1String( "vertex" ), Qt::CaseInsensitive ) == 0 )
1148  {
1150  }
1151  else if ( placementString.compare( QLatin1String( "lastvertex" ), Qt::CaseInsensitive ) == 0 )
1152  {
1154  }
1155  else if ( placementString.compare( QLatin1String( "firstvertex" ), Qt::CaseInsensitive ) == 0 )
1156  {
1158  }
1159  else if ( placementString.compare( QLatin1String( "centerpoint" ), Qt::CaseInsensitive ) == 0 )
1160  {
1162  }
1163  else if ( placementString.compare( QLatin1String( "curvepoint" ), Qt::CaseInsensitive ) == 0 )
1164  {
1166  }
1167  else if ( placementString.compare( QLatin1String( "segmentcenter" ), Qt::CaseInsensitive ) == 0 )
1168  {
1170  }
1171  else
1172  {
1174  }
1175  }
1176  }
1177 
1178  QgsScopedQPainterState painterState( context.renderContext().painter() );
1179 
1180  double averageOver = mAverageAngleLength;
1182  {
1183  context.setOriginalValueVariable( mAverageAngleLength );
1185  }
1186  averageOver = context.renderContext().convertToPainterUnits( averageOver, mAverageAngleLengthUnit, mAverageAngleLengthMapUnitScale ) / 2.0;
1187 
1188  if ( qgsDoubleNear( offset, 0.0 ) )
1189  {
1190  switch ( placement )
1191  {
1192  case Interval:
1193  renderPolylineInterval( points, context, averageOver );
1194  break;
1195 
1196  case CentralPoint:
1197  renderPolylineCentral( points, context, averageOver );
1198  break;
1199 
1200  case Vertex:
1201  case LastVertex:
1202  case FirstVertex:
1203  case CurvePoint:
1204  case SegmentCenter:
1205  renderPolylineVertex( points, context, placement );
1206  break;
1207  }
1208  }
1209  else
1210  {
1211  context.renderContext().setGeometry( nullptr ); //always use segmented geometry with offset
1213 
1214  for ( int part = 0; part < mline.count(); ++part )
1215  {
1216  const QPolygonF &points2 = mline[ part ];
1217 
1218  switch ( placement )
1219  {
1220  case Interval:
1221  renderPolylineInterval( points2, context, averageOver );
1222  break;
1223 
1224  case CentralPoint:
1225  renderPolylineCentral( points2, context, averageOver );
1226  break;
1227 
1228  case Vertex:
1229  case LastVertex:
1230  case FirstVertex:
1231  case CurvePoint:
1232  case SegmentCenter:
1233  renderPolylineVertex( points2, context, placement );
1234  break;
1235  }
1236  }
1237  }
1238 }
1239 
1240 void QgsTemplatedLineSymbolLayerBase::renderPolygonStroke( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
1241 {
1242  const QgsCurvePolygon *curvePolygon = dynamic_cast<const QgsCurvePolygon *>( context.renderContext().geometry() );
1243 
1244  if ( curvePolygon )
1245  {
1246  context.renderContext().setGeometry( curvePolygon->exteriorRing() );
1247  }
1248 
1249  switch ( mRingFilter )
1250  {
1251  case AllRings:
1252  case ExteriorRingOnly:
1253  renderPolyline( points, context );
1254  break;
1255  case InteriorRingsOnly:
1256  break;
1257  }
1258 
1259  if ( rings )
1260  {
1261  switch ( mRingFilter )
1262  {
1263  case AllRings:
1264  case InteriorRingsOnly:
1265  {
1266  mOffset = -mOffset; // invert the offset for rings!
1267  for ( int i = 0; i < rings->size(); ++i )
1268  {
1269  if ( curvePolygon )
1270  {
1271  context.renderContext().setGeometry( curvePolygon->interiorRing( i ) );
1272  }
1273  renderPolyline( rings->at( i ), context );
1274  }
1275  mOffset = -mOffset;
1276  }
1277  break;
1278  case ExteriorRingOnly:
1279  break;
1280  }
1281  }
1282 }
1283 
1285 {
1287  if ( intervalUnit() != unit || mOffsetUnit != unit || offsetAlongLineUnit() != unit )
1288  {
1290  }
1291  return unit;
1292 }
1293 
1295 {
1297  setIntervalMapUnitScale( scale );
1298  mOffsetMapUnitScale = scale;
1300 }
1301 
1303 {
1307  {
1308  return mOffsetMapUnitScale;
1309  }
1310  return QgsMapUnitScale();
1311 }
1312 
1314 {
1315  QVariantMap map;
1316  map[QStringLiteral( "rotate" )] = ( rotateSymbols() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
1317  map[QStringLiteral( "interval" )] = QString::number( interval() );
1318  map[QStringLiteral( "offset" )] = QString::number( mOffset );
1319  map[QStringLiteral( "offset_along_line" )] = QString::number( offsetAlongLine() );
1320  map[QStringLiteral( "offset_along_line_unit" )] = QgsUnitTypes::encodeUnit( offsetAlongLineUnit() );
1321  map[QStringLiteral( "offset_along_line_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( offsetAlongLineMapUnitScale() );
1322  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1323  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1324  map[QStringLiteral( "interval_unit" )] = QgsUnitTypes::encodeUnit( intervalUnit() );
1325  map[QStringLiteral( "interval_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( intervalMapUnitScale() );
1326  map[QStringLiteral( "average_angle_length" )] = QString::number( mAverageAngleLength );
1327  map[QStringLiteral( "average_angle_unit" )] = QgsUnitTypes::encodeUnit( mAverageAngleLengthUnit );
1328  map[QStringLiteral( "average_angle_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mAverageAngleLengthMapUnitScale );
1329 
1330  switch ( mPlacement )
1331  {
1332  case Vertex:
1333  map[QStringLiteral( "placement" )] = QStringLiteral( "vertex" );
1334  break;
1335  case LastVertex:
1336  map[QStringLiteral( "placement" )] = QStringLiteral( "lastvertex" );
1337  break;
1338  case FirstVertex:
1339  map[QStringLiteral( "placement" )] = QStringLiteral( "firstvertex" );
1340  break;
1341  case CentralPoint:
1342  map[QStringLiteral( "placement" )] = QStringLiteral( "centralpoint" );
1343  break;
1344  case CurvePoint:
1345  map[QStringLiteral( "placement" )] = QStringLiteral( "curvepoint" );
1346  break;
1347  case Interval:
1348  map[QStringLiteral( "placement" )] = QStringLiteral( "interval" );
1349  break;
1350  case SegmentCenter:
1351  map[QStringLiteral( "placement" )] = QStringLiteral( "segmentcenter" );
1352  break;
1353  }
1354 
1355  map[QStringLiteral( "ring_filter" )] = QString::number( static_cast< int >( mRingFilter ) );
1356  return map;
1357 }
1358 
1360 {
1361  switch ( mPlacement )
1362  {
1366  return true;
1367 
1372  return false;
1373  }
1374  return false;
1375 }
1376 
1378 {
1379  destLayer->setSubSymbol( const_cast< QgsTemplatedLineSymbolLayerBase * >( this )->subSymbol()->clone() );
1380  destLayer->setOffset( mOffset );
1381  destLayer->setPlacement( placement() );
1382  destLayer->setOffsetUnit( mOffsetUnit );
1384  destLayer->setIntervalUnit( intervalUnit() );
1386  destLayer->setOffsetAlongLine( offsetAlongLine() );
1389  destLayer->setAverageAngleLength( mAverageAngleLength );
1390  destLayer->setAverageAngleUnit( mAverageAngleLengthUnit );
1391  destLayer->setAverageAngleMapUnitScale( mAverageAngleLengthMapUnitScale );
1392  destLayer->setRingFilter( mRingFilter );
1393  copyDataDefinedProperties( destLayer );
1394  copyPaintEffect( destLayer );
1395 }
1396 
1398 {
1399  if ( properties.contains( QStringLiteral( "offset" ) ) )
1400  {
1401  destLayer->setOffset( properties[QStringLiteral( "offset" )].toDouble() );
1402  }
1403  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
1404  {
1405  destLayer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
1406  }
1407  if ( properties.contains( QStringLiteral( "interval_unit" ) ) )
1408  {
1409  destLayer->setIntervalUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "interval_unit" )].toString() ) );
1410  }
1411  if ( properties.contains( QStringLiteral( "offset_along_line" ) ) )
1412  {
1413  destLayer->setOffsetAlongLine( properties[QStringLiteral( "offset_along_line" )].toDouble() );
1414  }
1415  if ( properties.contains( QStringLiteral( "offset_along_line_unit" ) ) )
1416  {
1417  destLayer->setOffsetAlongLineUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_along_line_unit" )].toString() ) );
1418  }
1419  if ( properties.contains( ( QStringLiteral( "offset_along_line_map_unit_scale" ) ) ) )
1420  {
1421  destLayer->setOffsetAlongLineMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_along_line_map_unit_scale" )].toString() ) );
1422  }
1423 
1424  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1425  {
1426  destLayer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1427  }
1428  if ( properties.contains( QStringLiteral( "interval_map_unit_scale" ) ) )
1429  {
1430  destLayer->setIntervalMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "interval_map_unit_scale" )].toString() ) );
1431  }
1432 
1433  if ( properties.contains( QStringLiteral( "average_angle_length" ) ) )
1434  {
1435  destLayer->setAverageAngleLength( properties[QStringLiteral( "average_angle_length" )].toDouble() );
1436  }
1437  if ( properties.contains( QStringLiteral( "average_angle_unit" ) ) )
1438  {
1439  destLayer->setAverageAngleUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "average_angle_unit" )].toString() ) );
1440  }
1441  if ( properties.contains( ( QStringLiteral( "average_angle_map_unit_scale" ) ) ) )
1442  {
1443  destLayer->setAverageAngleMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "average_angle_map_unit_scale" )].toString() ) );
1444  }
1445 
1446  if ( properties.contains( QStringLiteral( "placement" ) ) )
1447  {
1448  if ( properties[QStringLiteral( "placement" )] == QLatin1String( "vertex" ) )
1450  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "lastvertex" ) )
1452  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "firstvertex" ) )
1454  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "centralpoint" ) )
1456  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "curvepoint" ) )
1458  else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "segmentcenter" ) )
1460  else
1462  }
1463 
1464  if ( properties.contains( QStringLiteral( "ring_filter" ) ) )
1465  {
1466  destLayer->setRingFilter( static_cast< RenderRingFilter>( properties[QStringLiteral( "ring_filter" )].toInt() ) );
1467  }
1468 
1470 }
1471 
1472 void QgsTemplatedLineSymbolLayerBase::renderPolylineInterval( const QPolygonF &points, QgsSymbolRenderContext &context, double averageOver )
1473 {
1474  if ( points.isEmpty() )
1475  return;
1476 
1477  double lengthLeft = 0; // how much is left until next marker
1478 
1479  QgsRenderContext &rc = context.renderContext();
1480  double interval = mInterval;
1481 
1483  QgsExpressionContextScopePopper scopePopper( context.renderContext().expressionContext(), scope );
1484 
1486  {
1487  context.setOriginalValueVariable( mInterval );
1489  }
1490  if ( interval <= 0 )
1491  {
1492  interval = 0.1;
1493  }
1494  double offsetAlongLine = mOffsetAlongLine;
1496  {
1497  context.setOriginalValueVariable( mOffsetAlongLine );
1499  }
1500 
1501  double painterUnitInterval = rc.convertToPainterUnits( interval, intervalUnit(), intervalMapUnitScale() );
1503  {
1504  // rendering for symbol previews -- an interval in meters in map units can't be calculated, so treat the size as millimeters
1505  // and clamp it to a reasonable range. It's the best we can do in this situation!
1506  painterUnitInterval = std::min( std::max( rc.convertToPainterUnits( interval, QgsUnitTypes::RenderMillimeters ), 10.0 ), 100.0 );
1507  }
1508 
1509  if ( painterUnitInterval < 0 )
1510  return;
1511 
1512  double painterUnitOffsetAlongLine = rc.convertToPainterUnits( offsetAlongLine, offsetAlongLineUnit(), offsetAlongLineMapUnitScale() );
1514  {
1515  // rendering for symbol previews -- an offset in meters in map units can't be calculated, so treat the size as millimeters
1516  // and clamp it to a reasonable range. It's the best we can do in this situation!
1517  painterUnitOffsetAlongLine = std::min( std::max( rc.convertToPainterUnits( offsetAlongLine, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
1518  }
1519 
1520  lengthLeft = painterUnitInterval - painterUnitOffsetAlongLine;
1521 
1522  if ( averageOver > 0 && !qgsDoubleNear( averageOver, 0.0 ) )
1523  {
1524  QVector< QPointF > angleStartPoints;
1525  QVector< QPointF > symbolPoints;
1526  QVector< QPointF > angleEndPoints;
1527 
1528  // we collect 3 arrays of points. These correspond to
1529  // 1. the actual point at which to render the symbol
1530  // 2. the start point of a line averaging the angle over the desired distance (i.e. -averageOver distance from the points in array 1)
1531  // 3. the end point of a line averaging the angle over the desired distance (i.e. +averageOver distance from the points in array 2)
1532  // it gets quite tricky, because for closed rings we need to trace backwards from the initial point to calculate this
1533  // (or trace past the final point)
1534  collectOffsetPoints( points, symbolPoints, painterUnitInterval, lengthLeft );
1535 
1536  if ( symbolPoints.empty() )
1537  {
1538  // no symbols to draw, shortcut out early
1539  return;
1540  }
1541 
1542  if ( symbolPoints.count() > 1 && symbolPoints.constFirst() == symbolPoints.constLast() )
1543  {
1544  // avoid duplicate points at start and end of closed rings
1545  symbolPoints.pop_back();
1546  }
1547 
1548  angleEndPoints.reserve( symbolPoints.size() );
1549  angleStartPoints.reserve( symbolPoints.size() );
1550  if ( averageOver <= painterUnitOffsetAlongLine )
1551  {
1552  collectOffsetPoints( points, angleStartPoints, painterUnitInterval, lengthLeft + averageOver, 0, symbolPoints.size() );
1553  }
1554  else
1555  {
1556  collectOffsetPoints( points, angleStartPoints, painterUnitInterval, 0, averageOver - painterUnitOffsetAlongLine, symbolPoints.size() );
1557  }
1558  collectOffsetPoints( points, angleEndPoints, painterUnitInterval, lengthLeft - averageOver, 0, symbolPoints.size() );
1559 
1560  int pointNum = 0;
1561  for ( int i = 0; i < symbolPoints.size(); ++ i )
1562  {
1563  if ( context.renderContext().renderingStopped() )
1564  break;
1565 
1566  const QPointF pt = symbolPoints[i];
1567  const QPointF startPt = angleStartPoints[i];
1568  const QPointF endPt = angleEndPoints[i];
1569 
1570  MyLine l( startPt, endPt );
1571  // rotate marker (if desired)
1572  if ( rotateSymbols() )
1573  {
1574  setSymbolLineAngle( l.angle() * 180 / M_PI );
1575  }
1576 
1578  renderSymbol( pt, context.feature(), rc, -1, context.selected() );
1579  }
1580  }
1581  else
1582  {
1583  // not averaging line angle -- always use exact section angle
1584  int pointNum = 0;
1585  QPointF lastPt = points[0];
1586  for ( int i = 1; i < points.count(); ++i )
1587  {
1588  if ( context.renderContext().renderingStopped() )
1589  break;
1590 
1591  const QPointF &pt = points[i];
1592 
1593  if ( lastPt == pt ) // must not be equal!
1594  continue;
1595 
1596  // for each line, find out dx and dy, and length
1597  MyLine l( lastPt, pt );
1598  QPointF diff = l.diffForInterval( painterUnitInterval );
1599 
1600  // if there's some length left from previous line
1601  // use only the rest for the first point in new line segment
1602  double c = 1 - lengthLeft / painterUnitInterval;
1603 
1604  lengthLeft += l.length();
1605 
1606  // rotate marker (if desired)
1607  if ( rotateSymbols() )
1608  {
1609  setSymbolLineAngle( l.angle() * 180 / M_PI );
1610  }
1611 
1612  // while we're not at the end of line segment, draw!
1613  while ( lengthLeft > painterUnitInterval )
1614  {
1615  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
1616  lastPt += c * diff;
1617  lengthLeft -= painterUnitInterval;
1619  renderSymbol( lastPt, context.feature(), rc, -1, context.selected() );
1620  c = 1; // reset c (if wasn't 1 already)
1621  }
1622 
1623  lastPt = pt;
1624  }
1625 
1626  }
1627 }
1628 
1629 static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt )
1630 {
1631  // calc average angle between the previous and next point
1632  double a1 = MyLine( prevPt, pt ).angle();
1633  double a2 = MyLine( pt, nextPt ).angle();
1634  double unitX = std::cos( a1 ) + std::cos( a2 ), unitY = std::sin( a1 ) + std::sin( a2 );
1635 
1636  return std::atan2( unitY, unitX );
1637 }
1638 
1639 void QgsTemplatedLineSymbolLayerBase::renderPolylineVertex( const QPolygonF &points, QgsSymbolRenderContext &context, QgsTemplatedLineSymbolLayerBase::Placement placement )
1640 {
1641  if ( points.isEmpty() )
1642  return;
1643 
1644  QgsRenderContext &rc = context.renderContext();
1645 
1646  double origAngle = symbolAngle();
1647  int i = -1, maxCount = 0;
1648  bool isRing = false;
1649 
1651  QgsExpressionContextScopePopper scopePopper( context.renderContext().expressionContext(), scope );
1653 
1654  double offsetAlongLine = mOffsetAlongLine;
1656  {
1657  context.setOriginalValueVariable( mOffsetAlongLine );
1659  }
1660  if ( !qgsDoubleNear( offsetAlongLine, 0.0 ) )
1661  {
1662  //scale offset along line
1664  }
1665 
1666  if ( qgsDoubleNear( offsetAlongLine, 0.0 ) && context.renderContext().geometry()
1668  {
1670  const QgsMapToPixel &mtp = context.renderContext().mapToPixel();
1671 
1672  QgsVertexId vId;
1673  QgsPoint vPoint;
1674  double x, y, z;
1675  QPointF mapPoint;
1676  int pointNum = 0;
1677  while ( context.renderContext().geometry()->nextVertex( vId, vPoint ) )
1678  {
1679  if ( context.renderContext().renderingStopped() )
1680  break;
1681 
1683 
1686  {
1687  //transform
1688  x = vPoint.x();
1689  y = vPoint.y();
1690  z = 0.0;
1691  if ( ct.isValid() )
1692  {
1693  ct.transformInPlace( x, y, z );
1694  }
1695  mapPoint.setX( x );
1696  mapPoint.setY( y );
1697  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1698  if ( rotateSymbols() )
1699  {
1700  double angle = context.renderContext().geometry()->vertexAngle( vId );
1701  setSymbolAngle( angle * 180 / M_PI );
1702  }
1703  renderSymbol( mapPoint, context.feature(), rc, -1, context.selected() );
1704  }
1705  }
1706 
1707  return;
1708  }
1709 
1710  switch ( placement )
1711  {
1712  case FirstVertex:
1713  {
1714  i = 0;
1715  maxCount = 1;
1716  break;
1717  }
1718 
1719  case LastVertex:
1720  {
1721  i = points.count() - 1;
1722  maxCount = points.count();
1723  break;
1724  }
1725 
1726  case Vertex:
1727  case SegmentCenter:
1728  {
1729  i = placement == Vertex ? 0 : 1;
1730  maxCount = points.count();
1731  if ( points.first() == points.last() )
1732  isRing = true;
1733  break;
1734  }
1735 
1736  case Interval:
1737  case CentralPoint:
1738  case CurvePoint:
1739  {
1740  return;
1741  }
1742  }
1743 
1745  {
1746  double distance;
1748  renderOffsetVertexAlongLine( points, i, distance, context );
1749  // restore original rotation
1750  setSymbolAngle( origAngle );
1751 
1752  return;
1753  }
1754 
1755  int pointNum = 0;
1756  QPointF prevPoint;
1757  if ( placement == SegmentCenter && !points.empty() )
1758  prevPoint = points.at( 0 );
1759 
1760  QPointF symbolPoint;
1761  for ( ; i < maxCount; ++i )
1762  {
1764 
1765  if ( isRing && placement == QgsTemplatedLineSymbolLayerBase::Vertex && i == points.count() - 1 )
1766  {
1767  continue; // don't draw the last marker - it has been drawn already
1768  }
1769 
1770  if ( placement == SegmentCenter )
1771  {
1772  QPointF currentPoint = points.at( i );
1773  symbolPoint = QPointF( 0.5 * ( currentPoint.x() + prevPoint.x() ),
1774  0.5 * ( currentPoint.y() + prevPoint.y() ) );
1775  if ( rotateSymbols() )
1776  {
1777  double angle = std::atan2( currentPoint.y() - prevPoint.y(),
1778  currentPoint.x() - prevPoint.x() );
1779  setSymbolAngle( origAngle + angle * 180 / M_PI );
1780  }
1781  prevPoint = currentPoint;
1782  }
1783  else
1784  {
1785  symbolPoint = points.at( i );
1786  // rotate marker (if desired)
1787  if ( rotateSymbols() )
1788  {
1789  double angle = markerAngle( points, isRing, i );
1790  setSymbolAngle( origAngle + angle * 180 / M_PI );
1791  }
1792  }
1793 
1794  renderSymbol( symbolPoint, context.feature(), rc, -1, context.selected() );
1795  }
1796 
1797  // restore original rotation
1798  setSymbolAngle( origAngle );
1799 }
1800 
1801 double QgsTemplatedLineSymbolLayerBase::markerAngle( const QPolygonF &points, bool isRing, int vertex )
1802 {
1803  double angle = 0;
1804  const QPointF &pt = points[vertex];
1805 
1806  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1807  {
1808  int prevIndex = vertex - 1;
1809  int nextIndex = vertex + 1;
1810 
1811  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1812  {
1813  prevIndex = points.count() - 2;
1814  nextIndex = 1;
1815  }
1816 
1817  QPointF prevPoint, nextPoint;
1818  while ( prevIndex >= 0 )
1819  {
1820  prevPoint = points[ prevIndex ];
1821  if ( prevPoint != pt )
1822  {
1823  break;
1824  }
1825  --prevIndex;
1826  }
1827 
1828  while ( nextIndex < points.count() )
1829  {
1830  nextPoint = points[ nextIndex ];
1831  if ( nextPoint != pt )
1832  {
1833  break;
1834  }
1835  ++nextIndex;
1836  }
1837 
1838  if ( prevIndex >= 0 && nextIndex < points.count() )
1839  {
1840  angle = _averageAngle( prevPoint, pt, nextPoint );
1841  }
1842  }
1843  else //no ring and vertex is at start / at end
1844  {
1845  if ( vertex == 0 )
1846  {
1847  while ( vertex < points.size() - 1 )
1848  {
1849  const QPointF &nextPt = points[vertex + 1];
1850  if ( pt != nextPt )
1851  {
1852  angle = MyLine( pt, nextPt ).angle();
1853  return angle;
1854  }
1855  ++vertex;
1856  }
1857  }
1858  else
1859  {
1860  // use last segment's angle
1861  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1862  {
1863  const QPointF &prevPt = points[vertex - 1];
1864  if ( pt != prevPt )
1865  {
1866  angle = MyLine( prevPt, pt ).angle();
1867  return angle;
1868  }
1869  --vertex;
1870  }
1871  }
1872  }
1873  return angle;
1874 }
1875 
1876 void QgsTemplatedLineSymbolLayerBase::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolRenderContext &context )
1877 {
1878  if ( points.isEmpty() )
1879  return;
1880 
1881  QgsRenderContext &rc = context.renderContext();
1882  double origAngle = symbolAngle();
1883  if ( qgsDoubleNear( distance, 0.0 ) )
1884  {
1885  // rotate marker (if desired)
1886  if ( rotateSymbols() )
1887  {
1888  bool isRing = false;
1889  if ( points.first() == points.last() )
1890  isRing = true;
1891  double angle = markerAngle( points, isRing, vertex );
1892  setSymbolAngle( origAngle + angle * 180 / M_PI );
1893  }
1894  renderSymbol( points[vertex], context.feature(), rc, -1, context.selected() );
1895  return;
1896  }
1897 
1898  int pointIncrement = distance > 0 ? 1 : -1;
1899  QPointF previousPoint = points[vertex];
1900  int startPoint = distance > 0 ? std::min( vertex + 1, points.count() - 1 ) : std::max( vertex - 1, 0 );
1901  int endPoint = distance > 0 ? points.count() - 1 : 0;
1902  double distanceLeft = std::fabs( distance );
1903 
1904  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1905  {
1906  const QPointF &pt = points[i];
1907 
1908  if ( previousPoint == pt ) // must not be equal!
1909  continue;
1910 
1911  // create line segment
1912  MyLine l( previousPoint, pt );
1913 
1914  if ( distanceLeft < l.length() )
1915  {
1916  //destination point is in current segment
1917  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1918  // rotate marker (if desired)
1919  if ( rotateSymbols() )
1920  {
1921  setSymbolAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1922  }
1923  renderSymbol( markerPoint, context.feature(), rc, -1, context.selected() );
1924  return;
1925  }
1926 
1927  distanceLeft -= l.length();
1928  previousPoint = pt;
1929  }
1930 
1931  //didn't find point
1932 }
1933 
1934 void QgsTemplatedLineSymbolLayerBase::collectOffsetPoints( const QVector<QPointF> &p, QVector<QPointF> &dest, double intervalPainterUnits, double initialOffset, double initialLag, int numberPointsRequired )
1935 {
1936  if ( p.empty() )
1937  return;
1938 
1939  QVector< QPointF > points = p;
1940  const bool closedRing = points.first() == points.last();
1941 
1942  double lengthLeft = initialOffset;
1943 
1944  double initialLagLeft = initialLag > 0 ? -initialLag : 1; // an initialLagLeft of > 0 signifies end of lagging start points
1945  if ( initialLagLeft < 0 && closedRing )
1946  {
1947  // tracking back around the ring from the first point, insert pseudo vertices before the first vertex
1948  QPointF lastPt = points.constLast();
1949  QVector< QPointF > pseudoPoints;
1950  for ( int i = points.count() - 2; i > 0; --i )
1951  {
1952  if ( initialLagLeft >= 0 )
1953  {
1954  break;
1955  }
1956 
1957  const QPointF &pt = points[i];
1958 
1959  if ( lastPt == pt ) // must not be equal!
1960  continue;
1961 
1962  MyLine l( lastPt, pt );
1963  initialLagLeft += l.length();
1964  lastPt = pt;
1965 
1966  pseudoPoints << pt;
1967  }
1968  std::reverse( pseudoPoints.begin(), pseudoPoints.end() );
1969 
1970  points = pseudoPoints;
1971  points.append( p );
1972  }
1973  else
1974  {
1975  while ( initialLagLeft < 0 )
1976  {
1977  dest << points.constFirst();
1978  initialLagLeft += intervalPainterUnits;
1979  }
1980  }
1981  if ( initialLag > 0 )
1982  {
1983  lengthLeft += intervalPainterUnits - initialLagLeft;
1984  }
1985 
1986  QPointF lastPt = points[0];
1987  for ( int i = 1; i < points.count(); ++i )
1988  {
1989  const QPointF &pt = points[i];
1990 
1991  if ( lastPt == pt ) // must not be equal!
1992  {
1993  if ( closedRing && i == points.count() - 1 && numberPointsRequired > 0 && dest.size() < numberPointsRequired )
1994  {
1995  lastPt = points[0];
1996  i = 0;
1997  }
1998  continue;
1999  }
2000 
2001  // for each line, find out dx and dy, and length
2002  MyLine l( lastPt, pt );
2003  QPointF diff = l.diffForInterval( intervalPainterUnits );
2004 
2005  // if there's some length left from previous line
2006  // use only the rest for the first point in new line segment
2007  double c = 1 - lengthLeft / intervalPainterUnits;
2008 
2009  lengthLeft += l.length();
2010 
2011 
2012  while ( lengthLeft > intervalPainterUnits || qgsDoubleNear( lengthLeft, intervalPainterUnits, 0.000000001 ) )
2013  {
2014  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
2015  lastPt += c * diff;
2016  lengthLeft -= intervalPainterUnits;
2017  dest << lastPt;
2018  c = 1; // reset c (if wasn't 1 already)
2019  if ( numberPointsRequired > 0 && dest.size() >= numberPointsRequired )
2020  break;
2021  }
2022  lastPt = pt;
2023 
2024  if ( numberPointsRequired > 0 && dest.size() >= numberPointsRequired )
2025  break;
2026 
2027  // if a closed ring, we keep looping around the ring until we hit the required number of points
2028  if ( closedRing && i == points.count() - 1 && numberPointsRequired > 0 && dest.size() < numberPointsRequired )
2029  {
2030  lastPt = points[0];
2031  i = 0;
2032  }
2033  }
2034 
2035  if ( !closedRing && numberPointsRequired > 0 && dest.size() < numberPointsRequired )
2036  {
2037  // pad with repeating last point to match desired size
2038  while ( dest.size() < numberPointsRequired )
2039  dest << points.constLast();
2040  }
2041 }
2042 
2043 void QgsTemplatedLineSymbolLayerBase::renderPolylineCentral( const QPolygonF &points, QgsSymbolRenderContext &context, double averageAngleOver )
2044 {
2045  if ( !points.isEmpty() )
2046  {
2047  // calc length
2048  qreal length = 0;
2049  QPolygonF::const_iterator it = points.constBegin();
2050  QPointF last = *it;
2051  for ( ++it; it != points.constEnd(); ++it )
2052  {
2053  length += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
2054  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
2055  last = *it;
2056  }
2057  if ( qgsDoubleNear( length, 0.0 ) )
2058  return;
2059 
2060  const double midPoint = length / 2;
2061 
2062  QPointF pt;
2063  double thisSymbolAngle = 0;
2064 
2065  if ( averageAngleOver > 0 && !qgsDoubleNear( averageAngleOver, 0.0 ) )
2066  {
2067  QVector< QPointF > angleStartPoints;
2068  QVector< QPointF > symbolPoints;
2069  QVector< QPointF > angleEndPoints;
2070  // collectOffsetPoints will have the first point in the line as the first result -- we don't want this, we need the second
2071  collectOffsetPoints( points, symbolPoints, midPoint, midPoint, 0.0, 2 );
2072  collectOffsetPoints( points, angleStartPoints, midPoint, 0, averageAngleOver, 2 );
2073  collectOffsetPoints( points, angleEndPoints, midPoint, midPoint - averageAngleOver, 0, 2 );
2074 
2075  pt = symbolPoints.at( 1 );
2076  MyLine l( angleStartPoints.at( 1 ), angleEndPoints.at( 1 ) );
2077  thisSymbolAngle = l.angle();
2078  }
2079  else
2080  {
2081  // find the segment where the central point lies
2082  it = points.constBegin();
2083  last = *it;
2084  qreal last_at = 0, next_at = 0;
2085  QPointF next;
2086  int segment = 0;
2087  for ( ++it; it != points.constEnd(); ++it )
2088  {
2089  next = *it;
2090  next_at += std::sqrt( ( last.x() - it->x() ) * ( last.x() - it->x() ) +
2091  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
2092  if ( next_at >= midPoint )
2093  break; // we have reached the center
2094  last = *it;
2095  last_at = next_at;
2096  segment++;
2097  }
2098 
2099  // find out the central point on segment
2100  MyLine l( last, next ); // for line angle
2101  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
2102  pt = last + ( next - last ) * k;
2103  thisSymbolAngle = l.angle();
2104  }
2105 
2106  // draw the marker
2107  // rotate marker (if desired)
2108  if ( rotateSymbols() )
2109  {
2110  setSymbolLineAngle( thisSymbolAngle * 180 / M_PI );
2111  }
2112 
2113  renderSymbol( pt, context.feature(), context.renderContext(), -1, context.selected() );
2114 
2115  }
2116 }
2117 
2119 {
2120  return mMarker.get();
2121 }
2122 
2124 {
2125  if ( !symbol || symbol->type() != QgsSymbol::Marker )
2126  {
2127  delete symbol;
2128  return false;
2129  }
2130 
2131  mMarker.reset( static_cast<QgsMarkerSymbol *>( symbol ) );
2132  mColor = mMarker->color();
2133  return true;
2134 }
2135 
2136 
2137 
2138 //
2139 // QgsMarkerLineSymbolLayer
2140 //
2141 
2142 QgsMarkerLineSymbolLayer::QgsMarkerLineSymbolLayer( bool rotateMarker, double interval )
2143  : QgsTemplatedLineSymbolLayerBase( rotateMarker, interval )
2144 {
2145  setSubSymbol( new QgsMarkerSymbol() );
2146 }
2147 
2149 {
2150  bool rotate = DEFAULT_MARKERLINE_ROTATE;
2152 
2153  if ( props.contains( QStringLiteral( "interval" ) ) )
2154  interval = props[QStringLiteral( "interval" )].toDouble();
2155  if ( props.contains( QStringLiteral( "rotate" ) ) )
2156  rotate = ( props[QStringLiteral( "rotate" )].toString() == QLatin1String( "1" ) );
2157 
2158  std::unique_ptr< QgsMarkerLineSymbolLayer > x = qgis::make_unique< QgsMarkerLineSymbolLayer >( rotate, interval );
2159  setCommonProperties( x.get(), props );
2160  return x.release();
2161 }
2162 
2164 {
2165  return QStringLiteral( "MarkerLine" );
2166 }
2167 
2168 void QgsMarkerLineSymbolLayer::setColor( const QColor &color )
2169 {
2170  mMarker->setColor( color );
2171  mColor = color;
2172 }
2173 
2175 {
2176  return mMarker ? mMarker->color() : mColor;
2177 }
2178 
2180 {
2181  // if being rotated, it gets initialized with every line segment
2182  QgsSymbol::RenderHints hints = QgsSymbol::RenderHints();
2183  if ( rotateSymbols() )
2184  hints |= QgsSymbol::DynamicRotation;
2185  mMarker->setRenderHints( hints );
2186 
2187  mMarker->startRender( context.renderContext(), context.fields() );
2188 }
2189 
2191 {
2192  mMarker->stopRender( context.renderContext() );
2193 }
2194 
2195 
2197 {
2198  std::unique_ptr< QgsMarkerLineSymbolLayer > x = qgis::make_unique< QgsMarkerLineSymbolLayer >( rotateSymbols(), interval() );
2199  copyTemplateSymbolProperties( x.get() );
2200  return x.release();
2201 }
2202 
2203 void QgsMarkerLineSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2204 {
2205  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
2206  {
2207  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:LineSymbolizer" ) );
2208  if ( !props.value( QStringLiteral( "uom" ), QString() ).toString().isEmpty() )
2209  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ).toString() );
2210  element.appendChild( symbolizerElem );
2211 
2212  // <Geometry>
2213  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ).toString() );
2214 
2215  QString gap;
2216  switch ( placement() )
2217  {
2219  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "firstPoint" ) ) );
2220  break;
2222  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "lastPoint" ) ) );
2223  break;
2225  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "centralPoint" ) ) );
2226  break;
2228  // no way to get line/polygon's vertices, use a VendorOption
2229  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "placement" ), QStringLiteral( "points" ) ) );
2230  break;
2231  default:
2233  gap = qgsDoubleToString( interval );
2234  break;
2235  }
2236 
2237  if ( !rotateSymbols() )
2238  {
2239  // markers in LineSymbolizer must be drawn following the line orientation,
2240  // use a VendorOption when no marker rotation
2241  symbolizerElem.appendChild( QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "rotateMarker" ), QStringLiteral( "0" ) ) );
2242  }
2243 
2244  // <Stroke>
2245  QDomElement strokeElem = doc.createElement( QStringLiteral( "se:Stroke" ) );
2246  symbolizerElem.appendChild( strokeElem );
2247 
2248  // <GraphicStroke>
2249  QDomElement graphicStrokeElem = doc.createElement( QStringLiteral( "se:GraphicStroke" ) );
2250  strokeElem.appendChild( graphicStrokeElem );
2251 
2252  QgsSymbolLayer *layer = mMarker->symbolLayer( i );
2253  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
2254  if ( !markerLayer )
2255  {
2256  graphicStrokeElem.appendChild( doc.createComment( QStringLiteral( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() ) ) );
2257  }
2258  else
2259  {
2260  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
2261  }
2262 
2263  if ( !gap.isEmpty() )
2264  {
2265  QDomElement gapElem = doc.createElement( QStringLiteral( "se:Gap" ) );
2266  QgsSymbolLayerUtils::createExpressionElement( doc, gapElem, gap );
2267  graphicStrokeElem.appendChild( gapElem );
2268  }
2269 
2270  if ( !qgsDoubleNear( mOffset, 0.0 ) )
2271  {
2272  QDomElement perpOffsetElem = doc.createElement( QStringLiteral( "se:PerpendicularOffset" ) );
2274  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
2275  symbolizerElem.appendChild( perpOffsetElem );
2276  }
2277  }
2278 }
2279 
2281 {
2282  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2283 
2284  QDomElement strokeElem = element.firstChildElement( QStringLiteral( "Stroke" ) );
2285  if ( strokeElem.isNull() )
2286  return nullptr;
2287 
2288  QDomElement graphicStrokeElem = strokeElem.firstChildElement( QStringLiteral( "GraphicStroke" ) );
2289  if ( graphicStrokeElem.isNull() )
2290  return nullptr;
2291 
2292  // retrieve vendor options
2293  bool rotateMarker = true;
2295 
2296  QgsStringMap vendorOptions = QgsSymbolLayerUtils::getVendorOptionList( element );
2297  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
2298  {
2299  if ( it.key() == QLatin1String( "placement" ) )
2300  {
2301  if ( it.value() == QLatin1String( "points" ) )
2303  else if ( it.value() == QLatin1String( "firstPoint" ) )
2305  else if ( it.value() == QLatin1String( "lastPoint" ) )
2307  else if ( it.value() == QLatin1String( "centralPoint" ) )
2309  }
2310  else if ( it.value() == QLatin1String( "rotateMarker" ) )
2311  {
2312  rotateMarker = it.value() == QLatin1String( "0" );
2313  }
2314  }
2315 
2316  std::unique_ptr< QgsMarkerSymbol > marker;
2317 
2319  if ( l )
2320  {
2321  QgsSymbolLayerList layers;
2322  layers.append( l );
2323  marker.reset( new QgsMarkerSymbol( layers ) );
2324  }
2325 
2326  if ( !marker )
2327  return nullptr;
2328 
2329  double interval = 0.0;
2330  QDomElement gapElem = graphicStrokeElem.firstChildElement( QStringLiteral( "Gap" ) );
2331  if ( !gapElem.isNull() )
2332  {
2333  bool ok;
2334  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
2335  if ( ok )
2336  interval = d;
2337  }
2338 
2339  double offset = 0.0;
2340  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( QStringLiteral( "PerpendicularOffset" ) );
2341  if ( !perpOffsetElem.isNull() )
2342  {
2343  bool ok;
2344  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
2345  if ( ok )
2346  offset = d;
2347  }
2348 
2349  QString uom = element.attribute( QStringLiteral( "uom" ) );
2352 
2354  x->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2355  x->setPlacement( placement );
2356  x->setInterval( interval );
2357  x->setSubSymbol( marker.release() );
2358  x->setOffset( offset );
2359  return x;
2360 }
2361 
2363 {
2364  mMarker->setSize( width );
2365 }
2366 
2368 {
2369  if ( key == QgsSymbolLayer::PropertyWidth && mMarker && property )
2370  {
2371  mMarker->setDataDefinedSize( property );
2372  }
2374 }
2375 
2376 void QgsMarkerLineSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context )
2377 {
2378  const double prevOpacity = mMarker->opacity();
2379  mMarker->setOpacity( mMarker->opacity() * context.opacity() );
2381  mMarker->setOpacity( prevOpacity );
2382 }
2383 
2385 {
2386  mMarker->setLineAngle( angle );
2387 }
2388 
2390 {
2391  return mMarker->angle();
2392 }
2393 
2395 {
2396  mMarker->setAngle( angle );
2397 }
2398 
2399 void QgsMarkerLineSymbolLayer::renderSymbol( const QPointF &point, const QgsFeature *feature, QgsRenderContext &context, int layer, bool selected )
2400 {
2401  mMarker->renderPoint( point, feature, context, layer, selected );
2402 }
2403 
2405 {
2406  return mMarker->size();
2407 }
2408 
2410 {
2411  return mMarker->size( context );
2412 }
2413 
2415 {
2417  mMarker->setOutputUnit( unit );
2418  setIntervalUnit( unit );
2419  mOffsetUnit = unit;
2420  setOffsetAlongLineUnit( unit );
2421 }
2422 
2424 {
2430  || ( mMarker && mMarker->usesMapUnits() );
2431 }
2432 
2433 QSet<QString> QgsMarkerLineSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
2434 {
2435  QSet<QString> attr = QgsLineSymbolLayer::usedAttributes( context );
2436  if ( mMarker )
2437  attr.unite( mMarker->usedAttributes( context ) );
2438  return attr;
2439 }
2440 
2442 {
2444  return true;
2445  if ( mMarker && mMarker->hasDataDefinedProperties() )
2446  return true;
2447  return false;
2448 }
2449 
2451 {
2452  return ( mMarker->size( context ) / 2.0 ) +
2454 }
2455 
2456 
2457 //
2458 // QgsHashedLineSymbolLayer
2459 //
2460 
2461 QgsHashedLineSymbolLayer::QgsHashedLineSymbolLayer( bool rotateSymbol, double interval )
2462  : QgsTemplatedLineSymbolLayerBase( rotateSymbol, interval )
2463 {
2464  setSubSymbol( new QgsLineSymbol() );
2465 }
2466 
2468 {
2469  bool rotate = DEFAULT_MARKERLINE_ROTATE;
2471 
2472  if ( props.contains( QStringLiteral( "interval" ) ) )
2473  interval = props[QStringLiteral( "interval" )].toDouble();
2474  if ( props.contains( QStringLiteral( "rotate" ) ) )
2475  rotate = ( props[QStringLiteral( "rotate" )] == QLatin1String( "1" ) );
2476 
2477  std::unique_ptr< QgsHashedLineSymbolLayer > x = qgis::make_unique< QgsHashedLineSymbolLayer >( rotate, interval );
2478  setCommonProperties( x.get(), props );
2479  if ( props.contains( QStringLiteral( "hash_angle" ) ) )
2480  {
2481  x->setHashAngle( props[QStringLiteral( "hash_angle" )].toDouble() );
2482  }
2483 
2484  if ( props.contains( QStringLiteral( "hash_length" ) ) )
2485  x->setHashLength( props[QStringLiteral( "hash_length" )].toDouble() );
2486 
2487  if ( props.contains( QStringLiteral( "hash_length_unit" ) ) )
2488  x->setHashLengthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "hash_length_unit" )].toString() ) );
2489 
2490  if ( props.contains( QStringLiteral( "hash_length_map_unit_scale" ) ) )
2491  x->setHashLengthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "hash_length_map_unit_scale" )].toString() ) );
2492 
2493  return x.release();
2494 }
2495 
2497 {
2498  return QStringLiteral( "HashLine" );
2499 }
2500 
2502 {
2503  // if being rotated, it gets initialized with every line segment
2504  QgsSymbol::RenderHints hints = QgsSymbol::RenderHints();
2505  if ( rotateSymbols() )
2506  hints |= QgsSymbol::DynamicRotation;
2507  mHashSymbol->setRenderHints( hints );
2508 
2509  mHashSymbol->startRender( context.renderContext(), context.fields() );
2510 }
2511 
2513 {
2514  mHashSymbol->stopRender( context.renderContext() );
2515 }
2516 
2518 {
2519  QVariantMap map = QgsTemplatedLineSymbolLayerBase::properties();
2520  map[ QStringLiteral( "hash_angle" ) ] = QString::number( mHashAngle );
2521 
2522  map[QStringLiteral( "hash_length" )] = QString::number( mHashLength );
2523  map[QStringLiteral( "hash_length_unit" )] = QgsUnitTypes::encodeUnit( mHashLengthUnit );
2524  map[QStringLiteral( "hash_length_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mHashLengthMapUnitScale );
2525 
2526  return map;
2527 }
2528 
2530 {
2531  std::unique_ptr< QgsHashedLineSymbolLayer > x = qgis::make_unique< QgsHashedLineSymbolLayer >( rotateSymbols(), interval() );
2532  copyTemplateSymbolProperties( x.get() );
2533  x->setHashAngle( mHashAngle );
2534  x->setHashLength( mHashLength );
2535  x->setHashLengthUnit( mHashLengthUnit );
2536  x->setHashLengthMapUnitScale( mHashLengthMapUnitScale );
2537  return x.release();
2538 }
2539 
2540 void QgsHashedLineSymbolLayer::setColor( const QColor &color )
2541 {
2542  mHashSymbol->setColor( color );
2543  mColor = color;
2544 }
2545 
2547 {
2548  return mHashSymbol ? mHashSymbol->color() : mColor;
2549 }
2550 
2552 {
2553  return mHashSymbol.get();
2554 }
2555 
2557 {
2558  if ( !symbol || symbol->type() != QgsSymbol::Line )
2559  {
2560  delete symbol;
2561  return false;
2562  }
2563 
2564  mHashSymbol.reset( static_cast<QgsLineSymbol *>( symbol ) );
2565  mColor = mHashSymbol->color();
2566  return true;
2567 }
2568 
2569 void QgsHashedLineSymbolLayer::setWidth( const double width )
2570 {
2571  mHashLength = width;
2572 }
2573 
2575 {
2576  return mHashLength;
2577 }
2578 
2580 {
2581  return context.convertToPainterUnits( mHashLength, mHashLengthUnit, mHashLengthMapUnitScale );
2582 }
2583 
2585 {
2586  return ( mHashSymbol->width( context ) / 2.0 )
2587  + context.convertToPainterUnits( mHashLength, mHashLengthUnit, mHashLengthMapUnitScale )
2588  + context.convertToPainterUnits( std::fabs( mOffset ), mOffsetUnit, mOffsetMapUnitScale );
2589 }
2590 
2592 {
2594  mHashSymbol->setOutputUnit( unit );
2595  setIntervalUnit( unit );
2596  mOffsetUnit = unit;
2597  setOffsetAlongLineUnit( unit );
2598 }
2599 
2600 QSet<QString> QgsHashedLineSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
2601 {
2602  QSet<QString> attr = QgsLineSymbolLayer::usedAttributes( context );
2603  if ( mHashSymbol )
2604  attr.unite( mHashSymbol->usedAttributes( context ) );
2605  return attr;
2606 }
2607 
2609 {
2611  return true;
2612  if ( mHashSymbol && mHashSymbol->hasDataDefinedProperties() )
2613  return true;
2614  return false;
2615 }
2616 
2618 {
2619  if ( key == QgsSymbolLayer::PropertyWidth && mHashSymbol && property )
2620  {
2621  mHashSymbol->setDataDefinedWidth( property );
2622  }
2624 }
2625 
2627 {
2628  return mHashLengthUnit == QgsUnitTypes::RenderMapUnits || mHashLengthUnit == QgsUnitTypes::RenderMetersInMapUnits
2634  || ( mHashSymbol && mHashSymbol->usesMapUnits() );
2635 }
2636 
2638 {
2639  mSymbolLineAngle = angle;
2640 }
2641 
2643 {
2644  return mSymbolAngle;
2645 }
2646 
2648 {
2649  mSymbolAngle = angle;
2650 }
2651 
2652 void QgsHashedLineSymbolLayer::renderSymbol( const QPointF &point, const QgsFeature *feature, QgsRenderContext &context, int layer, bool selected )
2653 {
2654  double lineLength = mHashLength;
2656  {
2657  context.expressionContext().setOriginalValueVariable( mHashLength );
2659  }
2660  const double w = context.convertToPainterUnits( lineLength, mHashLengthUnit, mHashLengthMapUnitScale ) / 2.0;
2661 
2662  double hashAngle = mHashAngle;
2664  {
2665  context.expressionContext().setOriginalValueVariable( mHashAngle );
2667  }
2668 
2669  QgsPointXY center( point );
2670  QgsPointXY start = center.project( w, 180 - ( mSymbolAngle + mSymbolLineAngle + hashAngle ) );
2671  QgsPointXY end = center.project( -w, 180 - ( mSymbolAngle + mSymbolLineAngle + hashAngle ) );
2672 
2673  QPolygonF points;
2674  points << QPointF( start.x(), start.y() ) << QPointF( end.x(), end.y() );
2675 
2676 
2677  mHashSymbol->renderPolyline( points, feature, context, layer, selected );
2678 }
2679 
2681 {
2682  return mHashAngle;
2683 }
2684 
2686 {
2687  mHashAngle = angle;
2688 }
2689 
2690 void QgsHashedLineSymbolLayer::renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context )
2691 {
2692  const double prevOpacity = mHashSymbol->opacity();
2693  mHashSymbol->setOpacity( mHashSymbol->opacity() * context.opacity() );
2695  mHashSymbol->setOpacity( prevOpacity );
2696 }
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...
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
virtual bool hasCurvedSegments() const
Returns true if the geometry contains curved segments.
virtual bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const =0
Returns next vertex id and coordinates.
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.
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.
Class for doing transforms between two map coordinate systems.
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...
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Curve polygon geometry type.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
Exports QGIS layers to the DXF format.
Definition: qgsdxfexport.h:64
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:228
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
RAII class to pop scope from an expression context on destruction.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
static const QString EXPR_GEOMETRY_POINT_COUNT
Inbuilt variable name for point count variable.
static const QString EXPR_GEOMETRY_POINT_NUM
Inbuilt variable name for point number variable.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
Line symbol layer type which draws repeating line sections along a line feature.
double hashAngle() const
Returns the angle to use when drawing the hashed lines sections, in degrees clockwise.
QgsHashedLineSymbolLayer(bool rotateSymbol=true, double interval=3)
Constructor for QgsHashedLineSymbolLayer.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
void setWidth(double width) override
Sets the width of the line symbol layer.
double symbolAngle() const override
Returns the symbol's current angle, in degrees clockwise.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
double width() const override
Returns the estimated width for the line symbol layer.
void setDataDefinedProperty(QgsSymbolLayer::Property key, const QgsProperty &property) override
Sets a data defined property for the layer.
void setSymbolLineAngle(double angle) override
Sets the line angle modification for the symbol's angle.
void setSymbolAngle(double angle) override
Sets the symbol's angle, in degrees clockwise.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsHashedLineSymbolLayer, using the settings serialized in the properties map (correspo...
void renderPolyline(const QPolygonF &points, QgsSymbolRenderContext &context) override
Renders the line symbol layer along the line joining points, using the given render context.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QgsHashedLineSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
QColor color() const override
The fill color.
void renderSymbol(const QPointF &point, const QgsFeature *feature, QgsRenderContext &context, int layer=-1, bool selected=false) override
Renders the templated symbol at the specified point, using the given render context.
void setColor(const QColor &color) override
The fill color.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QString layerType() const override
Returns a string that represents this layer type.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setHashAngle(double angle)
Sets the angle to use when drawing the hashed lines sections, in degrees clockwise.
RenderRingFilter
Options for filtering rings when the line symbol layer is being used to render a polygon's rings.
@ ExteriorRingOnly
Render the exterior ring only.
@ InteriorRingsOnly
Render the interior rings only.
@ AllRings
Render both exterior and interior rings.
QgsMapUnitScale mWidthMapUnitScale
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QgsUnitTypes::RenderUnit mWidthUnit
QgsUnitTypes::RenderUnit offsetUnit() const
Returns the units for the line's offset.
void setWidthMapUnitScale(const QgsMapUnitScale &scale)
void setWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the line's width.
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit for the line's offset.
void setOffset(double offset)
Sets the line's offset.
RenderRingFilter mRingFilter
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsUnitTypes::RenderUnit widthUnit() const
Returns the units for the line's width.
virtual double width() const
Returns the estimated width for the line symbol layer.
QgsMapUnitScale mOffsetMapUnitScale
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the line's offset.
QgsMapUnitScale mapUnitScale() const override
void setRingFilter(QgsLineSymbolLayer::RenderRingFilter filter)
Sets the line symbol layer's ring filter, which controls which rings are rendered when the line symbo...
void setMapUnitScale(const QgsMapUnitScale &scale) override
double offset() const
Returns the line's offset.
QgsUnitTypes::RenderUnit mOffsetUnit
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgssymbol.h:1204
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
double mapUnitsPerPixel() const
Returns current map units per pixel.
void transformInPlace(double &x, double &y) const
Transforms device coordinates to map coordinates.
Struct for storing maximum and minimum scales for measurements in map units.
Line symbol layer type which draws repeating marker symbols along a line feature.
Q_DECL_DEPRECATED bool rotateMarker() const
Shall the marker be rotated.
std::unique_ptr< QgsMarkerSymbol > mMarker
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QgsMarkerLineSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
void setDataDefinedProperty(QgsSymbolLayer::Property key, const QgsProperty &property) override
Sets a data defined property for the layer.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
void setColor(const QColor &color) override
The fill color.
double symbolAngle() const override
Returns the symbol's current angle, in degrees clockwise.
double width() const override
Returns the estimated width for the line symbol layer.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsMarkerLineSymbolLayer, using the settings serialized in the properties map (correspo...
QgsMarkerLineSymbolLayer(bool rotateMarker=DEFAULT_MARKERLINE_ROTATE, double interval=DEFAULT_MARKERLINE_INTERVAL)
Constructor for QgsMarkerLineSymbolLayer.
void setWidth(double width) override
Sets the width of the line symbol layer.
QColor color() const override
The fill color.
void setSymbolLineAngle(double angle) override
Sets the line angle modification for the symbol's angle.
void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Saves the symbol layer as SLD.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString layerType() const override
Returns a string that represents this layer type.
void renderPolyline(const QPolygonF &points, QgsSymbolRenderContext &context) override
Renders the line symbol layer along the line joining points, using the given render context.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsMarkerLineSymbolLayer from an SLD XML DOM element.
void setSymbolAngle(double angle) override
Sets the symbol's angle, in degrees clockwise.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void renderSymbol(const QPointF &point, const QgsFeature *feature, QgsRenderContext &context, int layer=-1, bool selected=false) override
Renders the templated symbol at the specified point, using the given render context.
Abstract base class for marker symbol layers.
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const
Writes the symbol layer definition as a SLD XML element.
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:1004
A class to represent a 2D point.
Definition: qgspointxy.h:44
QgsPointXY project(double distance, double bearing) const SIP_HOLDGIL
Returns a new point which corresponds to this point projected by a specified distance in a specified ...
Definition: qgspointxy.cpp:87
double y
Definition: qgspointxy.h:48
Q_GADGET double x
Definition: qgspointxy.h:47
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:38
Q_GADGET double x
Definition: qgspoint.h:41
double y
Definition: qgspoint.h:42
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.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
A store for object properties.
Definition: qgsproperty.h:232
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
const QgsAbstractGeometry * geometry() const
Returns pointer to the unsegmentized geometry.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
void setGeometry(const QgsAbstractGeometry *geometry)
Sets pointer to original (unsegmentized) geometry.
QColor selectionColor() const
Returns the color to use when rendering selected features.
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
Flags flags() const
Returns combination of flags used for rendering.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
const QgsVectorSimplifyMethod & vectorSimplifyMethod() const
Returns the simplification settings to use when rendering vector layers.
Scoped object for saving and restoring a QPainter object's state.
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void setDrawInsidePolygon(bool drawInsidePolygon)
Sets whether the line should only be drawn inside polygons, and any portion of the line which falls o...
bool tweakDashPatternOnCorners() const
Returns true if dash patterns tweaks should be applied on sharp corners, to ensure that a double-leng...
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void setPenCapStyle(Qt::PenCapStyle style)
Sets the pen cap style used to render the line (e.g.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QgsMapUnitScale mapUnitScale() const override
QVector< qreal > dxfCustomDashPattern(QgsUnitTypes::RenderUnit &unit) const override
Gets dash pattern.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsSimpleLineSymbolLayer, using the settings serialized in the properties map (correspo...
Qt::PenJoinStyle penJoinStyle() const
Returns the pen join style used to render the line (e.g.
void renderPolygonStroke(const QPolygonF &points, const QVector< QPolygonF > *rings, QgsSymbolRenderContext &context) override
Renders the line symbol layer along the outline of polygon, using the given render context.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setDashPatternOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit for the dash pattern offset.
Qt::PenStyle dxfPenStyle() const override
Gets pen style.
void setCustomDashPatternMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for lengths used in the custom dash pattern.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
QVector< qreal > customDashVector() const
Returns the custom dash vector, which is the pattern of alternating drawn/skipped lengths used while ...
void setCustomDashPatternUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit for lengths used in the custom dash pattern.
double dxfOffset(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets offset.
QgsSimpleLineSymbolLayer(const QColor &color=DEFAULT_SIMPLELINE_COLOR, double width=DEFAULT_SIMPLELINE_WIDTH, Qt::PenStyle penStyle=DEFAULT_SIMPLELINE_PENSTYLE)
Constructor for QgsSimpleLineSymbolLayer.
void setUseCustomDashPattern(bool b)
Sets whether the line uses a custom dash pattern.
void setTweakDashPatternOnCorners(bool enabled)
Sets whether dash patterns tweaks should be applied on sharp corners, to ensure that a double-length ...
bool canCauseArtifactsBetweenAdjacentTiles() const override
Returns true if the symbol layer rendering can cause visible artifacts across a single feature when t...
void setCustomDashVector(const QVector< qreal > &vector)
Sets the custom dash vector, which is the pattern of alternating drawn/skipped lengths used while ren...
void setDashPatternOffset(double offset)
Sets the dash pattern offset, which dictates how far along the dash pattern the pattern should start ...
QColor dxfColor(QgsSymbolRenderContext &context) const override
Gets color.
QString layerType() const override
Returns a string that represents this layer type.
void setDashPatternOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the dash pattern offset.
QgsSimpleLineSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Qt::PenStyle penStyle() const
Returns the pen style used to render the line (e.g.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the pen join style used to render the line (e.g.
void setAlignDashPattern(bool enabled)
Sets whether dash patterns should be aligned to the start and end of lines, by applying subtle tweaks...
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSimpleLineSymbolLayer from an SLD XML DOM element.
double dxfWidth(const QgsDxfExport &e, QgsSymbolRenderContext &context) const override
Gets line width.
Qt::PenCapStyle penCapStyle() const
Returns the pen cap style used to render the line (e.g.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void renderPolyline(const QPolygonF &points, QgsSymbolRenderContext &context) override
Renders the line symbol layer along the line joining points, using the given render context.
void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Saves the symbol layer as SLD.
void setMapUnitScale(const QgsMapUnitScale &scale) override
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
bool alignDashPattern() const
Returns true if dash patterns should be aligned to the start and end of lines, by applying subtle twe...
static QString encodePenStyle(Qt::PenStyle style)
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static bool createExpressionElement(QDomDocument &doc, QDomElement &element, const QString &function)
Creates a OGC Expression element based on the provided function expression.
static QColor decodeColor(const QString &str)
static double rescaleUom(double size, QgsUnitTypes::RenderUnit unit, const QVariantMap &props)
Rescales the given size based on the uomScale found in the props, if any is found,...
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static bool isSharpCorner(QPointF p1, QPointF p2, QPointF p3)
Returns true if the angle formed by the line p1 - p2 - p3 forms a "sharp" corner.
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.
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
static QVector< qreal > decodeRealVector(const QString &s)
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)
static QString encodePenCapStyle(Qt::PenCapStyle style)
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)
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
static double sizeInPixelsFromSldUom(const QString &uom, double size)
Returns the size scaled in pixels according to the uom attribute.
static void appendPolyline(QPolygonF &target, const QPolygonF &line)
Appends a polyline line to an existing target polyline.
static QString encodeColor(const QColor &color)
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
static Qt::PenStyle decodePenStyle(const QString &str)
static QgsSymbolLayer * createMarkerLayerFromSld(QDomElement &element)
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
static QgsStringMap getVendorOptionList(QDomElement &element)
static QPolygonF polylineSubstring(const QPolygonF &polyline, double startOffset, double endOffset)
Returns the substring of a polyline which starts at startOffset from the beginning of the line and en...
static QString encodeRealVector(const QVector< qreal > &v)
Property
Data definable properties.
@ PropertyStrokeStyle
Stroke style (eg solid, dashed)
@ PropertyPlacement
Line marker placement.
@ PropertyCapStyle
Line cap style.
@ PropertyLineDistance
Distance between lines, or length of lines for hash line symbols.
@ PropertyOffsetAlongLine
Offset along line.
@ PropertyCustomDash
Custom dash pattern.
@ PropertyJoinStyle
Line join style.
@ PropertyLineAngle
Line angle, or angle of hash lines for hash line symbols.
@ PropertyOffset
Symbol offset.
@ PropertyStrokeWidth
Stroke width.
@ PropertyDashPatternOffset
Dash pattern offset.
@ PropertyAverageAngleLength
Length to average symbol angles over.
@ PropertyInterval
Line marker interval.
@ PropertyStrokeColor
Stroke color.
@ PropertyWidth
Symbol width.
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
virtual QColor color() const
The fill color.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
virtual QgsSymbolLayer * clone() const =0
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool enabled() const
Returns true if symbol layer is enabled and will be drawn.
virtual QString layerType() const =0
Returns a string that represents this layer type.
virtual void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the layer.
virtual QgsSymbol * subSymbol()
Returns the symbol's sub symbol, if present.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer's property collection, used for data defined overrides.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
QgsPropertyCollection mDataDefinedProperties
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
virtual bool setSubSymbol(QgsSymbol *symbol)
Sets layer's subsymbol. takes ownership of the passed symbol.
QgsFields fields() const
Fields of the layer.
Definition: qgssymbol.h:900
bool selected() const
Returns true if symbols should be rendered using the selected symbol coloring and style.
Definition: qgssymbol.h:850
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgssymbol.h:794
QgsWkbTypes::GeometryType originalGeometryType() const
Returns the geometry type for the original feature geometry being rendered.
Definition: qgssymbol.h:892
const QgsFeature * feature() const
Returns the current feature being rendered.
Definition: qgssymbol.h:875
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
Definition: qgssymbol.cpp:1508
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:837
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:65
SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:138
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition: qgssymbol.h:107
@ Line
Line symbol.
Definition: qgssymbol.h:89
@ Marker
Marker symbol.
Definition: qgssymbol.h:88
Base class for templated line symbols, e.g.
bool rotateSymbols() const
Returns true if the repeating symbols be rotated to match their line segment orientation.
static void setCommonProperties(QgsTemplatedLineSymbolLayerBase *destLayer, const QVariantMap &properties)
Sets all common symbol properties in the destLayer, using the settings serialized in the properties m...
const QgsMapUnitScale & intervalMapUnitScale() const
Returns the map unit scale for the interval between symbols.
bool canCauseArtifactsBetweenAdjacentTiles() const override
Returns true if the symbol layer rendering can cause visible artifacts across a single feature when t...
QgsMapUnitScale mapUnitScale() const FINAL
void setMapUnitScale(const QgsMapUnitScale &scale) FINAL
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
Placement placement() const
Returns the placement of the symbols.
Placement
Defines how/where the templated symbol should be placed on the line.
@ Vertex
Place symbols on every vertex in the line.
@ LastVertex
Place symbols on the last vertex in the line.
@ CentralPoint
Place symbols at the mid point of the line.
@ FirstVertex
Place symbols on the first vertex in the line.
@ SegmentCenter
Place symbols at the center of every line segment.
@ Interval
Place symbols at regular intervals.
@ CurvePoint
Place symbols at every virtual curve point in the line (used when rendering curved geometry types onl...
double interval() const
Returns the interval between individual symbols.
void setAverageAngleMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the length over which the line's direction is averaged when calculating i...
double offsetAlongLine() const
Returns the offset along the line for the symbol placement.
void copyTemplateSymbolProperties(QgsTemplatedLineSymbolLayerBase *destLayer) const
Copies all common properties of this layer to another templated symbol layer.
void setOffsetAlongLine(double offsetAlongLine)
Sets the the offset along the line for the symbol placement.
void renderPolygonStroke(const QPolygonF &points, const QVector< QPolygonF > *rings, QgsSymbolRenderContext &context) FINAL
Renders the line symbol layer along the outline of polygon, using the given render context.
QgsUnitTypes::RenderUnit outputUnit() const FINAL
Returns the units to use for sizes and widths within the symbol layer.
QgsTemplatedLineSymbolLayerBase(bool rotateSymbol=true, double interval=3)
Constructor for QgsTemplatedLineSymbolLayerBase.
virtual void setSymbolLineAngle(double angle)=0
Sets the line angle modification for the symbol's angle.
void setPlacement(Placement placement)
Sets the placement of the symbols.
virtual double symbolAngle() const =0
Returns the symbol's current angle, in degrees clockwise.
virtual void setSymbolAngle(double angle)=0
Sets the symbol's angle, in degrees clockwise.
void setOffsetAlongLineUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit used for calculating the offset along line for symbols.
void setInterval(double interval)
Sets the interval between individual symbols.
const QgsMapUnitScale & offsetAlongLineMapUnitScale() const
Returns the map unit scale used for calculating the offset in map units along line for symbols.
QgsUnitTypes::RenderUnit averageAngleUnit() const
Returns the unit for the length over which the line's direction is averaged when calculating individu...
void renderPolyline(const QPolygonF &points, QgsSymbolRenderContext &context) override
Renders the line symbol layer along the line joining points, using the given render context.
QgsUnitTypes::RenderUnit offsetAlongLineUnit() const
Returns the unit used for calculating the offset along line for symbols.
QgsUnitTypes::RenderUnit intervalUnit() const
Returns the units for the interval between symbols.
void setAverageAngleUnit(QgsUnitTypes::RenderUnit unit)
Sets the unit for the length over which the line's direction is averaged when calculating individual ...
void setIntervalUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the interval between symbols.
virtual void renderSymbol(const QPointF &point, const QgsFeature *feature, QgsRenderContext &context, int layer=-1, bool selected=false)=0
Renders the templated symbol at the specified point, using the given render context.
void setIntervalMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the interval between symbols.
void setOffsetAlongLineMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale used for calculating the offset in map units along line for symbols.
void setAverageAngleLength(double length)
Sets the length of line over which the line's direction is averaged when calculating individual symbo...
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:167
@ RenderUnknownUnit
Mixed or unknown units.
Definition: qgsunittypes.h:174
@ RenderMetersInMapUnits
Meters value as Map units.
Definition: qgsunittypes.h:175
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:168
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:169
SimplifyHints simplifyHints() const
Gets the simplification hints of the vector layer managed.
float threshold() const
Gets the simplification threshold of the vector layer managed.
@ AntialiasingSimplification
The geometries can be rendered with 'AntiAliasing' disabled because of it is '1-pixel size'.
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
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
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:276
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:316
QMap< QString, QString > QgsStringMap
Definition: qgis.h:759
#define DEFAULT_MARKERLINE_INTERVAL
#define DEFAULT_SIMPLELINE_WIDTH
#define DEFAULT_MARKERLINE_ROTATE
#define DEFAULT_SIMPLELINE_PENSTYLE
#define DEFAULT_SIMPLELINE_COLOR
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:54
QList< QPolygonF > offsetLine(QPolygonF polyline, double dist, QgsWkbTypes::GeometryType geometryType)
calculate geometry shifted by a specified distance
Single variable definition for use within a QgsExpressionContextScope.
Utility class for identifying a unique vertex within a geometry.
VertexType type
Vertex type.
@ SegmentVertex
The actual start or end point of a segment.
@ CurveVertex
An intermediate point on a segment defining the curvature of the segment.