Skip to content
Permalink
Browse files

Merge pull request #41483 from nyalldawson/octagon

Add "octagon" and "square with corners" shapes to simple marker
  • Loading branch information
nyalldawson authored and github-actions committed Feb 11, 2021
1 parent dbd1c41 commit 9d4f07408f449f0e2ed8d2332c14321d56040913
@@ -49,6 +49,9 @@ leaves the actual drawing of the symbols to subclasses.
DiagonalHalfSquare,
RightHalfTriangle,
LeftHalfTriangle,
Octagon,
SquareWithCorners,
AsteriskFill,
};

static QList< QgsSimpleMarkerSymbolLayerBase::Shape > availableShapes();
@@ -64,6 +64,8 @@ QList<QgsSimpleMarkerSymbolLayerBase::Shape> QgsSimpleMarkerSymbolLayerBase::ava
<< Diamond
<< Pentagon
<< Hexagon
<< Octagon
<< SquareWithCorners
<< Triangle
<< EquilateralTriangle
<< Star
@@ -82,7 +84,9 @@ QList<QgsSimpleMarkerSymbolLayerBase::Shape> QgsSimpleMarkerSymbolLayerBase::ava
<< HalfSquare
<< DiagonalHalfSquare
<< RightHalfTriangle
<< LeftHalfTriangle;
<< LeftHalfTriangle
<< AsteriskFill;

return shapes;
}

