Skip to content

Commit

Permalink
[feature] Replace marker/hash line "on vertices" placement option
Browse files Browse the repository at this point in the history
with "on inner vertices"

This new mode places the symbols on all inner vertices (ie all
vertices except the first or last). (We can safely do this now
that its easy for users to also set the symbols to show on
first/last vertex by clicking those checkboxes too!)

The motivation here is that when the "Vertex" mode puts the
symbols on the first/last vertex as well as inner vertices,
it's basically impossible to style a line with a different
marker on the first/last vertex to the rest of the line's
vertices. (The best you can get is just hiding the unwanted
first/last vertex by overlaying a second symbol layer on
the first/last vertices with a larger symbol)

Sponsored by North Road, thanks to SLYR
  • Loading branch information
nyalldawson committed Nov 12, 2021
1 parent fbfa992 commit 388a72a
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 27 deletions.
5 changes: 4 additions & 1 deletion python/core/auto_additions/qgis.py
Expand Up @@ -1037,7 +1037,10 @@
QgsTemplatedLineSymbolLayerBase.SegmentCenter = Qgis.MarkerLinePlacement.SegmentCenter
QgsTemplatedLineSymbolLayerBase.SegmentCenter.is_monkey_patched = True
QgsTemplatedLineSymbolLayerBase.SegmentCenter.__doc__ = "Place symbols at the center of every line segment"
Qgis.MarkerLinePlacement.__doc__ = 'Defines how/where the symbols should be placed on a line.\n\n.. note::\n\n Prior to QGIS 3.24 this was available as :py:class:`QgsTemplatedLineSymbolLayerBase`.Placement\n\n.. versionadded:: 3.24\n\n' + '* ``Interval``: ' + Qgis.MarkerLinePlacement.Interval.__doc__ + '\n' + '* ``Vertex``: ' + Qgis.MarkerLinePlacement.Vertex.__doc__ + '\n' + '* ``LastVertex``: ' + Qgis.MarkerLinePlacement.LastVertex.__doc__ + '\n' + '* ``FirstVertex``: ' + Qgis.MarkerLinePlacement.FirstVertex.__doc__ + '\n' + '* ``CentralPoint``: ' + Qgis.MarkerLinePlacement.CentralPoint.__doc__ + '\n' + '* ``CurvePoint``: ' + Qgis.MarkerLinePlacement.CurvePoint.__doc__ + '\n' + '* ``SegmentCenter``: ' + Qgis.MarkerLinePlacement.SegmentCenter.__doc__
QgsTemplatedLineSymbolLayerBase.InnerVertices = Qgis.MarkerLinePlacement.InnerVertices
QgsTemplatedLineSymbolLayerBase.InnerVertices.is_monkey_patched = True
QgsTemplatedLineSymbolLayerBase.InnerVertices.__doc__ = "Inner vertices (i.e. all vertices except the first and last vertex)"
Qgis.MarkerLinePlacement.__doc__ = 'Defines how/where the symbols should be placed on a line.\n\n.. note::\n\n Prior to QGIS 3.24 this was available as :py:class:`QgsTemplatedLineSymbolLayerBase`.Placement\n\n.. versionadded:: 3.24\n\n' + '* ``Interval``: ' + Qgis.MarkerLinePlacement.Interval.__doc__ + '\n' + '* ``Vertex``: ' + Qgis.MarkerLinePlacement.Vertex.__doc__ + '\n' + '* ``LastVertex``: ' + Qgis.MarkerLinePlacement.LastVertex.__doc__ + '\n' + '* ``FirstVertex``: ' + Qgis.MarkerLinePlacement.FirstVertex.__doc__ + '\n' + '* ``CentralPoint``: ' + Qgis.MarkerLinePlacement.CentralPoint.__doc__ + '\n' + '* ``CurvePoint``: ' + Qgis.MarkerLinePlacement.CurvePoint.__doc__ + '\n' + '* ``SegmentCenter``: ' + Qgis.MarkerLinePlacement.SegmentCenter.__doc__ + '\n' + '* ``InnerVertices``: ' + Qgis.MarkerLinePlacement.InnerVertices.__doc__
# --
Qgis.MarkerLinePlacement.baseClass = Qgis
Qgis.MarkerLinePlacements.baseClass = Qgis
Expand Down
1 change: 1 addition & 0 deletions python/core/auto_generated/qgis.sip.in
Expand Up @@ -667,6 +667,7 @@ The development version
CentralPoint,
CurvePoint,
SegmentCenter,
InnerVertices,
};
typedef QFlags<Qgis::MarkerLinePlacement> MarkerLinePlacements;

