Skip to content

Commit 48d2a37

Browse files
committedApr 2, 2019
[FEATURE] New line symbol type: Hash line
This line symbol type is designed to replicate the ArcGIS Hash Line symbol layer type. It allows for a repeating line segment to be drawn over the length of a feature, with a line-sub symbol used to render each individual segment. To reduce code duplication, this is heavily based off the current line marker symbol layer, since the functionality is almost identical (draw some sub symbol at some interval along a line). Accordingly, I've split off QgsMarkerLineSymbolLayer to move as much of the common functionality as possible to a new abstract base class, so that only the actual marker/line segment rendering occurs in the marker line/hash line subclasses. This also gives the hash line all the existing placement options permissible for marker lines -- e.g. first/last vertex, mid points, regular intervals, etc. The hash line length and angle can have data defined overrides, which are evaluated per-line segment, allowing for the hash line to change size and angle over the length of a single rendered feature.
1 parent 59ed078 commit 48d2a37

17 files changed

+2478
-590
lines changed
 

‎python/core/__init__.py.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ from qgis._core import *
2828

2929
from .additions.edit import edit, QgsEditError
3030
from .additions.fromfunction import fromFunction
31+
from .additions.markerlinesymbollayer import *
3132
from .additions.metaenum import metaEnumFromType, metaEnumFromValue
3233
from .additions.processing import processing_output_layer_repr, processing_source_repr
3334
from .additions.projectdirtyblocker import ProjectDirtyBlocker
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
***************************************************************************
5+
markerlinesymbollayer.py
6+
---------------------
7+
Date : March 2019
8+
Copyright : (C) 2019 by Nyall Dawson
9+
Email : nyall dot dawson at gmail dot com
10+
***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************
18+
"""
19+
from qgis._core import (
20+
QgsMarkerLineSymbolLayer,
21+
QgsTemplatedLineSymbolLayerBase)
22+
23+
24+
# monkey patch deprecated enum values to maintain API
25+
# TODO - remove for QGIS 4.0
26+
QgsMarkerLineSymbolLayer.Interval = QgsTemplatedLineSymbolLayerBase.Interval
27+
QgsMarkerLineSymbolLayer.Vertex = QgsTemplatedLineSymbolLayerBase.Vertex
28+
QgsMarkerLineSymbolLayer.LastVertex = QgsTemplatedLineSymbolLayerBase.LastVertex
29+
QgsMarkerLineSymbolLayer.FirstVertex = QgsTemplatedLineSymbolLayerBase.FirstVertex
30+
QgsMarkerLineSymbolLayer.CentralPoint = QgsTemplatedLineSymbolLayerBase.CentralPoint
31+
QgsMarkerLineSymbolLayer.CurvePoint = QgsTemplatedLineSymbolLayerBase.CurvePoint

‎python/core/auto_generated/symbology/qgslinesymbollayer.sip.in

Lines changed: 337 additions & 110 deletions
Large diffs are not rendered by default.

‎python/core/auto_generated/symbology/qgssymbollayer.sip.in

Lines changed: 96 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -835,11 +835,53 @@ class QgsLineSymbolLayer : QgsSymbolLayer
835835
InteriorRingsOnly,
836836
};
837837

838+
virtual void setOutputUnit( QgsUnitTypes::RenderUnit unit );
839+
840+
virtual QgsUnitTypes::RenderUnit outputUnit() const;
841+
842+
virtual void setMapUnitScale( const QgsMapUnitScale &scale );
843+
844+
virtual QgsMapUnitScale mapUnitScale() const;
845+
846+
virtual void drawPreviewIcon( QgsSymbolRenderContext &context, QSize size );
847+
848+
virtual double dxfWidth( const QgsDxfExport &e, QgsSymbolRenderContext &context ) const;
849+
850+
838851
virtual void renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context ) = 0;
852+
%Docstring
853+
Renders the line symbol layer along the line joining ``points``, using the given render ``context``.
854+
855+
.. seealso:: :py:func:`renderPolygonStroke`
856+
%End
839857

840858
virtual void renderPolygonStroke( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context );
859+
%Docstring
860+
Renders the line symbol layer along the outline of polygon, using the given render ``context``.
861+
862+
The exterior ring of the polygon is specified in ``points``. Optionally, interior
863+
rings are set via the ``rings`` arugment.
864+
865+
.. seealso:: :py:func:`renderPolyline`
866+
%End
841867

842868
virtual void setWidth( double width );
869+
%Docstring
870+
Sets the ``width`` of the line symbol layer.
871+
872+
Calling this method updates the width of the line symbol layer, without
873+
changing the existing width units. It has different effects depending
874+
on the line symbol layer subclass, e.g. for a simple line layer it
875+
changes the stroke width of the line, for a marker line layer it
876+
changes the size of the markers used to draw the line.
877+
878+
.. seealso:: :py:func:`width`
879+
880+
.. warning::
881+
882+
Since the width units vary, this method is useful for changing the
883+
relative width of a line symbol layer only.
884+
%End
843885

844886
virtual double width() const;
845887
%Docstring
@@ -867,61 +909,94 @@ width of the symbol layer using the provided render ``context``.
867909
%End
868910

869911
double offset() const;
870-
void setOffset( double offset );
871-
872-
void setWidthUnit( QgsUnitTypes::RenderUnit unit );
873912
%Docstring
874-
Sets the units for the line's width.
913+
Returns the line's offset.
875914

876-
:param unit: width units
915+
Offset units can be retrieved by calling offsetUnit().
877916

878-
.. seealso:: :py:func:`widthUnit`
917+
.. seealso:: :py:func:`setOffset`
918+
919+
.. seealso:: :py:func:`offsetUnit`
920+
921+
.. seealso:: :py:func:`offsetMapUnitScale`
879922
%End
880923

881-
QgsUnitTypes::RenderUnit widthUnit() const;
924+
void setOffset( double offset );
882925
%Docstring
883-
Returns the units for the line's width.
926+
Sets the line's ``offset``.
884927

885-
.. seealso:: :py:func:`setWidthUnit`
886-
%End
928+
Offset units are set via setOffsetUnit().
887929

888-
void setWidthMapUnitScale( const QgsMapUnitScale &scale );
889-
const QgsMapUnitScale &widthMapUnitScale() const;
930+
.. seealso:: :py:func:`offset`
931+
932+
.. seealso:: :py:func:`setOffsetUnit`
933+
934+
.. seealso:: :py:func:`setOffsetMapUnitScale`
935+
%End
890936

891937
void setOffsetUnit( QgsUnitTypes::RenderUnit unit );
892938
%Docstring
893-
Sets the units for the line's offset.
894-
895-
:param unit: offset units
939+
Sets the ``unit`` for the line's offset.
896940

897941
.. seealso:: :py:func:`offsetUnit`
942+
943+
.. seealso:: :py:func:`setOffset`
944+
945+
.. seealso:: :py:func:`setOffsetMapUnitScale`
898946
%End
899947

900948
QgsUnitTypes::RenderUnit offsetUnit() const;
901949
%Docstring
902950
Returns the units for the line's offset.
903951

904952
.. seealso:: :py:func:`setOffsetUnit`
953+
954+
.. seealso:: :py:func:`offset`
955+
956+
.. seealso:: :py:func:`offsetMapUnitScale`
905957
%End
906958

907959
void setOffsetMapUnitScale( const QgsMapUnitScale &scale );
960+
%Docstring
961+
Sets the map unit ``scale`` for the line's offset.
962+
963+
.. seealso:: :py:func:`offsetMapUnitScale`
964+
965+
.. seealso:: :py:func:`setOffset`
966+
967+
.. seealso:: :py:func:`setOffsetUnit`
968+
%End
969+
908970
const QgsMapUnitScale &offsetMapUnitScale() const;
971+
%Docstring
972+
Returns the map unit scale for the line's offset.
909973

910-
virtual void setOutputUnit( QgsUnitTypes::RenderUnit unit );
974+
.. seealso:: :py:func:`setOffsetMapUnitScale`
911975

912-
virtual QgsUnitTypes::RenderUnit outputUnit() const;
976+
.. seealso:: :py:func:`offset`
913977

978+
.. seealso:: :py:func:`offsetUnit`
979+
%End
914980

915-
virtual void setMapUnitScale( const QgsMapUnitScale &scale );
916981

917-
virtual QgsMapUnitScale mapUnitScale() const;
982+
void setWidthUnit( QgsUnitTypes::RenderUnit unit );
983+
%Docstring
984+
Sets the units for the line's width.
918985

986+
:param unit: width units
919987

920-
virtual void drawPreviewIcon( QgsSymbolRenderContext &context, QSize size );
988+
.. seealso:: :py:func:`widthUnit`
989+
%End
921990

991+
QgsUnitTypes::RenderUnit widthUnit() const;
992+
%Docstring
993+
Returns the units for the line's width.
922994

923-
virtual double dxfWidth( const QgsDxfExport &e, QgsSymbolRenderContext &context ) const;
995+
.. seealso:: :py:func:`setWidthUnit`
996+
%End
924997

998+
void setWidthMapUnitScale( const QgsMapUnitScale &scale );
999+
const QgsMapUnitScale &widthMapUnitScale() const;
9251000

9261001
RenderRingFilter ringFilter() const;
9271002
%Docstring

‎python/gui/auto_generated/symbology/qgssymbollayerwidget.sip.in

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,38 @@ Creates a new QgsMarkerLineSymbolLayerWidget.
377377

378378

379379

380+
class QgsHashedLineSymbolLayerWidget : QgsSymbolLayerWidget
381+
{
382+
383+
%TypeHeaderCode
384+
#include "qgssymbollayerwidget.h"
385+
%End
386+
public:
387+
388+
QgsHashedLineSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent /TransferThis/ = 0 );
389+
%Docstring
390+
Constructor for QgsHashedLineSymbolLayerWidget.
391+
392+
:param vl: associated vector layer
393+
:param parent: parent widget
394+
%End
395+
396+
static QgsSymbolLayerWidget *create( QgsVectorLayer *vl ) /Factory/;
397+
%Docstring
398+
Creates a new QgsHashedLineSymbolLayerWidget.
399+
400+
:param vl: associated vector layer
401+
%End
402+
403+
virtual void setSymbolLayer( QgsSymbolLayer *layer );
404+
405+
virtual QgsSymbolLayer *symbolLayer();
406+
407+
408+
};
409+
410+
411+
380412

381413
class QgsSvgMarkerSymbolLayerWidget : QgsSymbolLayerWidget
382414
{

‎src/core/symbology/qgslinesymbollayer.cpp

Lines changed: 587 additions & 281 deletions
Large diffs are not rendered by default.

‎src/core/symbology/qgslinesymbollayer.h

Lines changed: 338 additions & 134 deletions
Large diffs are not rendered by default.

‎src/core/symbology/qgssymbollayer.h

Lines changed: 85 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ class CORE_EXPORT QgsSymbolLayer
139139
PropertyFillStyle, //!< Fill style (eg solid, dots)
140140
PropertyJoinStyle, //!< Line join style
141141
PropertySecondaryColor, //!< Secondary color (eg for gradient fills)
142-
PropertyLineAngle, //!< Line angle
143-
PropertyLineDistance, //!< Distance between lines
142+
PropertyLineAngle, //!< Line angle, or angle of hash lines for hash line symbols
143+
PropertyLineDistance, //!< Distance between lines, or length of lines for hash line symbols
144144
PropertyGradientType, //!< Gradient fill type
145145
PropertyCoordinateMode, //!< Gradient coordinate mode
146146
PropertyGradientSpread, //!< Gradient spread mode
@@ -786,10 +786,42 @@ class CORE_EXPORT QgsLineSymbolLayer : public QgsSymbolLayer
786786
InteriorRingsOnly, //!< Render the interior rings only
787787
};
788788

789+
void setOutputUnit( QgsUnitTypes::RenderUnit unit ) override;
790+
QgsUnitTypes::RenderUnit outputUnit() const override;
791+
void setMapUnitScale( const QgsMapUnitScale &scale ) override;
792+
QgsMapUnitScale mapUnitScale() const override;
793+
void drawPreviewIcon( QgsSymbolRenderContext &context, QSize size ) override;
794+
double dxfWidth( const QgsDxfExport &e, QgsSymbolRenderContext &context ) const override;
795+
796+
/**
797+
* Renders the line symbol layer along the line joining \a points, using the given render \a context.
798+
* \see renderPolygonStroke()
799+
*/
789800
virtual void renderPolyline( const QPolygonF &points, QgsSymbolRenderContext &context ) = 0;
790801

802+
/**
803+
* Renders the line symbol layer along the outline of polygon, using the given render \a context.
804+
*
805+
* The exterior ring of the polygon is specified in \a points. Optionally, interior
806+
* rings are set via the \a rings arugment.
807+
*
808+
* \see renderPolyline()
809+
*/
791810
virtual void renderPolygonStroke( const QPolygonF &points, QList<QPolygonF> *rings, QgsSymbolRenderContext &context );
792811

812+
/**
813+
* Sets the \a width of the line symbol layer.
814+
*
815+
* Calling this method updates the width of the line symbol layer, without
816+
* changing the existing width units. It has different effects depending
817+
* on the line symbol layer subclass, e.g. for a simple line layer it
818+
* changes the stroke width of the line, for a marker line layer it
819+
* changes the size of the markers used to draw the line.
820+
*
821+
* \see width()
822+
* \warning Since the width units vary, this method is useful for changing the
823+
* relative width of a line symbol layer only.
824+
*/
793825
virtual void setWidth( double width ) { mWidth = width; }
794826

795827
/**
@@ -815,50 +847,78 @@ class CORE_EXPORT QgsLineSymbolLayer : public QgsSymbolLayer
815847
*/
816848
virtual double width( const QgsRenderContext &context ) const;
817849

818-
double offset() const { return mOffset; }
819-
void setOffset( double offset ) { mOffset = offset; }
820-
821850
/**
822-
* Sets the units for the line's width.
823-
* \param unit width units
824-
* \see widthUnit()
825-
*/
826-
void setWidthUnit( QgsUnitTypes::RenderUnit unit ) { mWidthUnit = unit; }
851+
* Returns the line's offset.
852+
*
853+
* Offset units can be retrieved by calling offsetUnit().
854+
*
855+
* \see setOffset()
856+
* \see offsetUnit()
857+
* \see offsetMapUnitScale()
858+
*/
859+
double offset() const { return mOffset; }
827860

828861
/**
829-
* Returns the units for the line's width.
830-
* \see setWidthUnit()
831-
*/
832-
QgsUnitTypes::RenderUnit widthUnit() const { return mWidthUnit; }
833-
834-
void setWidthMapUnitScale( const QgsMapUnitScale &scale ) { mWidthMapUnitScale = scale; }
835-
const QgsMapUnitScale &widthMapUnitScale() const { return mWidthMapUnitScale; }
862+
* Sets the line's \a offset.
863+
*
864+
* Offset units are set via setOffsetUnit().
865+
*
866+
* \see offset()
867+
* \see setOffsetUnit()
868+
* \see setOffsetMapUnitScale()
869+
*/
870+
void setOffset( double offset ) { mOffset = offset; }
836871

837872
/**
838-
* Sets the units for the line's offset.
839-
* \param unit offset units
873+
* Sets the \a unit for the line's offset.
840874
* \see offsetUnit()
875+
* \see setOffset()
876+
* \see setOffsetMapUnitScale()
841877
*/
842878
void setOffsetUnit( QgsUnitTypes::RenderUnit unit ) { mOffsetUnit = unit; }
843879

844880
/**
845881
* Returns the units for the line's offset.
846882
* \see setOffsetUnit()
883+
* \see offset()
884+
* \see offsetMapUnitScale()
847885
*/
848886
QgsUnitTypes::RenderUnit offsetUnit() const { return mOffsetUnit; }
849887

888+
/**
889+
* Sets the map unit \a scale for the line's offset.
890+
* \see offsetMapUnitScale()
891+
* \see setOffset()
892+
* \see setOffsetUnit()
893+
*/
850894
void setOffsetMapUnitScale( const QgsMapUnitScale &scale ) { mOffsetMapUnitScale = scale; }
895+
896+
/**
897+
* Returns the map unit scale for the line's offset.
898+
* \see setOffsetMapUnitScale()
899+
* \see offset()
900+
* \see offsetUnit()
901+
*/
851902
const QgsMapUnitScale &offsetMapUnitScale() const { return mOffsetMapUnitScale; }
852903

853-
void setOutputUnit( QgsUnitTypes::RenderUnit unit ) override;
854-
QgsUnitTypes::RenderUnit outputUnit() const override;
904+
// TODO QGIS 4.0 - setWidthUnit(), widthUnit(), setWidthUnitScale(), widthUnitScale()
905+
// only apply to simple line symbol layers and do not belong here.
855906

856-
void setMapUnitScale( const QgsMapUnitScale &scale ) override;
857-
QgsMapUnitScale mapUnitScale() const override;
907+
/**
908+
* Sets the units for the line's width.
909+
* \param unit width units
910+
* \see widthUnit()
911+
*/
912+
void setWidthUnit( QgsUnitTypes::RenderUnit unit ) { mWidthUnit = unit; }
858913

859-
void drawPreviewIcon( QgsSymbolRenderContext &context, QSize size ) override;
914+
/**
915+
* Returns the units for the line's width.
916+
* \see setWidthUnit()
917+
*/
918+
QgsUnitTypes::RenderUnit widthUnit() const { return mWidthUnit; }
860919

861-
double dxfWidth( const QgsDxfExport &e, QgsSymbolRenderContext &context ) const override;
920+
void setWidthMapUnitScale( const QgsMapUnitScale &scale ) { mWidthMapUnitScale = scale; }
921+
const QgsMapUnitScale &widthMapUnitScale() const { return mWidthMapUnitScale; }
862922

863923
/**
864924
* Returns the line symbol layer's ring filter, which controls which rings are

‎src/core/symbology/qgssymbollayerregistry.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ QgsSymbolLayerRegistry::QgsSymbolLayerRegistry()
3030
QgsSimpleLineSymbolLayer::create, QgsSimpleLineSymbolLayer::createFromSld ) );
3131
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "MarkerLine" ), QObject::tr( "Marker line" ), QgsSymbol::Line,
3232
QgsMarkerLineSymbolLayer::create, QgsMarkerLineSymbolLayer::createFromSld ) );
33+
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "HashLine" ), QObject::tr( "Hashed line" ), QgsSymbol::Line,
34+
QgsHashedLineSymbolLayer::create ) );
3335
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "ArrowLine" ), QObject::tr( "Arrow" ), QgsSymbol::Line, QgsArrowSymbolLayer::create ) );
3436

3537
addSymbolLayerType( new QgsSymbolLayerMetadata( QStringLiteral( "SimpleMarker" ), QObject::tr( "Simple marker" ), QgsSymbol::Marker,

‎src/gui/symbology/qgslayerpropertieswidget.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ static void _initWidgetFunctions()
6666

6767
_initWidgetFunction( QStringLiteral( "SimpleLine" ), QgsSimpleLineSymbolLayerWidget::create );
6868
_initWidgetFunction( QStringLiteral( "MarkerLine" ), QgsMarkerLineSymbolLayerWidget::create );
69+
_initWidgetFunction( QStringLiteral( "HashLine" ), QgsHashedLineSymbolLayerWidget::create );
6970
_initWidgetFunction( QStringLiteral( "ArrowLine" ), QgsArrowSymbolLayerWidget::create );
7071

7172
_initWidgetFunction( QStringLiteral( "SimpleMarker" ), QgsSimpleMarkerSymbolLayerWidget::create );

‎src/gui/symbology/qgssymbollayerwidget.cpp

Lines changed: 241 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,20 +1726,20 @@ void QgsMarkerLineSymbolLayerWidget::setSymbolLayer( QgsSymbolLayer *layer )
17261726
mSpinOffsetAlongLine->setValue( mLayer->offsetAlongLine() );
17271727
mSpinOffsetAlongLine->blockSignals( false );
17281728
chkRotateMarker->blockSignals( true );
1729-
chkRotateMarker->setChecked( mLayer->rotateMarker() );
1729+
chkRotateMarker->setChecked( mLayer->rotateSymbols() );
17301730
chkRotateMarker->blockSignals( false );
17311731
spinOffset->blockSignals( true );
17321732
spinOffset->setValue( mLayer->offset() );
17331733
spinOffset->blockSignals( false );
1734-
if ( mLayer->placement() == QgsMarkerLineSymbolLayer::Interval )
1734+
if ( mLayer->placement() == QgsTemplatedLineSymbolLayerBase::Interval )
17351735
radInterval->setChecked( true );
1736-
else if ( mLayer->placement() == QgsMarkerLineSymbolLayer::Vertex )
1736+
else if ( mLayer->placement() == QgsTemplatedLineSymbolLayerBase::Vertex )
17371737
radVertex->setChecked( true );
1738-
else if ( mLayer->placement() == QgsMarkerLineSymbolLayer::LastVertex )
1738+
else if ( mLayer->placement() == QgsTemplatedLineSymbolLayerBase::LastVertex )
17391739
radVertexLast->setChecked( true );
1740-
else if ( mLayer->placement() == QgsMarkerLineSymbolLayer::CentralPoint )
1740+
else if ( mLayer->placement() == QgsTemplatedLineSymbolLayerBase::CentralPoint )
17411741
radCentralPoint->setChecked( true );
1742-
else if ( mLayer->placement() == QgsMarkerLineSymbolLayer::CurvePoint )
1742+
else if ( mLayer->placement() == QgsTemplatedLineSymbolLayerBase::CurvePoint )
17431743
radCurvePoint->setChecked( true );
17441744
else
17451745
radVertexFirst->setChecked( true );
@@ -1787,7 +1787,7 @@ void QgsMarkerLineSymbolLayerWidget::setOffsetAlongLine( double val )
17871787

17881788
void QgsMarkerLineSymbolLayerWidget::setRotate()
17891789
{
1790-
mLayer->setRotateMarker( chkRotateMarker->isChecked() );
1790+
mLayer->setRotateSymbols( chkRotateMarker->isChecked() );
17911791
emit changed();
17921792
}
17931793

@@ -1804,17 +1804,17 @@ void QgsMarkerLineSymbolLayerWidget::setPlacement()
18041804
mSpinOffsetAlongLine->setEnabled( radInterval->isChecked() || radVertexLast->isChecked() || radVertexFirst->isChecked() );
18051805
//mLayer->setPlacement( interval ? QgsMarkerLineSymbolLayer::Interval : QgsMarkerLineSymbolLayer::Vertex );
18061806
if ( radInterval->isChecked() )
1807-
mLayer->setPlacement( QgsMarkerLineSymbolLayer::Interval );
1807+
mLayer->setPlacement( QgsTemplatedLineSymbolLayerBase::Interval );
18081808
else if ( radVertex->isChecked() )
1809-
mLayer->setPlacement( QgsMarkerLineSymbolLayer::Vertex );
1809+
mLayer->setPlacement( QgsTemplatedLineSymbolLayerBase::Vertex );
18101810
else if ( radVertexLast->isChecked() )
1811-
mLayer->setPlacement( QgsMarkerLineSymbolLayer::LastVertex );
1811+
mLayer->setPlacement( QgsTemplatedLineSymbolLayerBase::LastVertex );
18121812
else if ( radVertexFirst->isChecked() )
1813-
mLayer->setPlacement( QgsMarkerLineSymbolLayer::FirstVertex );
1813+
mLayer->setPlacement( QgsTemplatedLineSymbolLayerBase::FirstVertex );
18141814
else if ( radCurvePoint->isChecked() )
1815-
mLayer->setPlacement( QgsMarkerLineSymbolLayer::CurvePoint );
1815+
mLayer->setPlacement( QgsTemplatedLineSymbolLayerBase::CurvePoint );
18161816
else
1817-
mLayer->setPlacement( QgsMarkerLineSymbolLayer::CentralPoint );
1817+
mLayer->setPlacement( QgsTemplatedLineSymbolLayerBase::CentralPoint );
18181818

18191819
emit changed();
18201820
}
@@ -1849,6 +1849,234 @@ void QgsMarkerLineSymbolLayerWidget::mOffsetAlongLineUnitWidget_changed()
18491849
emit changed();
18501850
}
18511851

1852+
1853+
///////////
1854+
1855+
QgsHashedLineSymbolLayerWidget::QgsHashedLineSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent )
1856+
: QgsSymbolLayerWidget( parent, vl )
1857+
{
1858+
mLayer = nullptr;
1859+
1860+
setupUi( this );
1861+
connect( mIntervalUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsHashedLineSymbolLayerWidget::mIntervalUnitWidget_changed );
1862+
connect( mOffsetUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsHashedLineSymbolLayerWidget::mOffsetUnitWidget_changed );
1863+
connect( mOffsetAlongLineUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsHashedLineSymbolLayerWidget::mOffsetAlongLineUnitWidget_changed );
1864+
connect( mHashLengthUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsHashedLineSymbolLayerWidget::hashLengthUnitWidgetChanged );
1865+
mIntervalUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
1866+
<< QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
1867+
mOffsetUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
1868+
<< QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
1869+
mOffsetAlongLineUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
1870+
<< QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
1871+
1872+
mHashLengthUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
1873+
<< QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
1874+
1875+
mRingFilterComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "mIconAllRings.svg" ) ), tr( "All Rings" ), QgsLineSymbolLayer::AllRings );
1876+
mRingFilterComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "mIconExteriorRing.svg" ) ), tr( "Exterior Ring Only" ), QgsLineSymbolLayer::ExteriorRingOnly );
1877+
mRingFilterComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "mIconInteriorRings.svg" ) ), tr( "Interior Rings Only" ), QgsLineSymbolLayer::InteriorRingsOnly );
1878+
connect( mRingFilterComboBox, qgis::overload< int >::of( &QComboBox::currentIndexChanged ), this, [ = ]( int )
1879+
{
1880+
if ( mLayer )
1881+
{
1882+
mLayer->setRingFilter( static_cast< QgsLineSymbolLayer::RenderRingFilter >( mRingFilterComboBox->currentData().toInt() ) );
1883+
emit changed();
1884+
}
1885+
} );
1886+
1887+
spinOffset->setClearValue( 0.0 );
1888+
1889+
1890+
if ( vl && vl->geometryType() != QgsWkbTypes::PolygonGeometry )
1891+
{
1892+
mRingFilterComboBox->hide();
1893+
mRingsLabel->hide();
1894+
}
1895+
1896+
mHashRotationSpinBox->setClearValue( 0 );
1897+
1898+
connect( spinInterval, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsHashedLineSymbolLayerWidget::setInterval );
1899+
connect( mSpinOffsetAlongLine, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsHashedLineSymbolLayerWidget::setOffsetAlongLine );
1900+
connect( mSpinHashLength, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsHashedLineSymbolLayerWidget::setHashLength );
1901+
connect( mHashRotationSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsHashedLineSymbolLayerWidget::setHashAngle );
1902+
connect( chkRotateMarker, &QAbstractButton::clicked, this, &QgsHashedLineSymbolLayerWidget::setRotate );
1903+
connect( spinOffset, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsHashedLineSymbolLayerWidget::setOffset );
1904+
connect( radInterval, &QAbstractButton::clicked, this, &QgsHashedLineSymbolLayerWidget::setPlacement );
1905+
connect( radVertex, &QAbstractButton::clicked, this, &QgsHashedLineSymbolLayerWidget::setPlacement );
1906+
connect( radVertexLast, &QAbstractButton::clicked, this, &QgsHashedLineSymbolLayerWidget::setPlacement );
1907+
connect( radVertexFirst, &QAbstractButton::clicked, this, &QgsHashedLineSymbolLayerWidget::setPlacement );
1908+
connect( radCentralPoint, &QAbstractButton::clicked, this, &QgsHashedLineSymbolLayerWidget::setPlacement );
1909+
connect( radCurvePoint, &QAbstractButton::clicked, this, &QgsHashedLineSymbolLayerWidget::setPlacement );
1910+
}
1911+
1912+
void QgsHashedLineSymbolLayerWidget::setSymbolLayer( QgsSymbolLayer *layer )
1913+
{
1914+
if ( layer->layerType() != QLatin1String( "HashLine" ) )
1915+
return;
1916+
1917+
// layer type is correct, we can do the cast
1918+
mLayer = static_cast<QgsHashedLineSymbolLayer *>( layer );
1919+
1920+
// set values
1921+
spinInterval->blockSignals( true );
1922+
spinInterval->setValue( mLayer->interval() );
1923+
spinInterval->blockSignals( false );
1924+
mSpinOffsetAlongLine->blockSignals( true );
1925+
mSpinOffsetAlongLine->setValue( mLayer->offsetAlongLine() );
1926+
mSpinOffsetAlongLine->blockSignals( false );
1927+
whileBlocking( mSpinHashLength )->setValue( mLayer->hashLength() );
1928+
whileBlocking( mHashRotationSpinBox )->setValue( mLayer->hashAngle() );
1929+
chkRotateMarker->blockSignals( true );
1930+
chkRotateMarker->setChecked( mLayer->rotateSymbols() );
1931+
chkRotateMarker->blockSignals( false );
1932+
spinOffset->blockSignals( true );
1933+
spinOffset->setValue( mLayer->offset() );
1934+
spinOffset->blockSignals( false );
1935+
if ( mLayer->placement() == QgsTemplatedLineSymbolLayerBase::Interval )
1936+
radInterval->setChecked( true );
1937+
else if ( mLayer->placement() == QgsTemplatedLineSymbolLayerBase::Vertex )
1938+
radVertex->setChecked( true );
1939+
else if ( mLayer->placement() == QgsTemplatedLineSymbolLayerBase::LastVertex )
1940+
radVertexLast->setChecked( true );
1941+
else if ( mLayer->placement() == QgsTemplatedLineSymbolLayerBase::CentralPoint )
1942+
radCentralPoint->setChecked( true );
1943+
else if ( mLayer->placement() == QgsTemplatedLineSymbolLayerBase::CurvePoint )
1944+
radCurvePoint->setChecked( true );
1945+
else
1946+
radVertexFirst->setChecked( true );
1947+
1948+
// set units
1949+
mIntervalUnitWidget->blockSignals( true );
1950+
mIntervalUnitWidget->setUnit( mLayer->intervalUnit() );
1951+
mIntervalUnitWidget->setMapUnitScale( mLayer->intervalMapUnitScale() );
1952+
mIntervalUnitWidget->blockSignals( false );
1953+
mOffsetUnitWidget->blockSignals( true );
1954+
mOffsetUnitWidget->setUnit( mLayer->offsetUnit() );
1955+
mOffsetUnitWidget->setMapUnitScale( mLayer->offsetMapUnitScale() );
1956+
mOffsetUnitWidget->blockSignals( false );
1957+
mOffsetAlongLineUnitWidget->blockSignals( true );
1958+
mOffsetAlongLineUnitWidget->setUnit( mLayer->offsetAlongLineUnit() );
1959+
mOffsetAlongLineUnitWidget->setMapUnitScale( mLayer->offsetAlongLineMapUnitScale() );
1960+
mOffsetAlongLineUnitWidget->blockSignals( false );
1961+
1962+
whileBlocking( mHashLengthUnitWidget )->setUnit( mLayer->hashLengthUnit() );
1963+
whileBlocking( mHashLengthUnitWidget )->setMapUnitScale( mLayer->hashLengthMapUnitScale() );
1964+
1965+
whileBlocking( mRingFilterComboBox )->setCurrentIndex( mRingFilterComboBox->findData( mLayer->ringFilter() ) );
1966+
1967+
setPlacement(); // update gui
1968+
1969+
registerDataDefinedButton( mIntervalDDBtn, QgsSymbolLayer::PropertyInterval );
1970+
registerDataDefinedButton( mLineOffsetDDBtn, QgsSymbolLayer::PropertyOffset );
1971+
registerDataDefinedButton( mPlacementDDBtn, QgsSymbolLayer::PropertyPlacement );
1972+
registerDataDefinedButton( mOffsetAlongLineDDBtn, QgsSymbolLayer::PropertyOffsetAlongLine );
1973+
registerDataDefinedButton( mHashLengthDDBtn, QgsSymbolLayer::PropertyLineDistance );
1974+
registerDataDefinedButton( mHashRotationDDBtn, QgsSymbolLayer::PropertyLineAngle );
1975+
}
1976+
1977+
QgsSymbolLayer *QgsHashedLineSymbolLayerWidget::symbolLayer()
1978+
{
1979+
return mLayer;
1980+
}
1981+
1982+
void QgsHashedLineSymbolLayerWidget::setInterval( double val )
1983+
{
1984+
mLayer->setInterval( val );
1985+
emit changed();
1986+
}
1987+
1988+
void QgsHashedLineSymbolLayerWidget::setOffsetAlongLine( double val )
1989+
{
1990+
mLayer->setOffsetAlongLine( val );
1991+
emit changed();
1992+
}
1993+
1994+
void QgsHashedLineSymbolLayerWidget::setHashLength( double val )
1995+
{
1996+
mLayer->setHashLength( val );
1997+
emit changed();
1998+
}
1999+
2000+
void QgsHashedLineSymbolLayerWidget::setHashAngle( double val )
2001+
{
2002+
mLayer->setHashAngle( val );
2003+
emit changed();
2004+
}
2005+
2006+
void QgsHashedLineSymbolLayerWidget::setRotate()
2007+
{
2008+
mLayer->setRotateSymbols( chkRotateMarker->isChecked() );
2009+
emit changed();
2010+
}
2011+
2012+
void QgsHashedLineSymbolLayerWidget::setOffset()
2013+
{
2014+
mLayer->setOffset( spinOffset->value() );
2015+
emit changed();
2016+
}
2017+
2018+
void QgsHashedLineSymbolLayerWidget::setPlacement()
2019+
{
2020+
bool interval = radInterval->isChecked();
2021+
spinInterval->setEnabled( interval );
2022+
mSpinOffsetAlongLine->setEnabled( radInterval->isChecked() || radVertexLast->isChecked() || radVertexFirst->isChecked() );
2023+
//mLayer->setPlacement( interval ? QgsMarkerLineSymbolLayer::Interval : QgsMarkerLineSymbolLayer::Vertex );
2024+
if ( radInterval->isChecked() )
2025+
mLayer->setPlacement( QgsTemplatedLineSymbolLayerBase::Interval );
2026+
else if ( radVertex->isChecked() )
2027+
mLayer->setPlacement( QgsTemplatedLineSymbolLayerBase::Vertex );
2028+
else if ( radVertexLast->isChecked() )
2029+
mLayer->setPlacement( QgsTemplatedLineSymbolLayerBase::LastVertex );
2030+
else if ( radVertexFirst->isChecked() )
2031+
mLayer->setPlacement( QgsTemplatedLineSymbolLayerBase::FirstVertex );
2032+
else if ( radCurvePoint->isChecked() )
2033+
mLayer->setPlacement( QgsTemplatedLineSymbolLayerBase::CurvePoint );
2034+
else
2035+
mLayer->setPlacement( QgsTemplatedLineSymbolLayerBase::CentralPoint );
2036+
2037+
emit changed();
2038+
}
2039+
2040+
void QgsHashedLineSymbolLayerWidget::mIntervalUnitWidget_changed()
2041+
{
2042+
if ( mLayer )
2043+
{
2044+
mLayer->setIntervalUnit( mIntervalUnitWidget->unit() );
2045+
mLayer->setIntervalMapUnitScale( mIntervalUnitWidget->getMapUnitScale() );
2046+
emit changed();
2047+
}
2048+
}
2049+
2050+
void QgsHashedLineSymbolLayerWidget::mOffsetUnitWidget_changed()
2051+
{
2052+
if ( mLayer )
2053+
{
2054+
mLayer->setOffsetUnit( mOffsetUnitWidget->unit() );
2055+
mLayer->setOffsetMapUnitScale( mOffsetUnitWidget->getMapUnitScale() );
2056+
emit changed();
2057+
}
2058+
}
2059+
2060+
void QgsHashedLineSymbolLayerWidget::mOffsetAlongLineUnitWidget_changed()
2061+
{
2062+
if ( mLayer )
2063+
{
2064+
mLayer->setOffsetAlongLineUnit( mOffsetAlongLineUnitWidget->unit() );
2065+
mLayer->setOffsetAlongLineMapUnitScale( mOffsetAlongLineUnitWidget->getMapUnitScale() );
2066+
}
2067+
emit changed();
2068+
}
2069+
2070+
void QgsHashedLineSymbolLayerWidget::hashLengthUnitWidgetChanged()
2071+
{
2072+
if ( mLayer )
2073+
{
2074+
mLayer->setHashLengthUnit( mHashLengthUnitWidget->unit() );
2075+
mLayer->setHashLengthMapUnitScale( mHashLengthUnitWidget->getMapUnitScale() );
2076+
}
2077+
emit changed();
2078+
}
2079+
18522080
///////////
18532081

18542082

‎src/gui/symbology/qgssymbollayerwidget.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,57 @@ class GUI_EXPORT QgsMarkerLineSymbolLayerWidget : public QgsSymbolLayerWidget, p
502502
};
503503

504504

505+
#include "ui_widget_hashline.h"
506+
507+
class QgsHashedLineSymbolLayer;
508+
509+
/**
510+
* \ingroup gui
511+
* \class QgsHashedLineSymbolLayerWidget
512+
*/
513+
class GUI_EXPORT QgsHashedLineSymbolLayerWidget : public QgsSymbolLayerWidget, private Ui::WidgetHashedLine
514+
{
515+
Q_OBJECT
516+
517+
public:
518+
519+
/**
520+
* Constructor for QgsHashedLineSymbolLayerWidget.
521+
* \param vl associated vector layer
522+
* \param parent parent widget
523+
*/
524+
QgsHashedLineSymbolLayerWidget( QgsVectorLayer *vl, QWidget *parent SIP_TRANSFERTHIS = nullptr );
525+
526+
/**
527+
* Creates a new QgsHashedLineSymbolLayerWidget.
528+
* \param vl associated vector layer
529+
*/
530+
static QgsSymbolLayerWidget *create( QgsVectorLayer *vl ) SIP_FACTORY { return new QgsHashedLineSymbolLayerWidget( vl ); }
531+
532+
// from base class
533+
void setSymbolLayer( QgsSymbolLayer *layer ) override;
534+
QgsSymbolLayer *symbolLayer() override;
535+
536+
private slots:
537+
538+
void setInterval( double val );
539+
void setOffsetAlongLine( double val );
540+
void setHashLength( double val );
541+
void setHashAngle( double val );
542+
543+
void setRotate();
544+
void setOffset();
545+
void setPlacement();
546+
void mIntervalUnitWidget_changed();
547+
void mOffsetUnitWidget_changed();
548+
void mOffsetAlongLineUnitWidget_changed();
549+
void hashLengthUnitWidgetChanged();
550+
private:
551+
QgsHashedLineSymbolLayer *mLayer = nullptr;
552+
553+
554+
};
555+
505556
///////////
506557

507558
#include "ui_widget_svgmarker.h"

‎src/plugins/grass/qgsgrasseditrenderer.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ QgsGrassEditRenderer::QgsGrassEditRenderer()
6161
markerLayers << markerSymbolLayer;
6262
QgsMarkerSymbol *markerSymbol = new QgsMarkerSymbol( markerLayers );
6363
firstVertexMarkerLine->setSubSymbol( markerSymbol );
64-
firstVertexMarkerLine->setPlacement( QgsMarkerLineSymbolLayer::FirstVertex );
64+
firstVertexMarkerLine->setPlacement( QgsTemplatedLineSymbolLayerBase::FirstVertex );
6565
QgsMarkerLineSymbolLayer *lastVertexMarkerLine = static_cast<QgsMarkerLineSymbolLayer *>( firstVertexMarkerLine->clone() );
66-
lastVertexMarkerLine->setPlacement( QgsMarkerLineSymbolLayer::LastVertex );
66+
lastVertexMarkerLine->setPlacement( QgsTemplatedLineSymbolLayerBase::LastVertex );
6767
Q_FOREACH ( int value, colors.keys() )
6868
{
6969
QgsSymbol *symbol = QgsSymbol::defaultSymbol( QgsWkbTypes::LineGeometry );

‎src/ui/symbollayer/widget_hashline.ui

Lines changed: 436 additions & 0 deletions
Large diffs are not rendered by default.

‎tests/src/core/testqgsmarkerlinesymbol.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ void TestQgsMarkerLineSymbol::pointNumInterval()
144144
mMapSettings->setLayers( QList<QgsMapLayer *>() << mLinesLayer );
145145

146146
QgsMarkerLineSymbolLayer *ml = new QgsMarkerLineSymbolLayer();
147-
ml->setPlacement( QgsMarkerLineSymbolLayer::Interval );
147+
ml->setPlacement( QgsTemplatedLineSymbolLayerBase::Interval );
148148
ml->setInterval( 4 );
149149
QgsLineSymbol *lineSymbol = new QgsLineSymbol();
150150
lineSymbol->changeSymbolLayer( 0, ml );
@@ -174,7 +174,7 @@ void TestQgsMarkerLineSymbol::pointNumVertex()
174174
mMapSettings->setLayers( QList<QgsMapLayer *>() << mLinesLayer );
175175

176176
QgsMarkerLineSymbolLayer *ml = new QgsMarkerLineSymbolLayer();
177-
ml->setPlacement( QgsMarkerLineSymbolLayer::Vertex );
177+
ml->setPlacement( QgsTemplatedLineSymbolLayerBase::Vertex );
178178
QgsLineSymbol *lineSymbol = new QgsLineSymbol();
179179
lineSymbol->changeSymbolLayer( 0, ml );
180180
QgsSingleSymbolRenderer *r = new QgsSingleSymbolRenderer( lineSymbol );
@@ -203,7 +203,7 @@ void TestQgsMarkerLineSymbol::ringFilter()
203203
mMapSettings->setLayers( QList<QgsMapLayer *>() << mLinesLayer );
204204

205205
QgsMarkerLineSymbolLayer *ml = new QgsMarkerLineSymbolLayer();
206-
ml->setPlacement( QgsMarkerLineSymbolLayer::Vertex );
206+
ml->setPlacement( QgsTemplatedLineSymbolLayerBase::Vertex );
207207
QgsLineSymbol *lineSymbol = new QgsLineSymbol();
208208
lineSymbol->changeSymbolLayer( 0, ml );
209209
QgsSingleSymbolRenderer *r = new QgsSingleSymbolRenderer( lineSymbol );

‎tests/src/core/testqgssymbol.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ void TestQgsSymbol::testCanvasClip()
177177
ms.setLayers( QList<QgsMapLayer *>() << mpLinesLayer );
178178

179179
QgsMarkerLineSymbolLayer *markerLine = new QgsMarkerLineSymbolLayer();
180-
markerLine->setPlacement( QgsMarkerLineSymbolLayer:: CentralPoint );
180+
markerLine->setPlacement( QgsTemplatedLineSymbolLayerBase::CentralPoint );
181181
static_cast< QgsSimpleMarkerSymbolLayer *>( markerLine->subSymbol()->symbolLayer( 0 ) )->setStrokeColor( Qt::black );
182182
QgsLineSymbol *lineSymbol = new QgsLineSymbol();
183183
lineSymbol->changeSymbolLayer( 0, markerLine );
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
***************************************************************************
5+
test_qgsmarkerlinesymbollayer.py
6+
---------------------
7+
Date : November 2018
8+
Copyright : (C) 2018 by Nyall Dawson
9+
Email : nyall dot dawson at gmail dot com
10+
***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************
18+
"""
19+
20+
__author__ = 'Nyall Dawson'
21+
__date__ = 'November 2018'
22+
__copyright__ = '(C) 2018, Nyall Dawson'
23+
# This will get replaced with a git SHA1 when you do a git archive
24+
__revision__ = '$Format:%H$'
25+
26+
import qgis # NOQA
27+
28+
from utilities import unitTestDataPath
29+
30+
from qgis.PyQt.QtCore import QDir, Qt, QSize
31+
from qgis.PyQt.QtGui import QImage, QColor, QPainter
32+
from qgis.PyQt.QtXml import QDomDocument
33+
34+
from qgis.core import (QgsGeometry,
35+
QgsFillSymbol,
36+
QgsRenderContext,
37+
QgsFeature,
38+
QgsMapSettings,
39+
QgsRenderChecker,
40+
QgsReadWriteContext,
41+
QgsSymbolLayerUtils,
42+
QgsSimpleMarkerSymbolLayer,
43+
QgsLineSymbolLayer,
44+
QgsMarkerLineSymbolLayer,
45+
QgsMarkerSymbol,
46+
QgsGeometryGeneratorSymbolLayer,
47+
QgsSymbol,
48+
QgsFontMarkerSymbolLayer,
49+
QgsFontUtils,
50+
QgsLineSymbol,
51+
QgsSymbolLayer,
52+
QgsProperty,
53+
QgsRectangle,
54+
QgsUnitTypes
55+
)
56+
57+
from qgis.testing import unittest, start_app
58+
59+
start_app()
60+
TEST_DATA_DIR = unitTestDataPath()
61+
62+
63+
class TestQgsMarkerLineSymbolLayer(unittest.TestCase):
64+
65+
def setUp(self):
66+
self.report = "<h1>Python QgsMarkerLineSymbolLayer Tests</h1>\n"
67+
68+
def tearDown(self):
69+
report_file_path = "%s/qgistest.html" % QDir.tempPath()
70+
with open(report_file_path, 'a') as report_file:
71+
report_file.write(self.report)
72+
73+
def testWidth(self):
74+
ms = QgsMapSettings()
75+
extent = QgsRectangle(100, 200, 100, 200)
76+
ms.setExtent(extent)
77+
ms.setOutputSize(QSize(400, 400))
78+
context = QgsRenderContext.fromMapSettings(ms)
79+
context.setScaleFactor(96 / 25.4) # 96 DPI
80+
ms.setExtent(QgsRectangle(100, 150, 100, 150))
81+
ms.setOutputDpi(ms.outputDpi() * 2)
82+
context2 = QgsRenderContext.fromMapSettings(ms)
83+
context2.setScaleFactor(300 / 25.4)
84+
85+
s = QgsFillSymbol()
86+
s.deleteSymbolLayer(0)
87+
88+
marker_line = QgsMarkerLineSymbolLayer(True)
89+
marker_line.setPlacement(QgsMarkerLineSymbolLayer.FirstVertex)
90+
marker = QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayer.Triangle, 10)
91+
marker.setColor(QColor(255, 0, 0))
92+
marker.setStrokeStyle(Qt.NoPen)
93+
marker_symbol = QgsMarkerSymbol()
94+
marker_symbol.changeSymbolLayer(0, marker)
95+
marker_line.setSubSymbol(marker_symbol)
96+
97+
self.assertEqual(marker_line.width(), 10)
98+
self.assertAlmostEqual(marker_line.width(context), 37.795275590551185, 3)
99+
self.assertAlmostEqual(marker_line.width(context2), 118.11023622047244, 3)
100+
101+
marker_line.subSymbol().setSizeUnit(QgsUnitTypes.RenderPixels)
102+
self.assertAlmostEqual(marker_line.width(context), 10.0, 3)
103+
self.assertAlmostEqual(marker_line.width(context2), 10.0, 3)
104+
105+
def testRingFilter(self):
106+
# test filtering rings during rendering
107+
s = QgsFillSymbol()
108+
s.deleteSymbolLayer(0)
109+
110+
marker_line = QgsMarkerLineSymbolLayer(True)
111+
marker_line.setPlacement(QgsMarkerLineSymbolLayer.FirstVertex)
112+
marker = QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayer.Triangle, 4)
113+
marker.setColor(QColor(255, 0, 0))
114+
marker.setStrokeStyle(Qt.NoPen)
115+
marker_symbol = QgsMarkerSymbol()
116+
marker_symbol.changeSymbolLayer(0, marker)
117+
marker_line.setSubSymbol(marker_symbol)
118+
119+
s.appendSymbolLayer(marker_line.clone())
120+
self.assertEqual(s.symbolLayer(0).ringFilter(), QgsLineSymbolLayer.AllRings)
121+
s.symbolLayer(0).setRingFilter(QgsLineSymbolLayer.ExteriorRingOnly)
122+
self.assertEqual(s.symbolLayer(0).ringFilter(), QgsLineSymbolLayer.ExteriorRingOnly)
123+
124+
s2 = s.clone()
125+
self.assertEqual(s2.symbolLayer(0).ringFilter(), QgsLineSymbolLayer.ExteriorRingOnly)
126+
127+
doc = QDomDocument()
128+
context = QgsReadWriteContext()
129+
element = QgsSymbolLayerUtils.saveSymbol('test', s, doc, context)
130+
131+
s2 = QgsSymbolLayerUtils.loadSymbol(element, context)
132+
self.assertEqual(s2.symbolLayer(0).ringFilter(), QgsLineSymbolLayer.ExteriorRingOnly)
133+
134+
# rendering test
135+
s3 = QgsFillSymbol()
136+
s3.deleteSymbolLayer(0)
137+
s3.appendSymbolLayer(
138+
QgsMarkerLineSymbolLayer())
139+
s3.symbolLayer(0).setRingFilter(QgsLineSymbolLayer.ExteriorRingOnly)
140+
141+
g = QgsGeometry.fromWkt('Polygon((0 0, 10 0, 10 10, 0 10, 0 0),(1 1, 1 2, 2 2, 2 1, 1 1),(8 8, 9 8, 9 9, 8 9, 8 8))')
142+
rendered_image = self.renderGeometry(s3, g)
143+
assert self.imageCheck('markerline_exterioronly', 'markerline_exterioronly', rendered_image)
144+
145+
s3.symbolLayer(0).setRingFilter(QgsLineSymbolLayer.InteriorRingsOnly)
146+
g = QgsGeometry.fromWkt('Polygon((0 0, 10 0, 10 10, 0 10, 0 0),(1 1, 1 2, 2 2, 2 1, 1 1),(8 8, 9 8, 9 9, 8 9, 8 8))')
147+
rendered_image = self.renderGeometry(s3, g)
148+
assert self.imageCheck('markerline_interioronly', 'markerline_interioronly', rendered_image)
149+
150+
def testPartNum(self):
151+
# test geometry_part_num variable
152+
s = QgsLineSymbol()
153+
s.deleteSymbolLayer(0)
154+
155+
sym_layer = QgsGeometryGeneratorSymbolLayer.create({'geometryModifier': 'segments_to_lines($geometry)'})
156+
sym_layer.setSymbolType(QgsSymbol.Line)
157+
s.appendSymbolLayer(sym_layer)
158+
159+
marker_line = QgsMarkerLineSymbolLayer(False)
160+
marker_line.setPlacement(QgsMarkerLineSymbolLayer.FirstVertex)
161+
f = QgsFontUtils.getStandardTestFont('Bold', 24)
162+
marker = QgsFontMarkerSymbolLayer(f.family(), 'x', 24, QColor(255, 255, 0))
163+
marker.setDataDefinedProperty(QgsSymbolLayer.PropertyCharacter, QgsProperty.fromExpression('@geometry_part_num'))
164+
marker_symbol = QgsMarkerSymbol()
165+
marker_symbol.changeSymbolLayer(0, marker)
166+
marker_line.setSubSymbol(marker_symbol)
167+
line_symbol = QgsLineSymbol()
168+
line_symbol.changeSymbolLayer(0, marker_line)
169+
sym_layer.setSubSymbol(line_symbol)
170+
171+
# rendering test
172+
g = QgsGeometry.fromWkt('LineString(0 0, 10 0, 10 10, 0 10)')
173+
rendered_image = self.renderGeometry(s, g, buffer=4)
174+
assert self.imageCheck('part_num_variable', 'part_num_variable', rendered_image)
175+
176+
marker.setDataDefinedProperty(QgsSymbolLayer.PropertyCharacter,
177+
QgsProperty.fromExpression('@geometry_part_count'))
178+
179+
# rendering test
180+
g = QgsGeometry.fromWkt('LineString(0 0, 10 0, 10 10, 0 10)')
181+
rendered_image = self.renderGeometry(s, g, buffer=4)
182+
assert self.imageCheck('part_count_variable', 'part_count_variable', rendered_image)
183+
184+
def renderGeometry(self, symbol, geom, buffer=20):
185+
f = QgsFeature()
186+
f.setGeometry(geom)
187+
188+
image = QImage(200, 200, QImage.Format_RGB32)
189+
190+
painter = QPainter()
191+
ms = QgsMapSettings()
192+
extent = geom.get().boundingBox()
193+
# buffer extent by 10%
194+
if extent.width() > 0:
195+
extent = extent.buffered((extent.height() + extent.width()) / buffer)
196+
else:
197+
extent = extent.buffered(buffer / 2)
198+
199+
ms.setExtent(extent)
200+
ms.setOutputSize(image.size())
201+
context = QgsRenderContext.fromMapSettings(ms)
202+
context.setPainter(painter)
203+
context.setScaleFactor(96 / 25.4) # 96 DPI
204+
context.expressionContext().setFeature(f)
205+
206+
painter.begin(image)
207+
try:
208+
image.fill(QColor(0, 0, 0))
209+
symbol.startRender(context)
210+
symbol.renderFeature(f, context)
211+
symbol.stopRender(context)
212+
finally:
213+
painter.end()
214+
215+
return image
216+
217+
def imageCheck(self, name, reference_image, image):
218+
self.report += "<h2>Render {}</h2>\n".format(name)
219+
temp_dir = QDir.tempPath() + '/'
220+
file_name = temp_dir + 'symbol_' + name + ".png"
221+
image.save(file_name, "PNG")
222+
checker = QgsRenderChecker()
223+
checker.setControlPathPrefix("symbol_markerline")
224+
checker.setControlName("expected_" + reference_image)
225+
checker.setRenderedImage(file_name)
226+
checker.setColorTolerance(2)
227+
result = checker.compareImages(name, 20)
228+
self.report += checker.report()
229+
print((self.report))
230+
return result
231+
232+
233+
if __name__ == '__main__':
234+
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.