@@ -105,6 +109,8 @@ bool QgsSimpleMarkerSymbolLayerBase::shapeIsFilled( QgsSimpleMarkerSymbolLayerBa
case Diamond:
case Pentagon:
case Hexagon:
case Octagon:
case SquareWithCorners:
case Triangle:
case EquilateralTriangle:
case Star:
@@ -120,6 +126,7 @@ bool QgsSimpleMarkerSymbolLayerBase::shapeIsFilled( QgsSimpleMarkerSymbolLayerBa
case DiagonalHalfSquare:
case RightHalfTriangle:
case LeftHalfTriangle:
case AsteriskFill:
return true;

case Cross:
@@ -299,12 +306,16 @@ QgsSimpleMarkerSymbolLayerBase::Shape QgsSimpleMarkerSymbolLayerBase::decodeShap

if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
return Square;
else if ( cleaned == QLatin1String( "square_with_corners" ) )
return SquareWithCorners;
else if ( cleaned == QLatin1String( "diamond" ) )
return Diamond;
else if ( cleaned == QLatin1String( "pentagon" ) )
return Pentagon;
else if ( cleaned == QLatin1String( "hexagon" ) )
return Hexagon;
else if ( cleaned == QLatin1String( "octagon" ) )
return Octagon;
else if ( cleaned == QLatin1String( "triangle" ) )
return Triangle;
else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
@@ -343,6 +354,8 @@ QgsSimpleMarkerSymbolLayerBase::Shape QgsSimpleMarkerSymbolLayerBase::decodeShap
return RightHalfTriangle;
else if ( cleaned == QLatin1String( "left_half_triangle" ) )
return LeftHalfTriangle;
else if ( cleaned == QLatin1String( "asterisk_fill" ) )
return AsteriskFill;

if ( ok )
*ok = false;
@@ -367,6 +380,10 @@ QString QgsSimpleMarkerSymbolLayerBase::encodeShape( QgsSimpleMarkerSymbolLayerB
return QStringLiteral( "pentagon" );
case Hexagon:
return QStringLiteral( "hexagon" );
case Octagon:
return QStringLiteral( "octagon" );
case SquareWithCorners:
return QStringLiteral( "square_with_corners" );
case Triangle:
return QStringLiteral( "triangle" );
case EquilateralTriangle:
@@ -399,6 +416,8 @@ QString QgsSimpleMarkerSymbolLayerBase::encodeShape( QgsSimpleMarkerSymbolLayerB
return QStringLiteral( "third_circle" );
case QuarterCircle:
return QStringLiteral( "quarter_circle" );
case AsteriskFill:
return QStringLiteral( "asterisk_fill" );
}
return QString();
}
@@ -418,6 +437,22 @@ bool QgsSimpleMarkerSymbolLayerBase::shapeToPolygon( QgsSimpleMarkerSymbolLayerB
polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
return true;

case SquareWithCorners:
{
static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;

polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
<< QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
<< QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
<< QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
<< QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
<< QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
<< QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
<< QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
<< QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
return true;
}

case QuarterSquare:
polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
return true;
@@ -467,6 +502,22 @@ bool QgsSimpleMarkerSymbolLayerBase::shapeToPolygon( QgsSimpleMarkerSymbolLayerB
<< QPointF( -0.8660, -0.5 );
return true;

case Octagon:
{
static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );

polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
<< QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
<< QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
<< QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
<< QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
<< QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
<< QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
<< QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
<< QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
return true;
}

case Triangle:
polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
return true;
@@ -540,6 +591,42 @@ bool QgsSimpleMarkerSymbolLayerBase::shapeToPolygon( QgsSimpleMarkerSymbolLayerB
<< QPointF( -1, -0.2 );
return true;

case AsteriskFill:
{
static constexpr double THICKNESS = 0.3;
static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;

polygon << QPointF( -HALF_THICKNESS, -1 )
<< QPointF( HALF_THICKNESS, -1 )
<< QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
<< QPointF( DIAGONAL1, -DIAGONAL2 )
<< QPointF( DIAGONAL2, -DIAGONAL1 )
<< QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
<< QPointF( 1, -HALF_THICKNESS )
<< QPointF( 1, HALF_THICKNESS )
<< QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
<< QPointF( DIAGONAL2, DIAGONAL1 )
<< QPointF( DIAGONAL1, DIAGONAL2 )
<< QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
<< QPointF( HALF_THICKNESS, 1 )
<< QPointF( -HALF_THICKNESS, 1 )
<< QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
<< QPointF( -DIAGONAL1, DIAGONAL2 )
<< QPointF( -DIAGONAL2, DIAGONAL1 )
<< QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
<< QPointF( -1, HALF_THICKNESS )
<< QPointF( -1, -HALF_THICKNESS )
<< QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
<< QPointF( -DIAGONAL2, -DIAGONAL1 )
<< QPointF( -DIAGONAL1, -DIAGONAL2 )
<< QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
<< QPointF( -HALF_THICKNESS, -1 );
return true;
}

case Circle:
case Cross:
case Cross2:
@@ -606,12 +693,14 @@ bool QgsSimpleMarkerSymbolLayerBase::prepareMarkerPath( QgsSimpleMarkerSymbolLay
return true;

case Square:
case SquareWithCorners:
case QuarterSquare:
case HalfSquare:
case DiagonalHalfSquare:
case Diamond:
case Pentagon:
case Hexagon:
case Octagon:
case Triangle:
case EquilateralTriangle:
case LeftHalfTriangle:
@@ -620,6 +709,7 @@ bool QgsSimpleMarkerSymbolLayerBase::prepareMarkerPath( QgsSimpleMarkerSymbolLay
case Arrow:
case ArrowHeadFilled:
case CrossFill:
case AsteriskFill:
return false;
}
return false;
@@ -42,6 +42,7 @@
*/
class CORE_EXPORT QgsSimpleMarkerSymbolLayerBase : public QgsMarkerSymbolLayer
{

public:

//! Marker symbol shapes
@@ -70,6 +71,9 @@ class CORE_EXPORT QgsSimpleMarkerSymbolLayerBase : public QgsMarkerSymbolLayer
DiagonalHalfSquare, //!< Diagonal half square (bottom left half)
RightHalfTriangle, //!< Right half of triangle
LeftHalfTriangle, //!< Left half of triangle
Octagon, //!< Octagon (since QGIS 3.18)
SquareWithCorners, //!< A square with diagonal corners (since QGIS 3.18)
AsteriskFill, //!< A filled asterisk shape (since QGIS 3.18)
};

//! Returns a list of all available shape types.
@@ -69,13 +69,18 @@ class TestQgsSimpleMarkerSymbol : public QObject
void init() {} // will be called before each testfunction is executed.
void cleanup() {} // will be called after every testfunction.

void decodeShape_data();
void decodeShape();
void simpleMarkerSymbol();
void simpleMarkerSymbolRotation();
void simpleMarkerSymbolPreviewRotation();
void simpleMarkerSymbolPreviewRotation_data();
void simpleMarkerSymbolBevelJoin();
void simpleMarkerSymbolMiterJoin();
void simpleMarkerSymbolRoundJoin();
void simpleMarkerOctagon();
void simpleMarkerSquareWithCorners();
void simpleMarkerAsterisk();
void bounds();
void boundsWithOffset();
void boundsWithRotation();
@@ -150,6 +155,61 @@ void TestQgsSimpleMarkerSymbol::cleanupTestCase()
QgsApplication::exitQgis();
}

void TestQgsSimpleMarkerSymbol::decodeShape_data()
{
QTest::addColumn<QString>( "string" );
QTest::addColumn<int>( "shape" );
QTest::addColumn<bool>( "ok" );

QTest::newRow( "empty string" ) << "" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Circle ) << false;
QTest::newRow( "invalid character" ) << "@" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Circle ) << false;
QTest::newRow( "square" ) << "square" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Square ) << true;
QTest::newRow( "square case" ) << "SQUARE" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Square ) << true;
QTest::newRow( "square case spaces" ) << " SQUARE " << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Square ) << true;
QTest::newRow( "square_with_corners" ) << "square_with_corners" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::SquareWithCorners ) << true;
QTest::newRow( "rectangle" ) << "rectangle" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Square ) << true;
QTest::newRow( "diamond" ) << "diamond" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Diamond ) << true;
QTest::newRow( "pentagon" ) << "pentagon" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Pentagon ) << true;
QTest::newRow( "hexagon" ) << "hexagon" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Hexagon ) << true;
QTest::newRow( "octagon" ) << "octagon" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Octagon ) << true;
QTest::newRow( "triangle" ) << "triangle" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Triangle ) << true;
QTest::newRow( "equilateral_triangle" ) << "equilateral_triangle" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::EquilateralTriangle ) << true;
QTest::newRow( "star" ) << "star" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Star ) << true;
QTest::newRow( "regular_star" ) << "regular_star" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Star ) << true;
QTest::newRow( "arrow" ) << "arrow" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Arrow ) << true;
QTest::newRow( "circle" ) << "circle" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Circle ) << true;
QTest::newRow( "cross" ) << "cross" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Cross ) << true;
QTest::newRow( "cross_fill" ) << "cross_fill" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::CrossFill ) << true;
QTest::newRow( "cross2" ) << "cross2" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Cross2 ) << true;
QTest::newRow( "x" ) << "x" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Cross2 ) << true;
QTest::newRow( "line" ) << "line" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::Line ) << true;
QTest::newRow( "arrowhead" ) << "arrowhead" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::ArrowHead ) << true;
QTest::newRow( "filled_arrowhead" ) << "filled_arrowhead" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::ArrowHeadFilled ) << true;
QTest::newRow( "semi_circle" ) << "semi_circle" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::SemiCircle ) << true;
QTest::newRow( "third_circle" ) << "third_circle" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::ThirdCircle ) << true;
QTest::newRow( "quarter_circle" ) << "quarter_circle" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::QuarterCircle ) << true;
QTest::newRow( "quarter_square" ) << "quarter_square" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::QuarterSquare ) << true;
QTest::newRow( "half_square" ) << "half_square" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::HalfSquare ) << true;
QTest::newRow( "diagonal_half_square" ) << "diagonal_half_square" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::DiagonalHalfSquare ) << true;
QTest::newRow( "right_half_triangle" ) << "right_half_triangle" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::RightHalfTriangle ) << true;
QTest::newRow( "left_half_triangle" ) << "left_half_triangle" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::LeftHalfTriangle ) << true;
QTest::newRow( "asterisk_fill" ) << "asterisk_fill" << static_cast< int >( QgsSimpleMarkerSymbolLayerBase::AsteriskFill ) << true;
}