Expand Down
1 change: 1 addition & 0 deletions src/core/qgis.h
Expand Up @@ -1065,6 +1065,7 @@ class CORE_EXPORT Qgis
CentralPoint = 1 << 4, //!< Place symbols at the mid point of the line
CurvePoint = 1 << 5, //!< Place symbols at every virtual curve point in the line (used when rendering curved geometry types only)
SegmentCenter = 1 << 6, //!< Place symbols at the center of every line segment
InnerVertices = 1 << 7, //!< Inner vertices (i.e. all vertices except the first and last vertex)
};
Q_ENUM( MarkerLinePlacement )
Q_DECLARE_FLAGS( MarkerLinePlacements, MarkerLinePlacement )
Expand Down
30 changes: 27 additions & 3 deletions src/core/symbology/qgslinesymbollayer.cpp
Expand Up @@ -1284,6 +1284,10 @@ void QgsTemplatedLineSymbolLayerBase::renderPolyline( const QPolygonF &points, Q
{
placements = Qgis::MarkerLinePlacement::Vertex;
}
else if ( placementString.compare( QLatin1String( "innervertices" ), Qt::CaseInsensitive ) == 0 )
{
placements = Qgis::MarkerLinePlacement::InnerVertices;
}
else if ( placementString.compare( QLatin1String( "lastvertex" ), Qt::CaseInsensitive ) == 0 )
{
placements = Qgis::MarkerLinePlacement::LastVertex;
Expand Down Expand Up @@ -1329,6 +1333,8 @@ void QgsTemplatedLineSymbolLayerBase::renderPolyline( const QPolygonF &points, Q
renderPolylineCentral( points, context, averageOver );
if ( placements & Qgis::MarkerLinePlacement::Vertex )
renderPolylineVertex( points, context, Qgis::MarkerLinePlacement::Vertex );
if ( placements & Qgis::MarkerLinePlacement::InnerVertices )
renderPolylineVertex( points, context, Qgis::MarkerLinePlacement::InnerVertices );
if ( placements & Qgis::MarkerLinePlacement::LastVertex )
renderPolylineVertex( points, context, Qgis::MarkerLinePlacement::LastVertex );
if ( placements & Qgis::MarkerLinePlacement::FirstVertex )
Expand All @@ -1353,6 +1359,8 @@ void QgsTemplatedLineSymbolLayerBase::renderPolyline( const QPolygonF &points, Q
renderPolylineCentral( points2, context, averageOver );
if ( placements & Qgis::MarkerLinePlacement::Vertex )
renderPolylineVertex( points2, context, Qgis::MarkerLinePlacement::Vertex );
if ( placements & Qgis::MarkerLinePlacement::InnerVertices )
renderPolylineVertex( points2, context, Qgis::MarkerLinePlacement::InnerVertices );
if ( placements & Qgis::MarkerLinePlacement::LastVertex )
renderPolylineVertex( points2, context, Qgis::MarkerLinePlacement::LastVertex );
if ( placements & Qgis::MarkerLinePlacement::FirstVertex )
Expand Down Expand Up @@ -1556,7 +1564,7 @@ void QgsTemplatedLineSymbolLayerBase::setCommonProperties( QgsTemplatedLineSymbo
if ( properties.contains( QStringLiteral( "placement" ) ) )
{
if ( properties[QStringLiteral( "placement" )] == QLatin1String( "vertex" ) )
destLayer->setPlacements( Qgis::MarkerLinePlacement::Vertex );
destLayer->setPlacements( Qgis::MarkerLinePlacement::InnerVertices | Qgis::MarkerLinePlacement::FirstVertex | Qgis::MarkerLinePlacement::LastVertex );
else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "lastvertex" ) )
destLayer->setPlacements( Qgis::MarkerLinePlacement::LastVertex );
else if ( properties[QStringLiteral( "placement" )] == QLatin1String( "firstvertex" ) )
Expand Down Expand Up @@ -1778,7 +1786,9 @@ void QgsTemplatedLineSymbolLayerBase::renderPolylineVertex( const QPolygonF &poi
}

if ( qgsDoubleNear( offsetAlongLine, 0.0 ) && context.renderContext().geometry()
&& context.renderContext().geometry()->hasCurvedSegments() && ( placement == Qgis::MarkerLinePlacement::Vertex || placement == Qgis::MarkerLinePlacement::CurvePoint ) )
&& context.renderContext().geometry()->hasCurvedSegments() && ( placement == Qgis::MarkerLinePlacement::Vertex
|| placement == Qgis::MarkerLinePlacement::InnerVertices
|| placement == Qgis::MarkerLinePlacement::CurvePoint ) )
{
QgsCoordinateTransform ct = context.renderContext().coordinateTransform();
const QgsMapToPixel &mtp = context.renderContext().mapToPixel();
Expand All @@ -1788,14 +1798,21 @@ void QgsTemplatedLineSymbolLayerBase::renderPolylineVertex( const QPolygonF &poi
double x, y, z;
QPointF mapPoint;
int pointNum = 0;
const int numPoints = context.renderContext().geometry()->nCoordinates();
while ( context.renderContext().geometry()->nextVertex( vId, vPoint ) )
{
if ( context.renderContext().renderingStopped() )
break;

scope->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_POINT_NUM, ++pointNum, true ) );

if ( ( placement == Qgis::MarkerLinePlacement::Vertex && vId.type == Qgis::VertexType::Segment )
if ( pointNum == 1 && placement == Qgis::MarkerLinePlacement::InnerVertices )
continue;

if ( pointNum == numPoints && placement == Qgis::MarkerLinePlacement::InnerVertices )
continue;

if ( ( ( placement == Qgis::MarkerLinePlacement::Vertex || placement == Qgis::MarkerLinePlacement::InnerVertices ) && vId.type == Qgis::VertexType::Segment )
|| ( placement == Qgis::MarkerLinePlacement::CurvePoint && vId.type == Qgis::VertexType::Curve ) )
{
//transform
Expand Down Expand Up @@ -1837,6 +1854,13 @@ void QgsTemplatedLineSymbolLayerBase::renderPolylineVertex( const QPolygonF &poi
break;
}

case Qgis::MarkerLinePlacement::InnerVertices:
{
i = 1;
maxCount = points.count() - 1;
break;
}

case Qgis::MarkerLinePlacement::Vertex:
case Qgis::MarkerLinePlacement::SegmentCenter:
{
Expand Down
2 changes: 1 addition & 1 deletion src/core/symbology/qgssymbollayer.cpp
Expand Up @@ -92,7 +92,7 @@ void QgsSymbolLayer::initPropertyDefinitions()
{ QgsSymbolLayer::PropertyOpacity, QgsPropertyDefinition( "alpha", QObject::tr( "Opacity" ), QgsPropertyDefinition::Opacity, origin )},
{ QgsSymbolLayer::PropertyCustomDash, QgsPropertyDefinition( "customDash", QgsPropertyDefinition::DataTypeString, QObject::tr( "Custom dash pattern" ), QObject::tr( "[<b><dash>;<space></b>] e.g. '8;2;1;2'" ), origin )},
{ QgsSymbolLayer::PropertyCapStyle, QgsPropertyDefinition( "capStyle", QObject::tr( "Line cap style" ), QgsPropertyDefinition::CapStyle, origin )},
{ QgsSymbolLayer::PropertyPlacement, QgsPropertyDefinition( "placement", QgsPropertyDefinition::DataTypeString, QObject::tr( "Marker placement" ), QObject::tr( "string " ) + "[<b>interval</b>|<b>vertex</b>|<b>lastvertex</b>|<b>firstvertex</b>|<b>centerpoint</b>|<b>curvepoint</b>|<b>segmentcenter</b>]", origin )},
{ QgsSymbolLayer::PropertyPlacement, QgsPropertyDefinition( "placement", QgsPropertyDefinition::DataTypeString, QObject::tr( "Marker placement" ), QObject::tr( "string " ) + "[<b>interval</b>|<b>innervertices</b>|<b>vertex</b>|<b>lastvertex</b>|<b>firstvertex</b>|<b>centerpoint</b>|<b>curvepoint</b>|<b>segmentcenter</b>]", origin )},
{ QgsSymbolLayer::PropertyInterval, QgsPropertyDefinition( "interval", QObject::tr( "Marker interval" ), QgsPropertyDefinition::DoublePositive, origin )},
{ QgsSymbolLayer::PropertyOffsetAlongLine, QgsPropertyDefinition( "offsetAlongLine", QObject::tr( "Offset along line" ), QgsPropertyDefinition::DoublePositive, origin )},
{ QgsSymbolLayer::PropertyAverageAngleLength, QgsPropertyDefinition( "averageAngleLength", QObject::tr( "Average line angles over" ), QgsPropertyDefinition::DoublePositive, origin )},
Expand Down
22 changes: 14 additions & 8 deletions src/gui/symbology/qgssymbollayerwidget.cpp
Expand Up @@ -1959,9 +1959,12 @@ void QgsMarkerLineSymbolLayerWidget::setSymbolLayer( QgsSymbolLayer *layer )
spinOffset->blockSignals( false );

whileBlocking( mCheckInterval )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::Interval );
whileBlocking( mCheckVertex )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::Vertex );
whileBlocking( mCheckVertexFirst )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::FirstVertex );
whileBlocking( mCheckVertexLast )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::LastVertex );
whileBlocking( mCheckVertex )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::InnerVertices
|| mLayer->placements() & Qgis::MarkerLinePlacement::Vertex );
whileBlocking( mCheckVertexFirst )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::FirstVertex
|| mLayer->placements() & Qgis::MarkerLinePlacement::Vertex );
whileBlocking( mCheckVertexLast )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::LastVertex
|| mLayer->placements() & Qgis::MarkerLinePlacement::Vertex );
whileBlocking( mCheckCentralPoint )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::CentralPoint );
whileBlocking( mCheckCurvePoint )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::CurvePoint );
whileBlocking( mCheckSegmentCentralPoint )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::SegmentCenter );
Expand Down Expand Up @@ -2059,7 +2062,7 @@ void QgsMarkerLineSymbolLayerWidget::setPlacement()
if ( mCheckInterval->isChecked() )
placements |= Qgis::MarkerLinePlacement::Interval;
if ( mCheckVertex->isChecked() )
placements |= Qgis::MarkerLinePlacement::Vertex;
placements |= Qgis::MarkerLinePlacement::InnerVertices;
if ( mCheckVertexLast->isChecked() )
placements |= Qgis::MarkerLinePlacement::LastVertex;
if ( mCheckVertexFirst->isChecked() )
Expand Down Expand Up @@ -2208,9 +2211,12 @@ void QgsHashedLineSymbolLayerWidget::setSymbolLayer( QgsSymbolLayer *layer )
spinOffset->blockSignals( false );

