Skip to content

Commit

Permalink
[FEATURE][layouts] Add "Stepped Line" scalebar style
Browse files Browse the repository at this point in the history
Designed to match the appearance and behavior of the ArcMap equivalent,
this was a scalebar format which was previously impossible to replicate
in QGIS.

Also fixes #26589

Sponsored by SLYR
  • Loading branch information
nyalldawson committed Mar 22, 2020
1 parent 3747098 commit e7cc830
Show file tree
Hide file tree
Showing 11 changed files with 255 additions and 7 deletions.
@@ -0,0 +1,50 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/scalebar/qgssteppedlinescalebarrenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/



class QgsSteppedLineScaleBarRenderer: QgsScaleBarRenderer
{
%Docstring
Scalebar style that draws a stepped line representation of a scalebar.

.. versionadded:: 3.14
%End

%TypeHeaderCode
#include "qgssteppedlinescalebarrenderer.h"
%End
public:

QgsSteppedLineScaleBarRenderer();
%Docstring
Constructor for QgsSteppedLineScaleBarRenderer.
%End

virtual QString id() const;

virtual QString visibleName() const;

virtual int sortKey() const;

virtual QgsSteppedLineScaleBarRenderer* clone() const /Factory/;


virtual void draw( QgsRenderContext &context,
const QgsScaleBarSettings &settings,
const QgsScaleBarRenderer::ScaleBarContext &scaleContext ) const;

};