void TestQgsSimpleMarkerSymbol::decodeShape()
{
QFETCH( QString, string );
QFETCH( int, shape );
QFETCH( bool, ok );

bool res = false;
QCOMPARE( static_cast< int >( QgsSimpleMarkerSymbolLayerBase::decodeShape( string, &res ) ), shape );
QCOMPARE( res, ok );

// round trip through encode
QCOMPARE( static_cast< int >( QgsSimpleMarkerSymbolLayerBase::decodeShape( QgsSimpleMarkerSymbolLayerBase::encodeShape( static_cast< QgsSimpleMarkerSymbolLayerBase::Shape>( shape ) ) ) ), shape );
}

void TestQgsSimpleMarkerSymbol::simpleMarkerSymbol()
{
mReport += QLatin1String( "<h2>Simple marker symbol layer test</h2>\n" );
@@ -247,6 +307,45 @@ void TestQgsSimpleMarkerSymbol::simpleMarkerSymbolRoundJoin()
QVERIFY( imageCheck( "simplemarker_roundjoin" ) );
}

void TestQgsSimpleMarkerSymbol::simpleMarkerOctagon()
{
mReport += QLatin1String( "<h2>Simple marker octagon</h2>\n" );

mSimpleMarkerLayer->setColor( Qt::blue );
mSimpleMarkerLayer->setStrokeColor( Qt::black );
mSimpleMarkerLayer->setShape( QgsSimpleMarkerSymbolLayerBase::Octagon );
mSimpleMarkerLayer->setSize( 25 );
mSimpleMarkerLayer->setStrokeWidth( 2 );
mSimpleMarkerLayer->setPenJoinStyle( Qt::MiterJoin );
QVERIFY( imageCheck( "simplemarker_octagon" ) );
}