whileBlocking( mCheckInterval )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::Interval );
whileBlocking( mCheckVertex )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::Vertex );
whileBlocking( mCheckVertexFirst )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::FirstVertex );
whileBlocking( mCheckVertexLast )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::LastVertex );
whileBlocking( mCheckVertex )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::InnerVertices
|| mLayer->placements() & Qgis::MarkerLinePlacement::Vertex );
whileBlocking( mCheckVertexFirst )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::FirstVertex
|| mLayer->placements() & Qgis::MarkerLinePlacement::Vertex );
whileBlocking( mCheckVertexLast )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::LastVertex
|| mLayer->placements() & Qgis::MarkerLinePlacement::Vertex );
whileBlocking( mCheckCentralPoint )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::CentralPoint );
whileBlocking( mCheckCurvePoint )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::CurvePoint );
whileBlocking( mCheckSegmentCentralPoint )->setChecked( mLayer->placements() & Qgis::MarkerLinePlacement::SegmentCenter );
Expand Down Expand Up @@ -2323,7 +2329,7 @@ void QgsHashedLineSymbolLayerWidget::setPlacement()
if ( mCheckInterval->isChecked() )
placements |= Qgis::MarkerLinePlacement::Interval;
if ( mCheckVertex->isChecked() )
placements |= Qgis::MarkerLinePlacement::Vertex;
placements |= Qgis::MarkerLinePlacement::InnerVertices;
if ( mCheckVertexLast->isChecked() )
placements |= Qgis::MarkerLinePlacement::LastVertex;
if ( mCheckVertexFirst->isChecked() )
Expand Down
17 changes: 10 additions & 7 deletions src/ui/symbollayer/widget_hashline.ui
Expand Up @@ -46,7 +46,7 @@
<item>
<widget class="QCheckBox" name="mCheckInterval">
<property name="text">
<string>with interval</string>
<string>With interval</string>
</property>
<property name="checked">
<bool>true</bool>
Expand Down Expand Up @@ -118,43 +118,46 @@
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="mCheckVertex">
<property name="toolTip">
<string>Shows symbols on inner vertices, i.e. all vertices except the first or last</string>
</property>
<property name="text">
<string>on every vertex</string>
<string>On inner vertices</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="mCheckVertexLast">
<property name="text">
<string>on last vertex only</string>
<string>On last vertex</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="mCheckVertexFirst">
<property name="text">
<string>on first vertex only</string>
<string>On first vertex</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="mCheckCentralPoint">
<property name="text">
<string>on central point</string>
<string>On central point</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QCheckBox" name="mCheckSegmentCentralPoint">
<property name="text">
<string>on central point of segments</string>
<string>On central point of segments</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QCheckBox" name="mCheckCurvePoint">
<property name="text">
<string>on every curve point</string>
<string>On every curve point</string>
</property>
</widget>
</item>
Expand Down
17 changes: 10 additions & 7 deletions src/ui/symbollayer/widget_markerline.ui
Expand Up @@ -69,7 +69,7 @@
<item>
<widget class="QCheckBox" name="mCheckInterval">
<property name="text">
<string>with interval</string>
<string>With interval</string>
</property>
<property name="checked">
<bool>true</bool>
Expand Down Expand Up @@ -118,43 +118,46 @@
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="mCheckVertex">
<property name="toolTip">
<string>Shows symbols on inner vertices, i.e. all vertices except the first or last</string>
</property>
<property name="text">
<string>on every vertex</string>
<string>On inner vertices</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QCheckBox" name="mCheckVertexLast">
<property name="text">
<string>on last vertex only</string>
<string>On last vertex</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="mCheckVertexFirst">
<property name="text">
<string>on first vertex only</string>
<string>On first vertex</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="mCheckCentralPoint">
<property name="text">
<string>on central point</string>
<string>On central point</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QCheckBox" name="mCheckSegmentCentralPoint">
<property name="text">
<string>on central point of segments</string>
<string>On central point of segments</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QCheckBox" name="mCheckCurvePoint">
<property name="text">
<string>on every curve point</string>
<string>On every curve point</string>
</property>
</widget>
</item>
Expand Down

0 comments on commit 388a72a

Please sign in to comment.