Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scale method option for layout scale bars #58904

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions python/PyQt6/core/auto_additions/qgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -9662,6 +9662,23 @@
"""
# --
Qgis.PictureFormat.baseClass = Qgis
# monkey patching scoped based enum
Qgis.ScaleCalculationMethod.HorizontalTop.__doc__ = "Calculate horizontally, across top of map"
Qgis.ScaleCalculationMethod.HorizontalMiddle.__doc__ = "Calculate horizontally, across midle of map"
Qgis.ScaleCalculationMethod.HorizontalBottom.__doc__ = "Calculate horizontally, across bottom of map"
Qgis.ScaleCalculationMethod.HorizontalAverage.__doc__ = "Calculate horizontally, using the average of the top, middle and bottom scales"
Qgis.ScaleCalculationMethod.__doc__ = """Scale calculation logic.

.. versionadded:: 3.40

* ``HorizontalTop``: Calculate horizontally, across top of map
* ``HorizontalMiddle``: Calculate horizontally, across midle of map
* ``HorizontalBottom``: Calculate horizontally, across bottom of map
* ``HorizontalAverage``: Calculate horizontally, using the average of the top, middle and bottom scales

"""
# --
Qgis.ScaleCalculationMethod.baseClass = Qgis
QgsScaleBarSettings.Alignment = Qgis.ScaleBarAlignment
# monkey patching scoped based enum
QgsScaleBarSettings.AlignLeft = Qgis.ScaleBarAlignment.Left
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,24 @@ Ownership of ``format`` is transferred to the scalebar.
.. seealso:: :py:func:`numericFormat`

.. versionadded:: 3.12
%End

Qgis::ScaleCalculationMethod method() const;
%Docstring
Returns the scale calculation method, which determines how the bar's scale will be calculated.

.. seealso:: :py:func:`setMethod`

.. versionadded:: 3.40
%End

void setMethod( Qgis::ScaleCalculationMethod method );
%Docstring
Sets the scale calculation ``method``, which determines how the bar's scale will be calculated.

.. seealso:: :py:func:`method`

.. versionadded:: 3.40
%End

void update();
Expand Down
7 changes: 7 additions & 0 deletions python/PyQt6/core/auto_generated/qgis.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -2775,6 +2775,13 @@ The development version
Unknown,
};

enum class ScaleCalculationMethod /BaseType=IntEnum/
{
HorizontalTop,
HorizontalMiddle,
HorizontalBottom,
HorizontalAverage,
};

enum class ScaleBarAlignment /BaseType=IntEnum/
{
Expand Down
17 changes: 17 additions & 0 deletions python/core/auto_additions/qgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -9586,6 +9586,23 @@
"""
# --
Qgis.PictureFormat.baseClass = Qgis
# monkey patching scoped based enum
Qgis.ScaleCalculationMethod.HorizontalTop.__doc__ = "Calculate horizontally, across top of map"
Qgis.ScaleCalculationMethod.HorizontalMiddle.__doc__ = "Calculate horizontally, across midle of map"
Qgis.ScaleCalculationMethod.HorizontalBottom.__doc__ = "Calculate horizontally, across bottom of map"
Qgis.ScaleCalculationMethod.HorizontalAverage.__doc__ = "Calculate horizontally, using the average of the top, middle and bottom scales"
Qgis.ScaleCalculationMethod.__doc__ = """Scale calculation logic.

.. versionadded:: 3.40

* ``HorizontalTop``: Calculate horizontally, across top of map
* ``HorizontalMiddle``: Calculate horizontally, across midle of map
* ``HorizontalBottom``: Calculate horizontally, across bottom of map
* ``HorizontalAverage``: Calculate horizontally, using the average of the top, middle and bottom scales

"""
# --
Qgis.ScaleCalculationMethod.baseClass = Qgis
QgsScaleBarSettings.Alignment = Qgis.ScaleBarAlignment
# monkey patching scoped based enum
QgsScaleBarSettings.AlignLeft = Qgis.ScaleBarAlignment.Left
Expand Down
18 changes: 18 additions & 0 deletions python/core/auto_generated/layout/qgslayoutitemscalebar.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,24 @@ Ownership of ``format`` is transferred to the scalebar.
.. seealso:: :py:func:`numericFormat`