/************************************************************************
* This file has been generated automatically from *
* *
* src/core/scalebar/qgssteppedlinescalebarrenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
1 change: 1 addition & 0 deletions python/core/core_auto.sip
Expand Up @@ -491,6 +491,7 @@
%Include auto_generated/scalebar/qgsscalebarrendererregistry.sip
%Include auto_generated/scalebar/qgsscalebarsettings.sip
%Include auto_generated/scalebar/qgssingleboxscalebarrenderer.sip
%Include auto_generated/scalebar/qgssteppedlinescalebarrenderer.sip
%Include auto_generated/scalebar/qgsticksscalebarrenderer.sip
%Include auto_generated/symbology/qgs25drenderer.sip
%Include auto_generated/symbology/qgsarrowsymbollayer.sip
Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -186,6 +186,7 @@ SET(QGIS_CORE_SRCS
scalebar/qgsscalebarrendererregistry.cpp
scalebar/qgsscalebarsettings.cpp
scalebar/qgssingleboxscalebarrenderer.cpp
scalebar/qgssteppedlinescalebarrenderer.cpp
scalebar/qgsticksscalebarrenderer.cpp

qgis.cpp
Expand Down Expand Up @@ -1276,6 +1277,7 @@ SET(QGIS_CORE_HDRS
scalebar/qgsscalebarrendererregistry.h
scalebar/qgsscalebarsettings.h
scalebar/qgssingleboxscalebarrenderer.h
scalebar/qgssteppedlinescalebarrenderer.h
scalebar/qgsticksscalebarrenderer.h

symbology/qgs25drenderer.h
Expand Down
5 changes: 1 addition & 4 deletions src/core/layout/qgslayoutitemscalebar.cpp
Expand Up @@ -21,12 +21,9 @@
#include "qgslayout.h"
#include "qgslayoututils.h"
#include "qgsdistancearea.h"
#include "qgssingleboxscalebarrenderer.h"
#include "qgsscalebarrenderer.h"
#include "qgsdoubleboxscalebarrenderer.h"
#include "qgsmapsettings.h"
#include "qgsnumericscalebarrenderer.h"
#include "qgssingleboxscalebarrenderer.h"
#include "qgsticksscalebarrenderer.h"
#include "qgsrectangle.h"
#include "qgsproject.h"
#include "qgssymbollayerutils.h"
Expand Down
2 changes: 1 addition & 1 deletion src/core/scalebar/qgsnumericscalebarrenderer.cpp
Expand Up @@ -33,7 +33,7 @@ QString QgsNumericScaleBarRenderer::visibleName() const

int QgsNumericScaleBarRenderer::sortKey() const
{
return 7;
return 8;
}

QgsNumericScaleBarRenderer *QgsNumericScaleBarRenderer::clone() const
Expand Down
2 changes: 2 additions & 0 deletions src/core/scalebar/qgsscalebarrendererregistry.cpp
Expand Up @@ -20,6 +20,7 @@
#include "qgsnumericscalebarrenderer.h"
#include "qgssingleboxscalebarrenderer.h"
#include "qgsticksscalebarrenderer.h"
#include "qgssteppedlinescalebarrenderer.h"

QgsScaleBarRendererRegistry::QgsScaleBarRendererRegistry()
{
Expand All @@ -29,6 +30,7 @@ QgsScaleBarRendererRegistry::QgsScaleBarRendererRegistry()
addRenderer( new QgsTicksScaleBarRenderer( QgsTicksScaleBarRenderer::TicksUp ) );
addRenderer( new QgsTicksScaleBarRenderer( QgsTicksScaleBarRenderer::TicksDown ) );
addRenderer( new QgsTicksScaleBarRenderer( QgsTicksScaleBarRenderer::TicksMiddle ) );
addRenderer( new QgsSteppedLineScaleBarRenderer() );
}

QgsScaleBarRendererRegistry::~QgsScaleBarRendererRegistry()
Expand Down
100 changes: 100 additions & 0 deletions src/core/scalebar/qgssteppedlinescalebarrenderer.cpp
@@ -0,0 +1,100 @@
/***************************************************************************
qgssteppedlinescalebarrenderer.cpp
--------------------------------
begin : March 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgssteppedlinescalebarrenderer.h"
#include "qgsscalebarsettings.h"
#include "qgslayoututils.h"
#include "qgssymbol.h"
#include <QList>
#include <QPainter>

QString QgsSteppedLineScaleBarRenderer::id() const
{
return QStringLiteral( "stepped" );
}

QString QgsSteppedLineScaleBarRenderer::visibleName() const
{
return QObject::tr( "Stepped Line" );
}

int QgsSteppedLineScaleBarRenderer::sortKey() const
{
return 7;
}

QgsSteppedLineScaleBarRenderer *QgsSteppedLineScaleBarRenderer::clone() const
{
return new QgsSteppedLineScaleBarRenderer( *this );
}

void QgsSteppedLineScaleBarRenderer::draw( QgsRenderContext &context, const QgsScaleBarSettings &settings, const ScaleBarContext &scaleContext ) const
{
if ( !context.painter() )
{
return;
}
QPainter *painter = context.painter();

std::unique_ptr< QgsLineSymbol > sym( settings.lineSymbol()->clone() );
sym->startRender( context ) ;

double scaledLabelBarSpace = context.convertToPainterUnits( settings.labelBarSpace(), QgsUnitTypes::RenderMillimeters );
double scaledBoxContentSpace = context.convertToPainterUnits( settings.boxContentSpace(), QgsUnitTypes::RenderMillimeters );
QFontMetricsF fontMetrics = QgsTextRenderer::fontMetrics( context, settings.textFormat() );
double barTopPosition = scaledBoxContentSpace + ( settings.labelVerticalPlacement() == QgsScaleBarSettings::LabelAboveSegment ? fontMetrics.ascent() + scaledLabelBarSpace : 0 );
const double barBottomPosition = barTopPosition + context.convertToPainterUnits( settings.height(), QgsUnitTypes::RenderMillimeters );

painter->save();
if ( context.flags() & QgsRenderContext::Antialiasing )
painter->setRenderHint( QPainter::Antialiasing, true );

painter->setPen( Qt::NoPen );

double xOffset = firstLabelXOffset( settings, context, scaleContext );

QList<double> positions = segmentPositions( context, scaleContext, settings );
QList<double> widths = segmentWidths( scaleContext, settings );

QPolygonF points;

for ( int i = 0; i < positions.size() + 1; ++i )
{
// we render one extra place, corresponding to the final position + width (i.e. the "end" of the bar)
double x = i < positions.size() ? context.convertToPainterUnits( positions.at( i ), QgsUnitTypes::RenderMillimeters ) + xOffset
: context.convertToPainterUnits( positions.at( i - 1 ), QgsUnitTypes::RenderMillimeters ) + xOffset + context.convertToPainterUnits( widths.at( i - 1 ), QgsUnitTypes::RenderMillimeters );
if ( i % 2 == 0 )
{
points << QPointF( x, barBottomPosition ) << QPointF( x, barTopPosition );
}
else
{
points << QPointF( x, barTopPosition ) << QPointF( x, barBottomPosition ) ;
}
}

sym->renderPolyline( points, nullptr, context );

painter->restore();

sym->stopRender( context );

//draw labels using the default method
drawDefaultLabels( context, settings, scaleContext );
}



50 changes: 50 additions & 0 deletions src/core/scalebar/qgssteppedlinescalebarrenderer.h
@@ -0,0 +1,50 @@
/***************************************************************************
qgssteppedlinescalebarrenderer.h
--------------------------------
begin : March 2020
copyright : (C) 2020 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSSTEPPEDLINESCALEBARRENDERER_H
#define QGSSTEPPEDLINESCALEBARRENDERER_H

#include "qgis_core.h"
#include "qgsscalebarrenderer.h"
#include <QString>

/**
* \class QgsSteppedLineScaleBarRenderer
* \ingroup core
* Scalebar style that draws a stepped line representation of a scalebar.
* \since QGIS 3.14
*/
class CORE_EXPORT QgsSteppedLineScaleBarRenderer: public QgsScaleBarRenderer
{
public:

/**
* Constructor for QgsSteppedLineScaleBarRenderer.
*/
QgsSteppedLineScaleBarRenderer() = default;

QString id() const override;
QString visibleName() const override;
int sortKey() const override;
QgsSteppedLineScaleBarRenderer* clone() const override SIP_FACTORY;

void draw( QgsRenderContext &context,
const QgsScaleBarSettings &settings,
const QgsScaleBarRenderer::ScaleBarContext &scaleContext ) const override;

};

