@@ -561,6 +561,8 @@ QgsMarkerLineSymbolLayerV2::QgsMarkerLineSymbolLayerV2( bool rotateMarker, doubl
561
561
mOffset = 0 ;
562
562
mOffsetUnit = QgsSymbolV2::MM;
563
563
mPlacement = Interval;
564
+ mOffsetAlongLine = 0 ;
565
+ mOffsetAlongLineUnit = QgsSymbolV2::MM;
564
566
565
567
setSubSymbol ( new QgsMarkerSymbolV2 () );
566
568
}
@@ -575,6 +577,7 @@ QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::create( const QgsStringMap& props
575
577
bool rotate = DEFAULT_MARKERLINE_ROTATE;
576
578
double interval = DEFAULT_MARKERLINE_INTERVAL;
577
579
580
+
578
581
if ( props.contains ( " interval" ) )
579
582
interval = props[" interval" ].toDouble ();
580
583
if ( props.contains ( " rotate" ) )
@@ -593,6 +596,14 @@ QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::create( const QgsStringMap& props
593
596
{
594
597
x->setIntervalUnit ( QgsSymbolLayerV2Utils::decodeOutputUnit ( props[" interval_unit" ] ) );
595
598
}
599
+ if ( props.contains ( " offset_along_line" ) )
600
+ {
601
+ x->setOffsetAlongLine ( props[" offset_along_line" ].toDouble () );
602
+ }
603
+ if ( props.contains ( " offset_along_line_unit" ) )
604
+ {
605
+ x->setOffsetAlongLineUnit ( QgsSymbolLayerV2Utils::decodeOutputUnit ( props[" offset_along_line_unit" ] ) );
606
+ }
596
607
597
608
if ( props.contains ( " placement" ) )
598
609
{
@@ -621,6 +632,10 @@ QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::create( const QgsStringMap& props
621
632
{
622
633
x->setDataDefinedProperty ( " placement" , props[" placement_expression" ] );
623
634
}
635
+ if ( props.contains ( " offset_along_line_expression" ) )
636
+ {
637
+ x->setDataDefinedProperty ( " offset_along_line" , props[" offset_along_line_expression" ] );
638
+ }
624
639
625
640
return x;
626
641
}
@@ -723,7 +738,7 @@ void QgsMarkerLineSymbolLayerV2::renderPolylineInterval( const QPolygonF& points
723
738
724
739
QPointF lastPt = points[0 ];
725
740
double lengthLeft = 0 ; // how much is left until next marker
726
- bool first = true ;
741
+ bool first = mOffsetAlongLine ? false : true ; // only draw marker at first vertex when no offset along line is set
727
742
double origAngle = mMarker ->angle ();
728
743
729
744
QgsRenderContext& rc = context.renderContext ();
@@ -738,8 +753,15 @@ void QgsMarkerLineSymbolLayerV2::renderPolylineInterval( const QPolygonF& points
738
753
{
739
754
interval = 0.1 ;
740
755
}
756
+ double offsetAlongLine = mOffsetAlongLine ;
757
+ QgsExpression* offsetAlongLineExpression = expression ( " offset_along_line" );
758
+ if ( offsetAlongLineExpression )
759
+ {
760
+ offsetAlongLine = offsetAlongLineExpression->evaluate ( const_cast <QgsFeature*>( context.feature () ) ).toDouble ();
761
+ }
741
762
742
763
double painterUnitInterval = interval * QgsSymbolLayerV2Utils::lineWidthScaleFactor ( rc, mIntervalUnit );
764
+ lengthLeft = painterUnitInterval - offsetAlongLine * QgsSymbolLayerV2Utils::lineWidthScaleFactor ( rc, mIntervalUnit );
743
765
744
766
for ( int i = 1 ; i < points.count (); ++i )
745
767
{
@@ -810,6 +832,18 @@ void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points,
810
832
int i, maxCount;
811
833
bool isRing = false ;
812
834
835
+ double offsetAlongLine = mOffsetAlongLine ;
836
+ QgsExpression* offsetAlongLineExpression = expression ( " offset_along_line" );
837
+ if ( offsetAlongLineExpression )
838
+ {
839
+ offsetAlongLine = offsetAlongLineExpression->evaluate ( const_cast <QgsFeature*>( context.feature () ) ).toDouble ();
840
+ }
841
+ if ( offsetAlongLine != 0 )
842
+ {
843
+ // scale offset along line
844
+ offsetAlongLine *= QgsSymbolLayerV2Utils::lineWidthScaleFactor ( rc, mOffsetAlongLineUnit );
845
+ }
846
+
813
847
if ( placement == FirstVertex )
814
848
{
815
849
i = 0 ;
@@ -828,6 +862,16 @@ void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points,
828
862
isRing = true ;
829
863
}
830
864
865
+ if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) )
866
+ {
867
+ double distance;
868
+ distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine;
869
+ renderOffsetVertexAlongLine ( points, i, distance, context );
870
+ // restore original rotation
871
+ mMarker ->setAngle ( origAngle );
872
+ return ;
873
+ }
874
+
831
875
for ( ; i < maxCount; ++i )
832
876
{
833
877
if ( isRing && placement == Vertex && i == points.count () - 1 )
@@ -923,6 +967,65 @@ double QgsMarkerLineSymbolLayerV2::markerAngle( const QPolygonF& points, bool is
923
967
return angle;
924
968
}
925
969
970
+ void QgsMarkerLineSymbolLayerV2::renderOffsetVertexAlongLine ( const QPolygonF &points, int vertex, double distance, QgsSymbolV2RenderContext& context )
971
+ {
972
+ if ( points.isEmpty () )
973
+ return ;
974
+
975
+ QgsRenderContext& rc = context.renderContext ();
976
+ double origAngle = mMarker ->angle ();
977
+ if ( distance == 0 )
978
+ {
979
+ // rotate marker (if desired)
980
+ if ( mRotateMarker )
981
+ {
982
+ bool isRing = false ;
983
+ if ( points.first () == points.last () )
984
+ isRing = true ;
985
+ double angle = markerAngle ( points, isRing, vertex );
986
+ mMarker ->setAngle ( origAngle + angle * 180 / M_PI );
987
+ }
988
+ mMarker ->renderPoint ( points[vertex], context.feature (), rc, -1 , context.selected () );
989
+ return ;
990
+ }
991
+
992
+ int pointIncrement = distance > 0 ? 1 : -1 ;
993
+ QPointF previousPoint = points[vertex];
994
+ int startPoint = distance > 0 ? qMin ( vertex + 1 , points.count () - 1 ) : qMax ( vertex - 1 , 0 );
995
+ int endPoint = distance > 0 ? points.count () - 1 : 0 ;
996
+ double distanceLeft = qAbs ( distance );
997
+
998
+ for ( int i = startPoint; pointIncrement > 0 ? i <= endPoint : i >= endPoint; i += pointIncrement )
999
+ {
1000
+ const QPointF& pt = points[i];
1001
+
1002
+ if ( previousPoint == pt ) // must not be equal!
1003
+ continue ;
1004
+
1005
+ // create line segment
1006
+ MyLine l ( previousPoint, pt );
1007
+
1008
+ if ( distanceLeft < l.length () )
1009
+ {
1010
+ // destination point is in current segment
1011
+ QPointF markerPoint = previousPoint + l.diffForInterval ( distanceLeft );
1012
+ // rotate marker (if desired)
1013
+ if ( mRotateMarker )
1014
+ {
1015
+ mMarker ->setAngle ( origAngle + ( l.angle () * 180 / M_PI ) );
1016
+ }
1017
+ mMarker ->renderPoint ( markerPoint, context.feature (), rc, -1 , context.selected () );
1018
+ return ;
1019
+ }
1020
+
1021
+ distanceLeft -= l.length ();
1022
+ previousPoint = pt;
1023
+ }
1024
+
1025
+ // didn't find point
1026
+ return ;
1027
+ }
1028
+
926
1029
void QgsMarkerLineSymbolLayerV2::renderPolylineCentral ( const QPolygonF& points, QgsSymbolV2RenderContext& context )
927
1030
{
928
1031
if ( points.size () > 0 )
@@ -978,6 +1081,8 @@ QgsStringMap QgsMarkerLineSymbolLayerV2::properties() const
978
1081
map[" rotate" ] = ( mRotateMarker ? " 1" : " 0" );
979
1082
map[" interval" ] = QString::number ( mInterval );
980
1083
map[" offset" ] = QString::number ( mOffset );
1084
+ map[" offset_along_line" ] = QString::number ( mOffsetAlongLine );
1085
+ map[" offset_along_line_unit" ] = QgsSymbolLayerV2Utils::encodeOutputUnit ( mOffsetAlongLineUnit );
981
1086
map[" offset_unit" ] = QgsSymbolLayerV2Utils::encodeOutputUnit ( mOffsetUnit );
982
1087
map[" interval_unit" ] = QgsSymbolLayerV2Utils::encodeOutputUnit ( mIntervalUnit );
983
1088
if ( mPlacement == Vertex )
@@ -1022,6 +1127,8 @@ QgsSymbolLayerV2* QgsMarkerLineSymbolLayerV2::clone() const
1022
1127
x->setPlacement ( mPlacement );
1023
1128
x->setOffsetUnit ( mOffsetUnit );
1024
1129
x->setIntervalUnit ( mIntervalUnit );
1130
+ x->setOffsetAlongLine ( mOffsetAlongLine );
1131
+ x->setOffsetAlongLineUnit ( mOffsetAlongLineUnit );
1025
1132
copyDataDefinedProperties ( x );
1026
1133
return x;
1027
1134
}
0 commit comments