.. versionadded:: 3.12
%End

Qgis::ScaleCalculationMethod method() const;
%Docstring
Returns the scale calculation method, which determines how the bar's scale will be calculated.

.. seealso:: :py:func:`setMethod`

.. versionadded:: 3.40
%End

void setMethod( Qgis::ScaleCalculationMethod method );
%Docstring
Sets the scale calculation ``method``, which determines how the bar's scale will be calculated.

.. seealso:: :py:func:`method`

.. versionadded:: 3.40
%End

void update();
Expand Down
7 changes: 7 additions & 0 deletions python/core/auto_generated/qgis.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -2775,6 +2775,13 @@ The development version
Unknown,
};

enum class ScaleCalculationMethod
{
HorizontalTop,
HorizontalMiddle,
HorizontalBottom,
HorizontalAverage,
};

enum class ScaleBarAlignment
{
Expand Down
80 changes: 71 additions & 9 deletions src/core/layout/qgslayoutitemscalebar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,21 @@ void QgsLayoutItemScaleBar::disconnectCurrentMap()
mMap = nullptr;
}

Qgis::ScaleCalculationMethod QgsLayoutItemScaleBar::method() const
{
return mMethod;
}

void QgsLayoutItemScaleBar::setMethod( Qgis::ScaleCalculationMethod method )
{
if ( mMethod == method )
return;

mMethod = method;
refreshSegmentMillimeters();
resizeToMinimumWidth();
}

void QgsLayoutItemScaleBar::refreshUnitsPerSegment( const QgsExpressionContext *context )
{
if ( mDataDefinedProperties.isActive( QgsLayoutObject::DataDefinedProperty::ScalebarSegmentWidth ) )
Expand Down Expand Up @@ -570,19 +585,62 @@ double QgsLayoutItemScaleBar::mapWidth() const
da.setEllipsoid( mLayout->project()->ellipsoid() );

const Qgis::DistanceUnit units = da.lengthUnits();
double measure = 0;
try

QList< double > yValues;
switch ( mMethod )
{
measure = da.measureLine( QgsPointXY( mapExtent.xMinimum(), mapExtent.yMinimum() ),
QgsPointXY( mapExtent.xMaximum(), mapExtent.yMinimum() ) );
measure /= QgsUnitTypes::fromUnitToUnitFactor( mSettings.units(), units );
case Qgis::ScaleCalculationMethod::HorizontalTop:
yValues << mapExtent.yMaximum();
break;

case Qgis::ScaleCalculationMethod::HorizontalMiddle:
yValues << 0.5 * ( mapExtent.yMaximum() + mapExtent.yMinimum() );
break;


case Qgis::ScaleCalculationMethod::HorizontalBottom:
yValues << mapExtent.yMinimum();
break;

case Qgis::ScaleCalculationMethod::HorizontalAverage:
yValues << mapExtent.yMaximum();
yValues << 0.5 * ( mapExtent.yMaximum() + mapExtent.yMinimum() );
yValues << mapExtent.yMinimum();
break;
}
catch ( QgsCsException & )

double sumValidMeasures = 0;
int validMeasureCount = 0;

for ( const double y : std::as_const( yValues ) )
{
// TODO report errors to user
QgsDebugError( QStringLiteral( "An error occurred while calculating length" ) );
try
{
double measure = da.measureLine( QgsPointXY( mapExtent.xMinimum(), y ),
QgsPointXY( mapExtent.xMaximum(), y ) );
if ( std::isnan( measure ) )
{
// TODO report errors to user
QgsDebugError( QStringLiteral( "An error occurred while calculating length" ) );
continue;
}

measure /= QgsUnitTypes::fromUnitToUnitFactor( mSettings.units(), units );
sumValidMeasures += measure;
validMeasureCount++;
}
catch ( QgsCsException & )
{
// TODO report errors to user
QgsDebugError( QStringLiteral( "An error occurred while calculating length" ) );
continue;
}
}
return measure;

if ( validMeasureCount == 0 )
return std::numeric_limits< double >::quiet_NaN();

return sumValidMeasures / validMeasureCount;
}
}