#endif // QGSSTEPPEDLINESCALEBARRENDERER_H
5 changes: 3 additions & 2 deletions src/gui/layout/qgslayoutscalebarwidget.cpp
Expand Up @@ -464,8 +464,9 @@ void QgsLayoutScaleBarWidget::toggleStyleSpecificControls( const QString &style
mGroupBoxSegments->setEnabled( true );
mLabelBarSpaceSpinBox->setEnabled( true );
mLineStyleButton->setEnabled( true );
mFillSymbol1Button->setEnabled( true );
mFillSymbol2Button->setEnabled( true );
const bool hasFill = style == QLatin1String( "Double Box" ) || style == QLatin1String( "Single Box" ) ;
mFillSymbol1Button->setEnabled( hasFill );
mFillSymbol2Button->setEnabled( hasFill );
mLabelVerticalPlacementComboBox->setEnabled( true );
mLabelHorizontalPlacementComboBox->setEnabled( true );
mAlignmentComboBox->setEnabled( false );
Expand Down
45 changes: 45 additions & 0 deletions tests/src/core/testqgslayoutscalebar.cpp
Expand Up @@ -65,6 +65,7 @@ class TestQgsLayoutScaleBar : public QObject
void oldDataDefinedProject();
void textFormat();
void numericFormat();
void steppedLine();

private:
QString mReport;
Expand Down Expand Up @@ -731,6 +732,50 @@ void TestQgsLayoutScaleBar::numericFormat()
QVERIFY( checker.testLayout( mReport, 0, 0 ) );
}

void TestQgsLayoutScaleBar::steppedLine()
{
QgsLayout l( QgsProject::instance() );
l.initializeDefaults();
QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
map->attemptSetSceneRect( QRectF( 20, 20, 150, 150 ) );
map->setFrameEnabled( true );
l.addLayoutItem( map );
map->setExtent( QgsRectangle( 17.923, 30.160, 18.023, 30.260 ) );

QgsLayoutItemScaleBar *scalebar = new QgsLayoutItemScaleBar( &l );
scalebar->attemptSetSceneRect( QRectF( 20, 180, 50, 20 ) );
l.addLayoutItem( scalebar );
scalebar->setLinkedMap( map );
scalebar->setTextFormat( QgsTextFormat::fromQFont( QgsFontUtils::getStandardTestFont() ) );
scalebar->setUnits( QgsUnitTypes::DistanceMeters );
scalebar->setUnitsPerSegment( 2000 );
scalebar->setNumberOfSegmentsLeft( 2 );
scalebar->setNumberOfSegments( 2 );
scalebar->setHeight( 20 );

std::unique_ptr< QgsLineSymbol > lineSymbol = qgis::make_unique< QgsLineSymbol >();
std::unique_ptr< QgsSimpleLineSymbolLayer > lineSymbolLayer = qgis::make_unique< QgsSimpleLineSymbolLayer >();
lineSymbolLayer->setWidth( 4 );
lineSymbolLayer->setWidthUnit( QgsUnitTypes::RenderMillimeters );
lineSymbolLayer->setColor( QColor( 255, 0, 0 ) );
lineSymbol->changeSymbolLayer( 0, lineSymbolLayer.release() );

lineSymbolLayer = qgis::make_unique< QgsSimpleLineSymbolLayer >();
lineSymbolLayer->setWidth( 2 );
lineSymbolLayer->setWidthUnit( QgsUnitTypes::RenderMillimeters );
lineSymbolLayer->setColor( QColor( 255, 255, 0 ) );
lineSymbol->appendSymbolLayer( lineSymbolLayer.release() );

scalebar->setLineSymbol( lineSymbol.release() );

dynamic_cast< QgsBasicNumericFormat *>( const_cast< QgsNumericFormat * >( scalebar->numericFormat() ) )->setShowThousandsSeparator( false );

scalebar->setStyle( QStringLiteral( "stepped" ) );
QgsLayoutChecker checker( QStringLiteral( "layoutscalebar_stepped" ), &l );
checker.setControlPathPrefix( QStringLiteral( "layout_scalebar" ) );
QVERIFY( checker.testLayout( mReport, 0, 0 ) );
}


QGSTEST_MAIN( TestQgsLayoutScaleBar )
#include "testqgslayoutscalebar.moc"
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit e7cc830

Please sign in to comment.