void TestQgsSimpleMarkerSymbol::simpleMarkerSquareWithCorners()
{
mReport += QLatin1String( "<h2>Simple marker square with corners</h2>\n" );

mSimpleMarkerLayer->setColor( Qt::blue );
mSimpleMarkerLayer->setStrokeColor( Qt::black );
mSimpleMarkerLayer->setShape( QgsSimpleMarkerSymbolLayerBase::SquareWithCorners );
mSimpleMarkerLayer->setSize( 25 );
mSimpleMarkerLayer->setStrokeWidth( 2 );
mSimpleMarkerLayer->setPenJoinStyle( Qt::MiterJoin );
QVERIFY( imageCheck( "simplemarker_square_with_corners" ) );
}

void TestQgsSimpleMarkerSymbol::simpleMarkerAsterisk()
{
mReport += QLatin1String( "<h2>Simple marker asterisk</h2>\n" );

mSimpleMarkerLayer->setColor( Qt::blue );
mSimpleMarkerLayer->setStrokeColor( Qt::black );
mSimpleMarkerLayer->setShape( QgsSimpleMarkerSymbolLayerBase::AsteriskFill );
mSimpleMarkerLayer->setSize( 25 );
mSimpleMarkerLayer->setStrokeWidth( 2 );
mSimpleMarkerLayer->setPenJoinStyle( Qt::MiterJoin );
QVERIFY( imageCheck( "simplemarker_asterisk" ) );
}

void TestQgsSimpleMarkerSymbol::bounds()
{
mSimpleMarkerLayer->setColor( QColor( 200, 200, 200 ) );
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 9d4f074

Please sign in to comment.