QGIS API Documentation  2.14.11-Essen
qgslinesymbollayerv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslinesymbollayerv2.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 "qgslinesymbollayerv2.h"
17 #include "qgscurvev2.h"
18 #include "qgscurvepolygonv2.h"
19 #include "qgsdxfexport.h"
20 #include "qgssymbollayerv2utils.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 
28 #include <QPainter>
29 #include <QDomDocument>
30 #include <QDomElement>
31 
32 #include <cmath>
33 
34 QgsSimpleLineSymbolLayerV2::QgsSimpleLineSymbolLayerV2( const QColor& color, double width, Qt::PenStyle penStyle )
35  : mPenStyle( penStyle )
36  , mPenJoinStyle( DEFAULT_SIMPLELINE_JOINSTYLE )
37  , mPenCapStyle( DEFAULT_SIMPLELINE_CAPSTYLE )
38  , mUseCustomDashPattern( false )
39  , mCustomDashPatternUnit( QgsSymbolV2::MM )
40  , mDrawInsidePolygon( false )
41 {
42  mColor = color;
43  mWidth = width;
44  mCustomDashVector << 5 << 2;
45 }
46 
48 {
50  mWidthUnit = unit;
51  mOffsetUnit = unit;
53 }
54 
56 {
58  if ( mWidthUnit != unit || mOffsetUnit != unit || mCustomDashPatternUnit != unit )
59  {
60  return QgsSymbolV2::Mixed;
61  }
62  return unit;
63 }
64 
66 {
68  mWidthMapUnitScale = scale;
69  mOffsetMapUnitScale = scale;
71 }
72 
74 {
78  {
79  return mWidthMapUnitScale;
80  }
81  return QgsMapUnitScale();
82 }
83 
85 {
89 
90  if ( props.contains( "line_color" ) )
91  {
92  color = QgsSymbolLayerV2Utils::decodeColor( props["line_color"] );
93  }
94  else if ( props.contains( "outline_color" ) )
95  {
96  color = QgsSymbolLayerV2Utils::decodeColor( props["outline_color"] );
97  }
98  else if ( props.contains( "color" ) )
99  {
100  //pre 2.5 projects used "color"
101  color = QgsSymbolLayerV2Utils::decodeColor( props["color"] );
102  }
103  if ( props.contains( "line_width" ) )
104  {
105  width = props["line_width"].toDouble();
106  }
107  else if ( props.contains( "outline_width" ) )
108  {
109  width = props["outline_width"].toDouble();
110  }
111  else if ( props.contains( "width" ) )
112  {
113  //pre 2.5 projects used "width"
114  width = props["width"].toDouble();
115  }
116  if ( props.contains( "line_style" ) )
117  {
118  penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["line_style"] );
119  }
120  else if ( props.contains( "outline_style" ) )
121  {
122  penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["outline_style"] );
123  }
124  else if ( props.contains( "penstyle" ) )
125  {
126  penStyle = QgsSymbolLayerV2Utils::decodePenStyle( props["penstyle"] );
127  }
128 
129  QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
130  if ( props.contains( "line_width_unit" ) )
131  {
132  l->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["line_width_unit"] ) );
133  }
134  else if ( props.contains( "outline_width_unit" ) )
135  {
136  l->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["outline_width_unit"] ) );
137  }
138  else if ( props.contains( "width_unit" ) )
139  {
140  //pre 2.5 projects used "width_unit"
141  l->setWidthUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["width_unit"] ) );
142  }
143  if ( props.contains( "width_map_unit_scale" ) )
144  l->setWidthMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["width_map_unit_scale"] ) );
145  if ( props.contains( "offset" ) )
146  l->setOffset( props["offset"].toDouble() );
147  if ( props.contains( "offset_unit" ) )
148  l->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
149  if ( props.contains( "offset_map_unit_scale" ) )
150  l->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
151  if ( props.contains( "joinstyle" ) )
153  if ( props.contains( "capstyle" ) )
154  l->setPenCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( props["capstyle"] ) );
155 
156  if ( props.contains( "use_custom_dash" ) )
157  {
158  l->setUseCustomDashPattern( props["use_custom_dash"].toInt() );
159  }
160  if ( props.contains( "customdash" ) )
161  {
163  }
164  if ( props.contains( "customdash_unit" ) )
165  {
166  l->setCustomDashPatternUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["customdash_unit"] ) );
167  }
168  if ( props.contains( "customdash_map_unit_scale" ) )
169  {
170  l->setCustomDashPatternMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["customdash_map_unit_scale"] ) );
171  }
172 
173  if ( props.contains( "draw_inside_polygon" ) )
174  {
175  l->setDrawInsidePolygon( props["draw_inside_polygon"].toInt() );
176  }
177 
178  l->restoreDataDefinedProperties( props );
179 
180  return l;
181 }
182 
183 
185 {
186  return "SimpleLine";
187 }
188 
190 {
191  QColor penColor = mColor;
192  penColor.setAlphaF( mColor.alphaF() * context.alpha() );
193  mPen.setColor( penColor );
195  mPen.setWidthF( scaledWidth );
196  if ( mUseCustomDashPattern && !qgsDoubleNear( scaledWidth, 0 ) )
197  {
198  mPen.setStyle( Qt::CustomDashLine );
199 
200  //scale pattern vector
201  double dashWidthDiv = scaledWidth;
202  //fix dash pattern width in Qt 4.8
203  QStringList versionSplit = QString( qVersion() ).split( '.' );
204  if ( versionSplit.size() > 1
205  && versionSplit.at( 1 ).toInt() >= 8
206  && ( scaledWidth * context.renderContext().rasterScaleFactor() ) < 1.0 )
207  {
208  dashWidthDiv = 1.0;
209  }
210  QVector<qreal> scaledVector;
212  for ( ; it != mCustomDashVector.constEnd(); ++it )
213  {
214  //the dash is specified in terms of pen widths, therefore the division
216  }
217  mPen.setDashPattern( scaledVector );
218  }
219  else
220  {
222  }
225 
226  mSelPen = mPen;
227  QColor selColor = context.renderContext().selectionColor();
228  if ( ! selectionIsOpaque )
229  selColor.setAlphaF( context.alpha() );
230  mSelPen.setColor( selColor );
231 
232  //prepare expressions for data defined properties
233  prepareExpressions( context );
234 }
235 
237 {
238  Q_UNUSED( context );
239 }
240 
242 {
243  QPainter* p = context.renderContext().painter();
244  if ( !p )
245  {
246  return;
247  }
248 
249  if ( mDrawInsidePolygon )
250  {
251  //only drawing the line on the interior of the polygon, so set clip path for painter
252  p->save();
253  QPainterPath clipPath;
254  clipPath.addPolygon( points );
255 
256  if ( rings )
257  {
258  //add polygon rings
260  for ( ; it != rings->constEnd(); ++it )
261  {
262  QPolygonF ring = *it;
263  clipPath.addPolygon( ring );
264  }
265  }
266 
267  //use intersect mode, as a clip path may already exist (eg, for composer maps)
268  p->setClipPath( clipPath, Qt::IntersectClip );
269  }
270 
271  renderPolyline( points, context );
272  if ( rings )
273  {
274  mOffset = -mOffset; // invert the offset for rings!
275  Q_FOREACH ( const QPolygonF& ring, *rings )
276  renderPolyline( ring, context );
277  mOffset = -mOffset;
278  }
279 
280  if ( mDrawInsidePolygon )
281  {
282  //restore painter to reset clip path
283  p->restore();
284  }
285 
286 }
287 
289 {
290  QPainter* p = context.renderContext().painter();
291  if ( !p )
292  {
293  return;
294  }
295 
296  //size scaling by field
298  {
299  applySizeScale( context, mPen, mSelPen );
300  }
301 
302  double offset = mOffset;
303  applyDataDefinedSymbology( context, mPen, mSelPen, offset );
304 
305  p->setPen( context.selected() ? mSelPen : mPen );
306  p->setBrush( Qt::NoBrush );
307 
308  // Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #2 points).
309  if ( points.size() <= 2 &&
312  ( p->renderHints() & QPainter::Antialiasing ) )
313  {
314  p->setRenderHint( QPainter::Antialiasing, false );
315 #if 0
316  p->drawPolyline( points );
317 #else
318  QPainterPath path;
319  path.addPolygon( points );
320  p->drawPath( path );
321 #endif
322  p->setRenderHint( QPainter::Antialiasing, true );
323  return;
324  }
325 
326  if ( qgsDoubleNear( offset, 0 ) )
327  {
328 #if 0
329  p->drawPolyline( points );
330 #else
331  QPainterPath path;
332  path.addPolygon( points );
333  p->drawPath( path );
334 #endif
335  }
336  else
337  {
339  QList<QPolygonF> mline = ::offsetLine( points, scaledOffset, context.feature() ? context.feature()->constGeometry()->type() : QGis::Line );
340  for ( int part = 0; part < mline.count(); ++part )
341  {
342 #if 0
343  p->drawPolyline( mline );
344 #else
345  QPainterPath path;
346  path.addPolygon( mline[ part ] );
347  p->drawPath( path );
348 #endif
349  }
350  }
351 }
352 
354 {
355  QgsStringMap map;
356  map["line_color"] = QgsSymbolLayerV2Utils::encodeColor( mColor );
357  map["line_width"] = QString::number( mWidth );
358  map["line_width_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mWidthUnit );
359  map["width_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mWidthMapUnitScale );
360  map["line_style"] = QgsSymbolLayerV2Utils::encodePenStyle( mPenStyle );
363  map["offset"] = QString::number( mOffset );
365  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
366  map["use_custom_dash"] = ( mUseCustomDashPattern ? "1" : "0" );
370  map["draw_inside_polygon"] = ( mDrawInsidePolygon ? "1" : "0" );
372  return map;
373 }
374 
376 {
378  l->setWidthUnit( mWidthUnit );
384  l->setOffset( mOffset );
391  copyPaintEffect( l );
392  return l;
393 }
394 
395 void QgsSimpleLineSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap& props ) const
396 {
397  if ( mPenStyle == Qt::NoPen )
398  return;
399 
400  QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
401  if ( !props.value( "uom", "" ).isEmpty() )
402  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
403  element.appendChild( symbolizerElem );
404 
405  // <Geometry>
406  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
407 
408  // <Stroke>
409  QDomElement strokeElem = doc.createElement( "se:Stroke" );
410  symbolizerElem.appendChild( strokeElem );
411 
412  Qt::PenStyle penStyle = mUseCustomDashPattern ? Qt::CustomDashLine : mPenStyle;
415  QgsSymbolLayerV2Utils::lineToSld( doc, strokeElem, penStyle, mColor, width,
416  &mPenJoinStyle, &mPenCapStyle, &customDashVector );
417 
418  // <se:PerpendicularOffset>
419  if ( !qgsDoubleNear( mOffset, 0.0 ) )
420  {
421  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
423  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
424  symbolizerElem.appendChild( perpOffsetElem );
425  }
426 }
427 
428 QString QgsSimpleLineSymbolLayerV2::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
429 {
430  if ( mUseCustomDashPattern )
431  {
432  return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor,
435  }
436  else
437  {
438  return QgsSymbolLayerV2Utils::ogrFeatureStylePen( mWidth, mmScaleFactor, mapUnitScaleFactor, mPen.color(), mPenJoinStyle,
440  }
441 }
442 
444 {
445  QgsDebugMsg( "Entered." );
446 
447  QDomElement strokeElem = element.firstChildElement( "Stroke" );
448  if ( strokeElem.isNull() )
449  return nullptr;
450 
451  Qt::PenStyle penStyle;
452  QColor color;
453  double width;
454  Qt::PenJoinStyle penJoinStyle;
455  Qt::PenCapStyle penCapStyle;
457 
458  if ( !QgsSymbolLayerV2Utils::lineFromSld( strokeElem, penStyle,
459  color, width,
460  &penJoinStyle, &penCapStyle,
461  &customDashVector ) )
462  return nullptr;
463 
464  double offset = 0.0;
465  QDomElement perpOffsetElem = element.firstChildElement( "PerpendicularOffset" );
466  if ( !perpOffsetElem.isNull() )
467  {
468  bool ok;
469  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
470  if ( ok )
471  offset = d;
472  }
473 
474  QgsSimpleLineSymbolLayerV2* l = new QgsSimpleLineSymbolLayerV2( color, width, penStyle );
475  l->setOffset( offset );
476  l->setPenJoinStyle( penJoinStyle );
477  l->setPenCapStyle( penCapStyle );
478  l->setUseCustomDashPattern( penStyle == Qt::CustomDashLine );
479  l->setCustomDashVector( customDashVector );
480  return l;
481 }
482 
483 void QgsSimpleLineSymbolLayerV2::applySizeScale( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen )
484 {
486  pen.setWidthF( scaledWidth );
487  selPen.setWidthF( scaledWidth );
488 }
489 
490 void QgsSimpleLineSymbolLayerV2::applyDataDefinedSymbology( QgsSymbolV2RenderContext& context, QPen& pen, QPen& selPen, double& offset )
491 {
492  if ( !hasDataDefinedProperties() )
493  return; // shortcut
494 
495  //data defined properties
496  bool hasStrokeWidthExpression = false;
498  {
499  context.setOriginalValueVariable( mWidth );
500  double scaledWidth = QgsSymbolLayerV2Utils::convertToPainterUnits( context.renderContext(),
503  pen.setWidthF( scaledWidth );
504  selPen.setWidthF( scaledWidth );
505  hasStrokeWidthExpression = true;
506  }
507 
508  //color
509  bool ok;
511  {
514  if ( ok )
515  pen.setColor( QgsSymbolLayerV2Utils::decodeColor( colorString ) );
516  }
517 
518  //offset
520  {
523  }
524 
525  //dash dot vector
527  {
529  double dashWidthDiv = mPen.widthF();
530 
531  if ( hasStrokeWidthExpression )
532  {
533  dashWidthDiv = pen.widthF();
534  scaledWidth = pen.widthF();
535  }
536 
537  //fix dash pattern width in Qt 4.8
538  QStringList versionSplit = QString( qVersion() ).split( '.' );
539  if ( versionSplit.size() > 1
540  && versionSplit.at( 1 ).toInt() >= 8
541  && ( scaledWidth * context.renderContext().rasterScaleFactor() ) < 1.0 )
542  {
543  dashWidthDiv = 1.0;
544  }
545 
546  QVector<qreal> dashVector;
548  if ( ok )
549  {
550  QStringList::const_iterator dashIt = dashList.constBegin();
551  for ( ; dashIt != dashList.constEnd(); ++dashIt )
552  {
554  }
555  pen.setDashPattern( dashVector );
556  }
557  }
558 
559  //line style
561  {
564  if ( ok )
565  pen.setStyle( QgsSymbolLayerV2Utils::decodePenStyle( lineStyleString ) );
566  }
567 
568  //join style
570  {
573  if ( ok )
575  }
576 
577  //cap style
579  {
582  if ( ok )
583  pen.setCapStyle( QgsSymbolLayerV2Utils::decodePenCapStyle( capStyleString ) );
584  }
585 }
586 
588 {
589  if ( mDrawInsidePolygon )
590  {
591  //set to clip line to the interior of polygon, so we expect no bleed
592  return 0;
593  }
594  else
595  {
596  return ( mWidth / 2.0 ) + mOffset;
597  }
598 }
599 
601 {
602  unit = mCustomDashPatternUnit;
604 }
605 
607 {
608  return mPenStyle;
609 }
610 
612 {
613  double width = mWidth;
614 
616  {
617  context.setOriginalValueVariable( mWidth );
619  }
620  else if ( context.renderHints() & QgsSymbolV2::DataDefinedSizeScale )
621  {
623  }
624 
625  return width * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), widthUnit(), e.mapUnits() );
626 }
627 
629 {
631  {
632  bool ok;
635  if ( ok )
636  return ( QgsSymbolLayerV2Utils::decodeColor( colorString ) );
637  }
638  return mColor;
639 }
640 
642 {
643  Q_UNUSED( e );
644  double offset = mOffset;
645 
647  {
650  }
651  return offset;
652 }
653 
655 
657 
658 class MyLine
659 {
660  public:
661  MyLine( QPointF p1, QPointF p2 ) : mVertical( false ), mIncreasing( false ), mT( 0.0 ), mLength( 0.0 )
662  {
663  if ( p1 == p2 )
664  return; // invalid
665 
666  // tangent and direction
667  if ( qgsDoubleNear( p1.x(), p2.x() ) )
668  {
669  // vertical line - tangent undefined
670  mVertical = true;
671  mIncreasing = ( p2.y() > p1.y() );
672  }
673  else
674  {
675  mVertical = false;
676  mT = float( p2.y() - p1.y() ) / ( p2.x() - p1.x() );
677  mIncreasing = ( p2.x() > p1.x() );
678  }
679 
680  // length
681  double x = ( p2.x() - p1.x() );
682  double y = ( p2.y() - p1.y() );
683  mLength = sqrt( x * x + y * y );
684  }
685 
686  // return angle in radians
687  double angle()
688  {
689  double a = ( mVertical ? M_PI / 2 : atan( mT ) );
690 
691  if ( !mIncreasing )
692  a += M_PI;
693  return a;
694  }
695 
696  // return difference for x,y when going along the line with specified interval
697  QPointF diffForInterval( double interval )
698  {
699  if ( mVertical )
700  return ( mIncreasing ? QPointF( 0, interval ) : QPointF( 0, -interval ) );
701 
702  double alpha = atan( mT );
703  double dx = cos( alpha ) * interval;
704  double dy = sin( alpha ) * interval;
705  return ( mIncreasing ? QPointF( dx, dy ) : QPointF( -dx, -dy ) );
706  }
707 
708  double length() { return mLength; }
709 
710  protected:
711  bool mVertical;
712  bool mIncreasing;
713  double mT;
714  double mLength;
715 };
716 
718 
719 QgsMarkerLineSymbolLayerV2::QgsMarkerLineSymbolLayerV2( bool rotateMarker, double interval )
720 {
721  mRotateMarker = rotateMarker;
722  mInterval = interval;
723  mIntervalUnit = QgsSymbolV2::MM;
724  mMarker = nullptr;
725  mPlacement = Interval;
726  mOffsetAlongLine = 0;
727  mOffsetAlongLineUnit = QgsSymbolV2::MM;
728 
730 }
731 
733 {
734  delete mMarker;
735 }
736 
738 {
739  bool rotate = DEFAULT_MARKERLINE_ROTATE;
740  double interval = DEFAULT_MARKERLINE_INTERVAL;
741 
742 
743  if ( props.contains( "interval" ) )
744  interval = props["interval"].toDouble();
745  if ( props.contains( "rotate" ) )
746  rotate = ( props["rotate"] == "1" );
747 
748  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotate, interval );
749  if ( props.contains( "offset" ) )
750  {
751  x->setOffset( props["offset"].toDouble() );
752  }
753  if ( props.contains( "offset_unit" ) )
754  {
755  x->setOffsetUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_unit"] ) );
756  }
757  if ( props.contains( "interval_unit" ) )
758  {
759  x->setIntervalUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["interval_unit"] ) );
760  }
761  if ( props.contains( "offset_along_line" ) )
762  {
763  x->setOffsetAlongLine( props["offset_along_line"].toDouble() );
764  }
765  if ( props.contains( "offset_along_line_unit" ) )
766  {
767  x->setOffsetAlongLineUnit( QgsSymbolLayerV2Utils::decodeOutputUnit( props["offset_along_line_unit"] ) );
768  }
769  if ( props.contains(( "offset_along_line_map_unit_scale" ) ) )
770  {
771  x->setOffsetAlongLineMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_along_line_map_unit_scale"] ) );
772  }
773 
774  if ( props.contains( "offset_map_unit_scale" ) )
775  {
776  x->setOffsetMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["offset_map_unit_scale"] ) );
777  }
778  if ( props.contains( "interval_map_unit_scale" ) )
779  {
780  x->setIntervalMapUnitScale( QgsSymbolLayerV2Utils::decodeMapUnitScale( props["interval_map_unit_scale"] ) );
781  }
782 
783  if ( props.contains( "placement" ) )
784  {
785  if ( props["placement"] == "vertex" )
786  x->setPlacement( Vertex );
787  else if ( props["placement"] == "lastvertex" )
788  x->setPlacement( LastVertex );
789  else if ( props["placement"] == "firstvertex" )
790  x->setPlacement( FirstVertex );
791  else if ( props["placement"] == "centralpoint" )
792  x->setPlacement( CentralPoint );
793  else if ( props["placement"] == "curvepoint" )
794  x->setPlacement( CurvePoint );
795  else
796  x->setPlacement( Interval );
797  }
798 
799  x->restoreDataDefinedProperties( props );
800 
801  return x;
802 }
803 
805 {
806  return "MarkerLine";
807 }
808 
810 {
811  mMarker->setColor( color );
812  mColor = color;
813 }
814 
816 {
817  return mMarker ? mMarker->color() : mColor;
818 }
819 
821 {
822  mMarker->setAlpha( context.alpha() );
823 
824  // if being rotated, it gets initialized with every line segment
825  int hints = 0;
826  if ( mRotateMarker )
830  mMarker->setRenderHints( hints );
831 
832  mMarker->startRender( context.renderContext(), context.fields() );
833 
834  //prepare expressions for data defined properties
835  prepareExpressions( context );
836 }
837 
839 {
840  mMarker->stopRender( context.renderContext() );
841 }
842 
844 {
845  double offset = mOffset;
846 
848  {
851  }
852 
853  Placement placement = mPlacement;
854 
855  bool ok;
857  {
859  if ( ok )
860  {
861  if ( placementString.compare( "vertex", Qt::CaseInsensitive ) == 0 )
862  {
863  placement = Vertex;
864  }
865  else if ( placementString.compare( "lastvertex", Qt::CaseInsensitive ) == 0 )
866  {
867  placement = LastVertex;
868  }
869  else if ( placementString.compare( "firstvertex", Qt::CaseInsensitive ) == 0 )
870  {
871  placement = FirstVertex;
872  }
873  else if ( placementString.compare( "centerpoint", Qt::CaseInsensitive ) == 0 )
874  {
875  placement = CentralPoint;
876  }
877  else if ( placementString.compare( "curvepoint", Qt::CaseInsensitive ) == 0 )
878  {
879  placement = CurvePoint;
880  }
881  else
882  {
883  placement = Interval;
884  }
885  }
886  }
887 
888 
889  context.renderContext().painter()->save();
890 
891  if ( qgsDoubleNear( offset, 0.0 ) )
892  {
893  if ( placement == Interval )
894  renderPolylineInterval( points, context );
895  else if ( placement == CentralPoint )
896  renderPolylineCentral( points, context );
897  else
898  renderPolylineVertex( points, context, placement );
899  }
900  else
901  {
902  context.renderContext().setGeometry( nullptr ); //always use segmented geometry with offset
904 
905  for ( int part = 0; part < mline.count(); ++part )
906  {
907  const QPolygonF &points2 = mline[ part ];
908 
909  if ( placement == Interval )
910  renderPolylineInterval( points2, context );
911  else if ( placement == CentralPoint )
912  renderPolylineCentral( points2, context );
913  else
914  renderPolylineVertex( points2, context, placement );
915  }
916  }
917 
918  context.renderContext().painter()->restore();
919 }
920 
922 {
923  const QgsCurvePolygonV2* curvePolygon = dynamic_cast<const QgsCurvePolygonV2*>( context.renderContext().geometry() );
924 
925  if ( curvePolygon )
926  {
927  context.renderContext().setGeometry( curvePolygon->exteriorRing() );
928  }
929  renderPolyline( points, context );
930  if ( rings )
931  {
932  mOffset = -mOffset; // invert the offset for rings!
933  for ( int i = 0; i < rings->size(); ++i )
934  {
935  if ( curvePolygon )
936  {
937  context.renderContext().setGeometry( curvePolygon->interiorRing( i ) );
938  }
939  renderPolyline( rings->at( i ), context );
940  }
941  mOffset = -mOffset;
942  }
943 }
944 
946 {
947  if ( points.isEmpty() )
948  return;
949 
950  QPointF lastPt = points[0];
951  double lengthLeft = 0; // how much is left until next marker
952 
953  QgsRenderContext& rc = context.renderContext();
954  double interval = mInterval;
955 
957  {
958  context.setOriginalValueVariable( mInterval );
959  interval = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_INTERVAL, context, mInterval ).toDouble();
960  }
961  if ( interval <= 0 )
962  {
963  interval = 0.1;
964  }
965  double offsetAlongLine = mOffsetAlongLine;
967  {
968  context.setOriginalValueVariable( mOffsetAlongLine );
969  offsetAlongLine = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET_ALONG_LINE, context, mOffsetAlongLine ).toDouble();
970  }
971 
972  double painterUnitInterval = QgsSymbolLayerV2Utils::convertToPainterUnits( rc, interval, mIntervalUnit, mIntervalMapUnitScale );
973  lengthLeft = painterUnitInterval - QgsSymbolLayerV2Utils::convertToPainterUnits( rc, offsetAlongLine, mIntervalUnit, mIntervalMapUnitScale );
974 
975  for ( int i = 1; i < points.count(); ++i )
976  {
977  const QPointF& pt = points[i];
978 
979  if ( lastPt == pt ) // must not be equal!
980  continue;
981 
982  // for each line, find out dx and dy, and length
983  MyLine l( lastPt, pt );
984  QPointF diff = l.diffForInterval( painterUnitInterval );
985 
986  // if there's some length left from previous line
987  // use only the rest for the first point in new line segment
988  double c = 1 - lengthLeft / painterUnitInterval;
989 
990  lengthLeft += l.length();
991 
992  // rotate marker (if desired)
993  if ( mRotateMarker )
994  {
995  mMarker->setLineAngle( l.angle() * 180 / M_PI );
996  }
997 
998 
999  // while we're not at the end of line segment, draw!
1000  while ( lengthLeft > painterUnitInterval )
1001  {
1002  // "c" is 1 for regular point or in interval (0,1] for begin of line segment
1003  lastPt += c * diff;
1004  lengthLeft -= painterUnitInterval;
1005  mMarker->renderPoint( lastPt, context.feature(), rc, -1, context.selected() );
1006  c = 1; // reset c (if wasn't 1 already)
1007  }
1008 
1009  lastPt = pt;
1010  }
1011 }
1012 
1013 static double _averageAngle( QPointF prevPt, QPointF pt, QPointF nextPt )
1014 {
1015  // calc average angle between the previous and next point
1016  double a1 = MyLine( prevPt, pt ).angle();
1017  double a2 = MyLine( pt, nextPt ).angle();
1018  double unitX = cos( a1 ) + cos( a2 ), unitY = sin( a1 ) + sin( a2 );
1019 
1020  return atan2( unitY, unitX );
1021 }
1022 
1024 {
1025  if ( points.isEmpty() )
1026  return;
1027 
1028  QgsRenderContext& rc = context.renderContext();
1029 
1030  double origAngle = mMarker->angle();
1031  int i, maxCount;
1032  bool isRing = false;
1033 
1034  double offsetAlongLine = mOffsetAlongLine;
1036  {
1037  context.setOriginalValueVariable( mOffsetAlongLine );
1038  offsetAlongLine = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET_ALONG_LINE, context, mOffsetAlongLine ).toDouble();
1039  }
1040  if ( !qgsDoubleNear( offsetAlongLine, 0.0 ) )
1041  {
1042  //scale offset along line
1043  offsetAlongLine = QgsSymbolLayerV2Utils::convertToPainterUnits( rc, offsetAlongLine, mOffsetAlongLineUnit, mOffsetAlongLineMapUnitScale );
1044  }
1045 
1046  if ( qgsDoubleNear( offsetAlongLine, 0.0 ) && context.renderContext().geometry()
1047  && context.renderContext().geometry()->hasCurvedSegments() && ( placement == Vertex || placement == CurvePoint ) )
1048  {
1050  const QgsMapToPixel& mtp = context.renderContext().mapToPixel();
1051 
1052  QgsVertexId vId;
1053  QgsPointV2 vPoint;
1054  double x, y, z;
1055  QPointF mapPoint;
1056  while ( context.renderContext().geometry()->nextVertex( vId, vPoint ) )
1057  {
1058  if (( placement == Vertex && vId.type == QgsVertexId::SegmentVertex )
1059  || ( placement == CurvePoint && vId.type == QgsVertexId::CurveVertex ) )
1060  {
1061  //transform
1062  x = vPoint.x(), y = vPoint.y();
1063  z = vPoint.z();
1064  if ( ct )
1065  {
1066  ct->transformInPlace( x, y, z );
1067  }
1068  mapPoint.setX( x );
1069  mapPoint.setY( y );
1070  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1071  if ( mRotateMarker )
1072  {
1073  double angle = context.renderContext().geometry()->vertexAngle( vId );
1074  mMarker->setAngle( angle * 180 / M_PI );
1075  }
1076  mMarker->renderPoint( mapPoint, context.feature(), rc, -1, context.selected() );
1077  }
1078  }
1079  return;
1080  }
1081 
1082  if ( placement == FirstVertex )
1083  {
1084  i = 0;
1085  maxCount = 1;
1086  }
1087  else if ( placement == LastVertex )
1088  {
1089  i = points.count() - 1;
1090  maxCount = points.count();
1091  }
1092  else if ( placement == Vertex )
1093  {
1094  i = 0;
1095  maxCount = points.count();
1096  if ( points.first() == points.last() )
1097  isRing = true;
1098  }
1099  else
1100  {
1101  return;
1102  }
1103 
1104  if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
1105  {
1106  double distance;
1107  distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine;
1108  renderOffsetVertexAlongLine( points, i, distance, context );
1109  // restore original rotation
1110  mMarker->setAngle( origAngle );
1111  return;
1112  }
1113 
1114  for ( ; i < maxCount; ++i )
1115  {
1116  if ( isRing && placement == Vertex && i == points.count() - 1 )
1117  {
1118  continue; // don't draw the last marker - it has been drawn already
1119  }
1120  // rotate marker (if desired)
1121  if ( mRotateMarker )
1122  {
1123  double angle = markerAngle( points, isRing, i );
1124  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1125  }
1126 
1127  mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() );
1128  }
1129 
1130  // restore original rotation
1131  mMarker->setAngle( origAngle );
1132 }
1133 
1134 double QgsMarkerLineSymbolLayerV2::markerAngle( const QPolygonF& points, bool isRing, int vertex )
1135 {
1136  double angle = 0;
1137  const QPointF& pt = points[vertex];
1138 
1139  if ( isRing || ( vertex > 0 && vertex < points.count() - 1 ) )
1140  {
1141  int prevIndex = vertex - 1;
1142  int nextIndex = vertex + 1;
1143 
1144  if ( isRing && ( vertex == 0 || vertex == points.count() - 1 ) )
1145  {
1146  prevIndex = points.count() - 2;
1147  nextIndex = 1;
1148  }
1149 
1150  QPointF prevPoint, nextPoint;
1151  while ( prevIndex >= 0 )
1152  {
1153  prevPoint = points[ prevIndex ];
1154  if ( prevPoint != pt )
1155  {
1156  break;
1157  }
1158  --prevIndex;
1159  }
1160 
1161  while ( nextIndex < points.count() )
1162  {
1163  nextPoint = points[ nextIndex ];
1164  if ( nextPoint != pt )
1165  {
1166  break;
1167  }
1168  ++nextIndex;
1169  }
1170 
1171  if ( prevIndex >= 0 && nextIndex < points.count() )
1172  {
1173  angle = _averageAngle( prevPoint, pt, nextPoint );
1174  }
1175  }
1176  else //no ring and vertex is at start / at end
1177  {
1178  if ( vertex == 0 )
1179  {
1180  while ( vertex < points.size() - 1 )
1181  {
1182  const QPointF& nextPt = points[vertex+1];
1183  if ( pt != nextPt )
1184  {
1185  angle = MyLine( pt, nextPt ).angle();
1186  return angle;
1187  }
1188  ++vertex;
1189  }
1190  }
1191  else
1192  {
1193  // use last segment's angle
1194  while ( vertex >= 1 ) //in case of duplicated vertices, take the next suitable one
1195  {
1196  const QPointF& prevPt = points[vertex-1];
1197  if ( pt != prevPt )
1198  {
1199  angle = MyLine( prevPt, pt ).angle();
1200  return angle;
1201  }
1202  --vertex;
1203  }
1204  }
1205  }
1206  return angle;
1207 }
1208 
1209 void QgsMarkerLineSymbolLayerV2::renderOffsetVertexAlongLine( const QPolygonF &points, int vertex, double distance, QgsSymbolV2RenderContext& context )
1210 {
1211  if ( points.isEmpty() )
1212  return;
1213 
1214  QgsRenderContext& rc = context.renderContext();
1215  double origAngle = mMarker->angle();
1216  if ( qgsDoubleNear( distance, 0.0 ) )
1217  {
1218  // rotate marker (if desired)
1219  if ( mRotateMarker )
1220  {
1221  bool isRing = false;
1222  if ( points.first() == points.last() )
1223  isRing = true;
1224  double angle = markerAngle( points, isRing, vertex );
1225  mMarker->setAngle( origAngle + angle * 180 / M_PI );
1226  }
1227  mMarker->renderPoint( points[vertex], context.feature(), rc, -1, context.selected() );
1228  return;
1229  }
1230 
1231  int pointIncrement = distance > 0 ? 1 : -1;
1232  QPointF previousPoint = points[vertex];
1233  int startPoint = distance > 0 ? qMin( vertex + 1, points.count() - 1 ) : qMax( vertex - 1, 0 );
1234  int endPoint = distance > 0 ? points.count() - 1 : 0;
1235  double distanceLeft = qAbs( distance );
1236 
1237  for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
1238  {
1239  const QPointF& pt = points[i];
1240 
1241  if ( previousPoint == pt ) // must not be equal!
1242  continue;
1243 
1244  // create line segment
1245  MyLine l( previousPoint, pt );
1246 
1247  if ( distanceLeft < l.length() )
1248  {
1249  //destination point is in current segment
1250  QPointF markerPoint = previousPoint + l.diffForInterval( distanceLeft );
1251  // rotate marker (if desired)
1252  if ( mRotateMarker )
1253  {
1254  mMarker->setAngle( origAngle + ( l.angle() * 180 / M_PI ) );
1255  }
1256  mMarker->renderPoint( markerPoint, context.feature(), rc, -1, context.selected() );
1257  return;
1258  }
1259 
1260  distanceLeft -= l.length();
1261  previousPoint = pt;
1262  }
1263 
1264  //didn't find point
1265  return;
1266 }
1267 
1269 {
1270  if ( !points.isEmpty() )
1271  {
1272  // calc length
1273  qreal length = 0;
1274  QPolygonF::const_iterator it = points.constBegin();
1275  QPointF last = *it;
1276  for ( ++it; it != points.constEnd(); ++it )
1277  {
1278  length += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1279  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1280  last = *it;
1281  }
1282 
1283  // find the segment where the central point lies
1284  it = points.constBegin();
1285  last = *it;
1286  qreal last_at = 0, next_at = 0;
1287  QPointF next;
1288  int segment = 0;
1289  for ( ++it; it != points.constEnd(); ++it )
1290  {
1291  next = *it;
1292  next_at += sqrt(( last.x() - it->x() ) * ( last.x() - it->x() ) +
1293  ( last.y() - it->y() ) * ( last.y() - it->y() ) );
1294  if ( next_at >= length / 2 )
1295  break; // we have reached the center
1296  last = *it;
1297  last_at = next_at;
1298  segment++;
1299  }
1300 
1301  // find out the central point on segment
1302  MyLine l( last, next ); // for line angle
1303  qreal k = ( length * 0.5 - last_at ) / ( next_at - last_at );
1304  QPointF pt = last + ( next - last ) * k;
1305 
1306  // draw the marker
1307  double origAngle = mMarker->angle();
1308  if ( mRotateMarker )
1309  mMarker->setAngle( origAngle + l.angle() * 180 / M_PI );
1310  mMarker->renderPoint( pt, context.feature(), context.renderContext(), -1, context.selected() );
1311  if ( mRotateMarker )
1312  mMarker->setAngle( origAngle );
1313  }
1314 }
1315 
1316 
1318 {
1319  QgsStringMap map;
1320  map["rotate"] = ( mRotateMarker ? "1" : "0" );
1321  map["interval"] = QString::number( mInterval );
1322  map["offset"] = QString::number( mOffset );
1323  map["offset_along_line"] = QString::number( mOffsetAlongLine );
1324  map["offset_along_line_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetAlongLineUnit );
1325  map["offset_along_line_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetAlongLineMapUnitScale );
1326  map["offset_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mOffsetUnit );
1327  map["offset_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mOffsetMapUnitScale );
1328  map["interval_unit"] = QgsSymbolLayerV2Utils::encodeOutputUnit( mIntervalUnit );
1329  map["interval_map_unit_scale"] = QgsSymbolLayerV2Utils::encodeMapUnitScale( mIntervalMapUnitScale );
1330  if ( mPlacement == Vertex )
1331  map["placement"] = "vertex";
1332  else if ( mPlacement == LastVertex )
1333  map["placement"] = "lastvertex";
1334  else if ( mPlacement == FirstVertex )
1335  map["placement"] = "firstvertex";
1336  else if ( mPlacement == CentralPoint )
1337  map["placement"] = "centralpoint";
1338  else if ( mPlacement == CurvePoint )
1339  map["placement"] = "curvepoint";
1340  else
1341  map["placement"] = "interval";
1342 
1344  return map;
1345 }
1346 
1348 {
1349  return mMarker;
1350 }
1351 
1353 {
1354  if ( !symbol || symbol->type() != QgsSymbolV2::Marker )
1355  {
1356  delete symbol;
1357  return false;
1358  }
1359 
1360  delete mMarker;
1361  mMarker = static_cast<QgsMarkerSymbolV2*>( symbol );
1362  mColor = mMarker->color();
1363  return true;
1364 }
1365 
1367 {
1368  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( mRotateMarker, mInterval );
1369  x->setSubSymbol( mMarker->clone() );
1370  x->setOffset( mOffset );
1371  x->setPlacement( mPlacement );
1372  x->setOffsetUnit( mOffsetUnit );
1374  x->setIntervalUnit( mIntervalUnit );
1375  x->setIntervalMapUnitScale( mIntervalMapUnitScale );
1376  x->setOffsetAlongLine( mOffsetAlongLine );
1377  x->setOffsetAlongLineMapUnitScale( mOffsetAlongLineMapUnitScale );
1378  x->setOffsetAlongLineUnit( mOffsetAlongLineUnit );
1380  copyPaintEffect( x );
1381  return x;
1382 }
1383 
1385 {
1386  for ( int i = 0; i < mMarker->symbolLayerCount(); i++ )
1387  {
1388  QDomElement symbolizerElem = doc.createElement( "se:LineSymbolizer" );
1389  if ( !props.value( "uom", "" ).isEmpty() )
1390  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
1391  element.appendChild( symbolizerElem );
1392 
1393  // <Geometry>
1394  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
1395 
1396  QString gap;
1397  switch ( mPlacement )
1398  {
1399  case FirstVertex:
1400  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "firstPoint" ) );
1401  break;
1402  case LastVertex:
1403  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "lastPoint" ) );
1404  break;
1405  case CentralPoint:
1406  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "centralPoint" ) );
1407  break;
1408  case Vertex:
1409  // no way to get line/polygon's vertices, use a VendorOption
1410  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "placement", "points" ) );
1411  break;
1412  default:
1413  double interval = QgsSymbolLayerV2Utils::rescaleUom( mInterval, mIntervalUnit, props );
1414  gap = qgsDoubleToString( interval );
1415  break;
1416  }
1417 
1418  if ( !mRotateMarker )
1419  {
1420  // markers in LineSymbolizer must be drawn following the line orientation,
1421  // use a VendorOption when no marker rotation
1422  symbolizerElem.appendChild( QgsSymbolLayerV2Utils::createVendorOptionElement( doc, "rotateMarker", "0" ) );
1423  }
1424 
1425  // <Stroke>
1426  QDomElement strokeElem = doc.createElement( "se:Stroke" );
1427  symbolizerElem.appendChild( strokeElem );
1428 
1429  // <GraphicStroke>
1430  QDomElement graphicStrokeElem = doc.createElement( "se:GraphicStroke" );
1431  strokeElem.appendChild( graphicStrokeElem );
1432 
1433  QgsSymbolLayerV2 *layer = mMarker->symbolLayer( i );
1434  QgsMarkerSymbolLayerV2 *markerLayer = static_cast<QgsMarkerSymbolLayerV2 *>( layer );
1435  if ( !markerLayer )
1436  {
1437  graphicStrokeElem.appendChild( doc.createComment( QString( "MarkerSymbolLayerV2 expected, %1 found. Skip it." ).arg( layer->layerType() ) ) );
1438  }
1439  else
1440  {
1441  markerLayer->writeSldMarker( doc, graphicStrokeElem, props );
1442  }
1443 
1444  if ( !gap.isEmpty() )
1445  {
1446  QDomElement gapElem = doc.createElement( "se:Gap" );
1448  graphicStrokeElem.appendChild( gapElem );
1449  }
1450 
1451  if ( !qgsDoubleNear( mOffset, 0.0 ) )
1452  {
1453  QDomElement perpOffsetElem = doc.createElement( "se:PerpendicularOffset" );
1455  perpOffsetElem.appendChild( doc.createTextNode( qgsDoubleToString( offset ) ) );
1456  symbolizerElem.appendChild( perpOffsetElem );
1457  }
1458  }
1459 }
1460 
1462 {
1463  QgsDebugMsg( "Entered." );
1464 
1465  QDomElement strokeElem = element.firstChildElement( "Stroke" );
1466  if ( strokeElem.isNull() )
1467  return nullptr;
1468 
1469  QDomElement graphicStrokeElem = strokeElem.firstChildElement( "GraphicStroke" );
1470  if ( graphicStrokeElem.isNull() )
1471  return nullptr;
1472 
1473  // retrieve vendor options
1474  bool rotateMarker = true;
1475  Placement placement = Interval;
1476 
1477  QgsStringMap vendorOptions = QgsSymbolLayerV2Utils::getVendorOptionList( element );
1478  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
1479  {
1480  if ( it.key() == "placement" )
1481  {
1482  if ( it.value() == "points" ) placement = Vertex;
1483  else if ( it.value() == "firstPoint" ) placement = FirstVertex;
1484  else if ( it.value() == "lastPoint" ) placement = LastVertex;
1485  else if ( it.value() == "centralPoint" ) placement = CentralPoint;
1486  }
1487  else if ( it.value() == "rotateMarker" )
1488  {
1489  rotateMarker = it.value() == "0";
1490  }
1491  }
1492 
1493  QgsMarkerSymbolV2 *marker = nullptr;
1494 
1496  if ( l )
1497  {
1498  QgsSymbolLayerV2List layers;
1499  layers.append( l );
1500  marker = new QgsMarkerSymbolV2( layers );
1501  }
1502 
1503  if ( !marker )
1504  return nullptr;
1505 
1506  double interval = 0.0;
1507  QDomElement gapElem = graphicStrokeElem.firstChildElement( "Gap" );
1508  if ( !gapElem.isNull() )
1509  {
1510  bool ok;
1511  double d = gapElem.firstChild().nodeValue().toDouble( &ok );
1512  if ( ok )
1513  interval = d;
1514  }
1515 
1516  double offset = 0.0;
1517  QDomElement perpOffsetElem = graphicStrokeElem.firstChildElement( "PerpendicularOffset" );
1518  if ( !perpOffsetElem.isNull() )
1519  {
1520  bool ok;
1521  double d = perpOffsetElem.firstChild().nodeValue().toDouble( &ok );
1522  if ( ok )
1523  offset = d;
1524  }
1525 
1526  QgsMarkerLineSymbolLayerV2* x = new QgsMarkerLineSymbolLayerV2( rotateMarker );
1527  x->setPlacement( placement );
1528  x->setInterval( interval );
1529  x->setSubSymbol( marker );
1530  x->setOffset( offset );
1531  return x;
1532 }
1533 
1535 {
1536  mMarker->setSize( width );
1537 }
1538 
1540 {
1541  if ( property == QgsSymbolLayerV2::EXPR_WIDTH && mMarker && dataDefined )
1542  {
1543  mMarker->setDataDefinedSize( *dataDefined );
1544  }
1545  QgsLineSymbolLayerV2::setDataDefinedProperty( property, dataDefined );
1546 }
1547 
1549 {
1550  return mMarker->size();
1551 }
1552 
1554 {
1556  mMarker->setOutputUnit( unit );
1557  mIntervalUnit = unit;
1558  mOffsetUnit = unit;
1559  mOffsetAlongLineUnit = unit;
1560 }
1561 
1563 {
1565  if ( mIntervalUnit != unit || mOffsetUnit != unit || mOffsetAlongLineUnit != unit )
1566  {
1567  return QgsSymbolV2::Mixed;
1568  }
1569  return unit;
1570 }
1571 
1573 {
1575  mIntervalMapUnitScale = scale;
1576  mOffsetMapUnitScale = scale;
1577  mOffsetAlongLineMapUnitScale = scale;
1578 }
1579 
1581 {
1582  if ( QgsLineSymbolLayerV2::mapUnitScale() == mIntervalMapUnitScale &&
1583  mIntervalMapUnitScale == mOffsetMapUnitScale &&
1584  mOffsetMapUnitScale == mOffsetAlongLineMapUnitScale )
1585  {
1586  return mOffsetMapUnitScale;
1587  }
1588  return QgsMapUnitScale();
1589 }
1590 
1592 {
1594  if ( mMarker )
1595  attr.unite( mMarker->usedAttributes() );
1596  return attr;
1597 }
1598 
1600 {
1601  return ( mMarker->size() / 2.0 ) + mOffset;
1602 }
1603 
1604 
1605 
static double mapUnitScaleFactor(double scaleDenominator, QgsSymbolV2::OutputUnit symbolUnits, QGis::UnitType mapUnits)
void setIntervalUnit(QgsSymbolV2::OutputUnit unit)
static QString encodeOutputUnit(QgsSymbolV2::OutputUnit unit)
Qt::PenCapStyle penCapStyle() const
#define DEFAULT_SIMPLELINE_PENSTYLE
#define DEFAULT_MARKERLINE_ROTATE
const QgsCurveV2 * exteriorRing() const
static const QString EXPR_JOINSTYLE
double estimateMaxBleed() const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
const QgsFields * fields() const
Fields of the layer.
Definition: qgssymbolv2.h:381
void setStyle(Qt::PenStyle style)
double dxfWidth(const QgsDxfExport &e, QgsSymbolV2RenderContext &context) const override
OutputUnit
The unit of the output.
Definition: qgssymbolv2.h:62
QVector< qreal > dxfCustomDashPattern(QgsSymbolV2::OutputUnit &unit) const override
const QgsCurveV2 * interiorRing(int i) const
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)
A container class for data source field mapping or expression.
const QgsVectorSimplifyMethod & vectorSimplifyMethod() const
Added in QGIS v2.4.
bool contains(const Key &key) const
void setClipPath(const QPainterPath &path, Qt::ClipOperation operation)
double markerAngle(const QPolygonF &points, bool isRing, int vertex)
qreal alphaF() const
void renderPolyline(const QPolygonF &points, QgsSymbolV2RenderContext &context) override
static double _averageAngle(QPointF prevPt, QPointF pt, QPointF nextPt)
Qt::PenJoinStyle penJoinStyle() const
void setRenderHint(RenderHint hint, bool on)
QDomNode appendChild(const QDomNode &newChild)
#define DEFAULT_MARKERLINE_INTERVAL
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
static double rescaleUom(double size, QgsSymbolV2::OutputUnit unit, const QgsStringMap &props)
Rescales the given size based on the uomScale found in the props, if any is found, otherwise returns the value un-modified.
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
RenderHints renderHints() const
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
QString nodeValue() const
virtual QSet< QString > usedAttributes() const
Returns the set of attributes referenced by the layer.
virtual void setWidth(double width) override
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setCustomDashPatternUnit(QgsSymbolV2::OutputUnit unit)
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
static QString encodeColor(const QColor &color)
Placement
Defines how/where the marker should be placed on the line.
QgsMapUnitScale mCustomDashPatternMapUnitScale
Qt::PenStyle penStyle() const
void renderPolyline(const QPolygonF &points, QgsSymbolV2RenderContext &context) override
static const QString EXPR_WIDTH
static const QString EXPR_CUSTOMDASH
void drawPolyline(const QPointF *points, int pointCount)
static QgsStringMap getVendorOptionList(QDomElement &element)
const_iterator constEnd() const
void startRender(QgsSymbolV2RenderContext &context) override
const T & at(int i) const
T & last()
virtual bool hasCurvedSegments() const
Returns true if the geometry contains curved segments.
static QVector< qreal > decodeRealVector(const QString &s)
void renderPolylineInterval(const QPolygonF &points, QgsSymbolV2RenderContext &context)
QgsSymbolV2::OutputUnit outputUnit() const override
bool setSubSymbol(QgsSymbolV2 *symbol) override
set layer&#39;s subsymbol. takes ownership of the passed symbol
void setPenJoinStyle(Qt::PenJoinStyle style)
virtual bool hasDataDefinedProperties() const
Checks whether the layer has any associated data defined properties.
void save()
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
double dxfOffset(const QgsDxfExport &e, QgsSymbolV2RenderContext &context) const override
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.
void setJoinStyle(Qt::PenJoinStyle style)
SymbolType type() const
Definition: qgssymbolv2.h:104
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
const QgsGeometry * constGeometry() const
Gets a const pointer to the geometry object associated with this feature.
Definition: qgsfeature.cpp:82
static const bool selectionIsOpaque
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbolv2.h:375
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
QGis::UnitType mapUnits() const
Retrieve map units.
Definition: qgsdxfexport.h:93
T & first()
static const QString EXPR_OFFSET_ALONG_LINE
QgsMapUnitScale mWidthMapUnitScale
double z() const
Returns the point&#39;s z-coordinate.
Definition: qgspointv2.h:80
void setIntervalMapUnitScale(const QgsMapUnitScale &scale)
double y() const
Returns the point&#39;s y-coordinate.
Definition: qgspointv2.h:74
QString layerType() const override
Returns a string that represents this layer type.
double toDouble(bool *ok) const
void setMapUnitScale(const QgsMapUnitScale &scale) override
QgsMapUnitScale mOffsetMapUnitScale
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
static const QString EXPR_OFFSET
QgsSymbolV2::OutputUnit mOffsetUnit
void setWidthUnit(QgsSymbolV2::OutputUnit unit)
virtual Q_DECL_DEPRECATED QVariant evaluateDataDefinedProperty(const QString &property, const QgsFeature *feature, const QVariant &defaultVal=QVariant(), bool *ok=nullptr) const
Evaluates the matching data defined property and returns the calculated value.
Marker symbol.
Definition: qgssymbolv2.h:78
int size() const
void setOffsetAlongLine(double offsetAlongLine)
Sets the the offset along the line for the marker placement.
double symbologyScaleDenominator() const
Retrieve reference scale for output.
Definition: qgsdxfexport.h:80
void setInterval(double interval)
The interval between individual markers.
QgsMapUnitScale mapUnitScale() const override
static QgsSymbolV2::OutputUnit decodeOutputUnit(const QString &str)
void setDrawInsidePolygon(bool drawInsidePolygon)
void transformInPlace(double &x, double &y) const
Transform device coordinates to map coordinates.
static QString encodePenStyle(Qt::PenStyle style)
void setCapStyle(Qt::PenCapStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:34
virtual double width() const
QgsSimpleLineSymbolLayerV2 * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QColor color() const
Mixed units in symbol layers.
Definition: qgssymbolv2.h:66
static QgsSymbolLayerV2 * createMarkerLayerFromSld(QDomElement &element)
The output shall be in millimeters.
Definition: qgssymbolv2.h:64
static const QString EXPR_PLACEMENT
QString number(int n, int base)
int count(const T &value) const
qreal x() const
qreal y() const
void append(const T &value)
void setDashPattern(const QVector< qreal > &pattern)
void addPolygon(const QPolygonF &polygon)
void setOffset(double offset)
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
void setMapUnitScale(const QgsMapUnitScale &scale) override
void renderPolygonOutline(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context) override
QgsSymbolV2::OutputUnit outputUnit() const override
static double convertToPainterUnits(const QgsRenderContext &c, double size, QgsSymbolV2::OutputUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale())
Converts a size from the specied units to painter units.
Utility class for identifying a unique vertex within a geometry.
The geometries can be rendered with &#39;AntiAliasing&#39; disabled because of it is &#39;1-pixel size&#39;...
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
static bool createExpressionElement(QDomDocument &doc, QDomElement &element, const QString &function)
Creates a OGC Expression element based on the provided function expression.
void setPen(const QColor &color)
virtual QColor color() const override
The fill color.
QgsSymbolV2::OutputUnit mWidthUnit
qreal alpha() const
Get alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbolv2.h:363
void setAttribute(const QString &name, const QString &value)
void setWidthMapUnitScale(const QgsMapUnitScale &scale)
QVector< qreal > mCustomDashVector
Vector with an even number of entries for the.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspointv2.h:34
QString qgsDoubleToString(double a, int precision=17)
Definition: qgis.h:274
#define DEFAULT_SIMPLELINE_WIDTH
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
void renderPolygonOutline(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context) override
#define M_PI
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
Create a new MarkerLineSymbolLayerV2 from SLD.
const QgsCoordinateTransform * coordinateTransform() const
#define DEFAULT_SIMPLELINE_CAPSTYLE
QColor selectionColor() const
#define DEFAULT_SIMPLELINE_JOINSTYLE
void setWidthF(qreal width)
float threshold() const
Gets the simplification threshold of the vector layer managed.
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
void setBrush(const QBrush &brush)
QString layerType() const override
Returns a string that represents this layer type.
double x() const
Returns the point&#39;s x-coordinate.
Definition: qgspointv2.h:68
QVector< qreal > customDashVector() const
void stopRender(QgsSymbolV2RenderContext &context) override
void setOffsetAlongLineMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale used for calculating the offset in map units along line for markers...
virtual bool nextVertex(QgsVertexId &id, QgsPointV2 &vertex) const =0
Returns next vertex id and coordinates.
iterator end()
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the SLD element following the SLD v1.1 specs.
void setOffsetAlongLineUnit(QgsSymbolV2::OutputUnit unit)
Sets the unit used for calculating the offset along line for markers.
void setColor(const QColor &color)
virtual Q_DECL_DEPRECATED void prepareExpressions(const QgsFields *fields, double scale=-1.0)
Prepares all data defined property expressions for evaluation.
QGis::GeometryType type() const
Returns type of the geometry as a QGis::GeometryType.
virtual QColor color() const
The fill color.
double estimateMaxBleed() const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape...
QgsSymbolV2 * subSymbol() override
void setPenCapStyle(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)
void setCustomDashVector(const QVector< qreal > &vector)
static QgsSymbolLayerV2 * create(const QgsStringMap &properties=QgsStringMap())
Create a new MarkerLineSymbolLayerV2.
iterator begin()
static Qt::PenStyle decodePenStyle(const QString &str)
static const QString EXPR_CAPSTYLE
QgsMarkerLineSymbolLayerV2 * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
QDomText createTextNode(const QString &value)
void copyDataDefinedProperties(QgsSymbolLayerV2 *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
void saveDataDefinedProperties(QgsStringMap &stringMap) const
Saves all data defined properties to a string map.
void setOffsetUnit(QgsSymbolV2::OutputUnit unit)
static const QString EXPR_COLOR
void setDataDefinedProperty(const QString &property, QgsDataDefined *dataDefined) override
Sets a data defined property for the layer.
static QString encodeRealVector(const QVector< qreal > &v)
QgsSymbolV2::OutputUnit outputUnit() const override
QgsSymbolV2::OutputUnit mCustomDashPatternUnit
virtual QString layerType() const =0
Returns a string that represents this layer type.
const QgsAbstractGeometryV2 * geometry() const
Returns pointer to the unsegmentized geometry.
bool isNull() const
void restore()
virtual double vertexAngle(QgsVertexId vertex) const =0
Returns approximate angle at a vertex.
static QgsSymbolLayerV2 * createFromSld(QDomElement &element)
void copyPaintEffect(QgsSymbolLayerV2 *destLayer) const
Copies paint effect of this layer to another symbol layer.
const T & at(int i) const
#define DEFAULT_SIMPLELINE_COLOR
const_iterator constBegin() const
Contains information about the context of a rendering operation.
QgsMapUnitScale mapUnitScale() const override
QDomNode firstChild() const
QPainter * painter()
const QgsMapToPixel & mapToPixel() const
void setMapUnitScale(const QgsMapUnitScale &scale) override
void drawPath(const QPainterPath &path)
QSet< T > & unite(const QSet< T > &other)
void setPlacement(Placement p)
The placement of the markers.
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Struct for storing maximum and minimum scales for measurements in map units.
Qt::PenStyle dxfPenStyle() const override
QList< QPolygonF > offsetLine(QPolygonF polyline, double dist, QGis::GeometryType geometryType)
calculate geometry shifted by a specified distance
virtual bool hasDataDefinedProperty(const QString &property) const
Checks whether the layer has a matching data defined property and if that property is currently activ...
QgsSimpleLineSymbolLayerV2(const QColor &color=DEFAULT_SIMPLELINE_COLOR, double width=DEFAULT_SIMPLELINE_WIDTH, Qt::PenStyle penStyle=DEFAULT_SIMPLELINE_PENSTYLE)
bool isEmpty() const
void setCustomDashPatternMapUnitScale(const QgsMapUnitScale &scale)
void stopRender(QgsSymbolV2RenderContext &context) override
QgsRenderContext & renderContext()
Definition: qgssymbolv2.h:346
void setX(qreal x)
void setY(qreal y)
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
QDomElement firstChildElement(const QString &tagName) const
QColor dxfColor(QgsSymbolV2RenderContext &context) const override
int count(const T &value) const
QgsMapUnitScale mapUnitScale() const override
void setOutputUnit(QgsSymbolV2::OutputUnit unit) override
SimplifyHints simplifyHints() const
Gets the simplification hints of the vector layer managed.
qreal & rx()
qreal & ry()
QDomComment createComment(const QString &value)
Class for doing transforms between two map coordinate systems.
qreal widthF() const
void setColor(const QColor &color) override
The fill color.
void push_back(const T &value)
void setAlphaF(qreal alpha)
double toDouble(bool *ok) const
static QColor decodeColor(const QString &str)
typedef const_iterator
Curve polygon geometry type.
void restoreDataDefinedProperties(const QgsStringMap &stringMap)
Restores all data defined properties from string map.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
double rasterScaleFactor() const
const_iterator constEnd() const
QDomElement createElement(const QString &tagName)
const_iterator constBegin() const
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the SLD element following the SLD v1.1 specs.
void startRender(QgsSymbolV2RenderContext &context) override
QgsMarkerLineSymbolLayerV2(bool rotateMarker=DEFAULT_MARKERLINE_ROTATE, double interval=DEFAULT_MARKERLINE_INTERVAL)
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
static bool isGeneralizableByDeviceBoundingBox(const QgsRectangle &envelope, float mapToPixelTol=1.0f)
Returns whether the device-envelope can be replaced by its BBOX when is applied the specified toleran...
void transformInPlace(double &x, double &y, double &z, TransformDirection direction=ForwardTransform) const
int size() const
int compare(const QString &other) const
static const QString EXPR_LINE_STYLE
QString toString() const
virtual bool setSubSymbol(QgsSymbolV2 *symbol)
set layer&#39;s subsymbol. takes ownership of the passed symbol
static const QString EXPR_INTERVAL
void renderPolylineCentral(const QPolygonF &points, QgsSymbolV2RenderContext &context)
void setGeometry(const QgsAbstractGeometryV2 *geometry)
Sets pointer to original (unsegmentized) geometry.
virtual double width() const override
QSet< QString > usedAttributes() const override
Returns the set of attributes referenced by the layer.
void renderPolylineVertex(const QPolygonF &points, QgsSymbolV2RenderContext &context, Placement placement=Vertex)
QgsSymbolV2::OutputUnit widthUnit() const
const T value(const Key &key) const
static QString encodePenCapStyle(Qt::PenCapStyle style)
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
virtual Q_DECL_DEPRECATED void setDataDefinedProperty(const QString &property, const QString &expressionString)
Sets a data defined expression for a property.