Expand Down Expand Up @@ -965,6 +1023,7 @@ bool QgsLayoutItemScaleBar::writePropertiesToElement( QDomElement &composerScale
composerScaleBarElem.setAttribute( QStringLiteral( "maxBarWidth" ), mSettings.maximumBarWidth() );
composerScaleBarElem.setAttribute( QStringLiteral( "segmentMillimeters" ), QString::number( mSegmentMillimeters ) );
composerScaleBarElem.setAttribute( QStringLiteral( "numMapUnitsPerScaleBarUnit" ), QString::number( mSettings.mapUnitsPerScaleBarUnit() ) );
composerScaleBarElem.setAttribute( QStringLiteral( "method" ), qgsEnumValueToKey( mMethod ) );

const QDomElement textElem = mSettings.textFormat().writeXml( doc, rwContext );
composerScaleBarElem.appendChild( textElem );
Expand Down Expand Up @@ -1092,6 +1151,9 @@ bool QgsLayoutItemScaleBar::readPropertiesFromElement( const QDomElement &itemEl
mSegmentMillimeters = itemElem.attribute( QStringLiteral( "segmentMillimeters" ), QStringLiteral( "0.0" ) ).toDouble();
mSettings.setMapUnitsPerScaleBarUnit( itemElem.attribute( QStringLiteral( "numMapUnitsPerScaleBarUnit" ), QStringLiteral( "1.0" ) ).toDouble() );

// default to horizontal bottom to keep same behavior for older projects
mMethod = qgsEnumKeyToValue( itemElem.attribute( QStringLiteral( "method" ) ), Qgis::ScaleCalculationMethod::HorizontalBottom );

const QDomElement lineSymbolElem = itemElem.firstChildElement( QStringLiteral( "lineSymbol" ) );
bool foundLineSymbol = false;
if ( !lineSymbolElem.isNull() )
Expand Down
17 changes: 17 additions & 0 deletions src/core/layout/qgslayoutitemscalebar.h
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,22 @@ class CORE_EXPORT QgsLayoutItemScaleBar: public QgsLayoutItem
*/
void setNumericFormat( QgsNumericFormat *format SIP_TRANSFER );

/**
* Returns the scale calculation method, which determines how the bar's scale will be calculated.
*
* \see setMethod()
* \since QGIS 3.40
*/
Qgis::ScaleCalculationMethod method() const;

/**
* Sets the scale calculation \a method, which determines how the bar's scale will be calculated.
*
* \see method()
* \since QGIS 3.40
*/
void setMethod( Qgis::ScaleCalculationMethod method );

/**
* Adjusts the scale bar box size and updates the item.
*/
Expand Down Expand Up @@ -675,6 +691,7 @@ class CORE_EXPORT QgsLayoutItemScaleBar: public QgsLayoutItem
QString mMapUuid;

QgsScaleBarSettings mSettings;
Qgis::ScaleCalculationMethod mMethod = Qgis::ScaleCalculationMethod::HorizontalAverage;

//! Scalebar style
std::unique_ptr< QgsScaleBarRenderer > mStyle;
Expand Down
13 changes: 13 additions & 0 deletions src/core/qgis.h
Original file line number Diff line number Diff line change
Expand Up @@ -4875,6 +4875,19 @@ class CORE_EXPORT Qgis
};
Q_ENUM( PictureFormat )

/**
* Scale calculation logic.
*
* \since QGIS 3.40
*/
enum class ScaleCalculationMethod : int
{
HorizontalTop = 0, //!< Calculate horizontally, across top of map
HorizontalMiddle, //!< Calculate horizontally, across midle of map
HorizontalBottom, //!< Calculate horizontally, across bottom of map
HorizontalAverage, //!< Calculate horizontally, using the average of the top, middle and bottom scales
};
Q_ENUM( ScaleCalculationMethod )

/**
* Scalebar alignment.
Expand Down
22 changes: 22 additions & 0 deletions src/gui/layout/qgslayoutscalebarwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ QgsLayoutScaleBarWidget::QgsLayoutScaleBarWidget( QgsLayoutItemScaleBar *scaleBa
connect( mMinWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutScaleBarWidget::mMinWidthSpinBox_valueChanged );
connect( mMaxWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutScaleBarWidget::mMaxWidthSpinBox_valueChanged );
connect( mNumberFormatPushButton, &QPushButton::clicked, this, &QgsLayoutScaleBarWidget::changeNumberFormat );
connect( mMethodCombo, qOverload<int>( &QComboBox::currentIndexChanged ), this, [ = ]
{
if ( !mScalebar )
{
return;
}

disconnectUpdateSignal();
mScalebar->beginCommand( tr( "Set Scalebar Method" ) );
mScalebar->setMethod( mMethodCombo->currentData().value< Qgis::ScaleCalculationMethod >() );
mScalebar->update();
connectUpdateSignal();
mScalebar->endCommand();
} );

registerDataDefinedButton( mSegmentsLeftDDBtn, QgsLayoutObject::DataDefinedProperty::ScalebarLeftSegments );
registerDataDefinedButton( mSegmentsRightDDBtn, QgsLayoutObject::DataDefinedProperty::ScalebarRightSegments );
Expand Down Expand Up @@ -115,6 +129,11 @@ QgsLayoutScaleBarWidget::QgsLayoutScaleBarWidget( QgsLayoutItemScaleBar *scaleBa
mUnitsComboBox->addItem( tr( "Millimeters" ), static_cast< int >( Qgis::DistanceUnit::Millimeters ) );
mUnitsComboBox->addItem( tr( "Inches" ), static_cast< int >( Qgis::DistanceUnit::Inches ) );

mMethodCombo->addItem( tr( "Average Top, Middle and Bottom Scales" ), QVariant::fromValue( Qgis::ScaleCalculationMethod::HorizontalAverage ) );
mMethodCombo->addItem( tr( "Calculate along Top of Map" ), QVariant::fromValue( Qgis::ScaleCalculationMethod::HorizontalTop ) );
mMethodCombo->addItem( tr( "Calculate along Middle of Map" ), QVariant::fromValue( Qgis::ScaleCalculationMethod::HorizontalMiddle ) );
mMethodCombo->addItem( tr( "Calculate along Bottom of Map" ), QVariant::fromValue( Qgis::ScaleCalculationMethod::HorizontalBottom ) );

mLineStyleButton->setSymbolType( Qgis::SymbolType::Line );
connect( mLineStyleButton, &QgsSymbolButton::changed, this, &QgsLayoutScaleBarWidget::lineSymbolChanged );

Expand Down Expand Up @@ -349,6 +368,8 @@ void QgsLayoutScaleBarWidget::setGuiElements()
mMinWidthSpinBox->setValue( mScalebar->minimumBarWidth() );
mMaxWidthSpinBox->setValue( mScalebar->maximumBarWidth() );

mMethodCombo->setCurrentIndex( mMethodCombo->findData( QVariant::fromValue( mScalebar->method() ) ) );

populateDataDefinedButtons();

blockMemberSignals( false );
Expand Down Expand Up @@ -745,6 +766,7 @@ void QgsLayoutScaleBarWidget::blockMemberSignals( bool block )
mFontButton->blockSignals( block );
mMinWidthSpinBox->blockSignals( block );
mMaxWidthSpinBox->blockSignals( block );
mMethodCombo->blockSignals( block );
}

void QgsLayoutScaleBarWidget::connectUpdateSignal()
Expand Down
Loading
Loading