diff --git a/python/PyQt6/core/auto_additions/qgis.py b/python/PyQt6/core/auto_additions/qgis.py index 0f59f8308705..4c61d8d0ae19 100644 --- a/python/PyQt6/core/auto_additions/qgis.py +++ b/python/PyQt6/core/auto_additions/qgis.py @@ -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 diff --git a/python/PyQt6/core/auto_generated/layout/qgslayoutitemscalebar.sip.in b/python/PyQt6/core/auto_generated/layout/qgslayoutitemscalebar.sip.in index 57ce4fcce5e3..ce1185869383 100644 --- a/python/PyQt6/core/auto_generated/layout/qgslayoutitemscalebar.sip.in +++ b/python/PyQt6/core/auto_generated/layout/qgslayoutitemscalebar.sip.in @@ -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(); diff --git a/python/PyQt6/core/auto_generated/qgis.sip.in b/python/PyQt6/core/auto_generated/qgis.sip.in index 4de51989d532..8bb6e3e76482 100644 --- a/python/PyQt6/core/auto_generated/qgis.sip.in +++ b/python/PyQt6/core/auto_generated/qgis.sip.in @@ -2775,6 +2775,13 @@ The development version Unknown, }; + enum class ScaleCalculationMethod /BaseType=IntEnum/ + { + HorizontalTop, + HorizontalMiddle, + HorizontalBottom, + HorizontalAverage, + }; enum class ScaleBarAlignment /BaseType=IntEnum/ { diff --git a/python/core/auto_additions/qgis.py b/python/core/auto_additions/qgis.py index db67ec7a24bb..90ff98b65432 100644 --- a/python/core/auto_additions/qgis.py +++ b/python/core/auto_additions/qgis.py @@ -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 diff --git a/python/core/auto_generated/layout/qgslayoutitemscalebar.sip.in b/python/core/auto_generated/layout/qgslayoutitemscalebar.sip.in index 57ce4fcce5e3..ce1185869383 100644 --- a/python/core/auto_generated/layout/qgslayoutitemscalebar.sip.in +++ b/python/core/auto_generated/layout/qgslayoutitemscalebar.sip.in @@ -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(); diff --git a/python/core/auto_generated/qgis.sip.in b/python/core/auto_generated/qgis.sip.in index 8d685aaa33f7..1276bd62b93a 100644 --- a/python/core/auto_generated/qgis.sip.in +++ b/python/core/auto_generated/qgis.sip.in @@ -2775,6 +2775,13 @@ The development version Unknown, }; + enum class ScaleCalculationMethod + { + HorizontalTop, + HorizontalMiddle, + HorizontalBottom, + HorizontalAverage, + }; enum class ScaleBarAlignment { diff --git a/src/core/layout/qgslayoutitemscalebar.cpp b/src/core/layout/qgslayoutitemscalebar.cpp index 257ad9d7a293..1b1203b8aa62 100644 --- a/src/core/layout/qgslayoutitemscalebar.cpp +++ b/src/core/layout/qgslayoutitemscalebar.cpp @@ -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 ) ) @@ -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; } } @@ -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 ); @@ -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() ) diff --git a/src/core/layout/qgslayoutitemscalebar.h b/src/core/layout/qgslayoutitemscalebar.h index 4db571c5dffc..23b963ba9804 100644 --- a/src/core/layout/qgslayoutitemscalebar.h +++ b/src/core/layout/qgslayoutitemscalebar.h @@ -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. */ @@ -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; diff --git a/src/core/qgis.h b/src/core/qgis.h index 14ea99d1f3d4..450adccc7c35 100644 --- a/src/core/qgis.h +++ b/src/core/qgis.h @@ -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. diff --git a/src/gui/layout/qgslayoutscalebarwidget.cpp b/src/gui/layout/qgslayoutscalebarwidget.cpp index 79bfb9e224c6..2d7ac1f81f48 100644 --- a/src/gui/layout/qgslayoutscalebarwidget.cpp +++ b/src/gui/layout/qgslayoutscalebarwidget.cpp @@ -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( &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 ); @@ -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 ); @@ -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 ); @@ -745,6 +766,7 @@ void QgsLayoutScaleBarWidget::blockMemberSignals( bool block ) mFontButton->blockSignals( block ); mMinWidthSpinBox->blockSignals( block ); mMaxWidthSpinBox->blockSignals( block ); + mMethodCombo->blockSignals( block ); } void QgsLayoutScaleBarWidget::connectUpdateSignal() diff --git a/src/ui/layout/qgslayoutscalebarwidgetbase.ui b/src/ui/layout/qgslayoutscalebarwidgetbase.ui index 368599c9b145..777224db63d1 100644 --- a/src/ui/layout/qgslayoutscalebarwidgetbase.ui +++ b/src/ui/layout/qgslayoutscalebarwidgetbase.ui @@ -62,14 +62,14 @@ 0 0 566 - 1018 + 1031 - Qt::StrongFocus + Qt::FocusPolicy::StrongFocus Main Properties @@ -122,7 +122,7 @@ - Qt::StrongFocus + Qt::FocusPolicy::StrongFocus Units @@ -134,29 +134,24 @@ false - - - - &Label for units - - - true - - - mUnitLabelLineEdit + + + + Text used for labeling the scalebar units, e.g., "m" or "km". This should be matched to reflect the multiplier above. - - + + - Label unit multiplier - - - false + Customize - - mMapUnitsPerBarUnitSpinBox + + + + + + Specifies the underlying units used for scalebar calculations, e.g., "meters" or "feet" @@ -171,18 +166,11 @@ 9999999999999.000000000000000 - + false - - - - Specifies the underlying units used for scalebar calculations, e.g., "meters" or "feet" - - - @@ -190,13 +178,6 @@ - - - - Text used for labeling the scalebar units, e.g., "m" or "km". This should be matched to reflect the multiplier above. - - - @@ -210,10 +191,39 @@ - - + + - Customize + Label unit multiplier + + + false + + + mMapUnitsPerBarUnitSpinBox + + + + + + + &Label for units + + + true + + + mUnitLabelLineEdit + + + + + + + + + + Method @@ -223,7 +233,7 @@ - Qt::StrongFocus + Qt::FocusPolicy::StrongFocus Segments @@ -505,7 +515,7 @@ 9999999999999.000000000000000 - + false @@ -526,7 +536,7 @@ - Qt::StrongFocus + Qt::FocusPolicy::StrongFocus Appearance @@ -782,6 +792,7 @@ mMapUnitsPerBarUnitSpinBox mUnitLabelLineEdit mNumberFormatPushButton + mMethodCombo mGroupBoxSegments mSegmentsLeftSpinBox mSegmentsLeftDDBtn diff --git a/tests/src/core/testqgslayoutscalebar.cpp b/tests/src/core/testqgslayoutscalebar.cpp index d0ebf7600d2e..ab7defc6d9d5 100644 --- a/tests/src/core/testqgslayoutscalebar.cpp +++ b/tests/src/core/testqgslayoutscalebar.cpp @@ -65,6 +65,7 @@ class TestQgsLayoutScaleBar : public QgsTest void hollow(); void hollowDefaults(); void tickSubdivisions(); + void methodTop(); }; void TestQgsLayoutScaleBar::initTestCase() @@ -906,6 +907,175 @@ void TestQgsLayoutScaleBar::tickSubdivisions() QGSVERIFYLAYOUTCHECK( QStringLiteral( "layoutscalebar_tick_subdivisions" ), &l ); } +void TestQgsLayoutScaleBar::methodTop() +{ + QgsLayout l( QgsProject::instance() ); + l.initializeDefaults(); + QgsLayoutItemMap *map1 = new QgsLayoutItemMap( &l ); + map1->attemptSetSceneRect( QRectF( 20, 20, 150, 150 ) ); + map1->setFrameEnabled( false ); + map1->setVisibility( false ); + l.addLayoutItem( map1 ); + // only scale at center of map can be calculated + map1->setExtent( QgsRectangle( -100, -100, 100, 100 ) ); + map1->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) ); + + QgsLayoutItemScaleBar *scalebar1 = new QgsLayoutItemScaleBar( &l ); + scalebar1->attemptSetSceneRect( QRectF( 20, 10, 50, 20 ) ); + l.addLayoutItem( scalebar1 ); + scalebar1->setLinkedMap( map1 ); + scalebar1->setTextFormat( QgsTextFormat::fromQFont( QgsFontUtils::getStandardTestFont() ) ); + scalebar1->setUnits( Qgis::DistanceUnit::Kilometers ); + scalebar1->setUnitsPerSegment( 10000 ); + scalebar1->setNumberOfSegmentsLeft( 0 ); + scalebar1->setNumberOfSegments( 2 ); + scalebar1->setHeight( 5 ); + scalebar1->setSubdivisionsHeight( 25 ); //ensure subdivisionsHeight is non used in non tick-style scalebars + scalebar1->setMethod( Qgis::ScaleCalculationMethod::HorizontalMiddle ); + Q_NOWARN_DEPRECATED_PUSH + scalebar1->setLineWidth( 1.0 ); + Q_NOWARN_DEPRECATED_POP + qgis::down_cast< QgsBasicNumericFormat *>( const_cast< QgsNumericFormat * >( scalebar1->numericFormat() ) )->setShowThousandsSeparator( false ); + scalebar1->setStyle( QStringLiteral( "Single Box" ) ); + + QgsLayoutItemScaleBar *scalebar1A = new QgsLayoutItemScaleBar( &l ); + scalebar1A->attemptSetSceneRect( QRectF( 20, 30, 50, 20 ) ); + l.addLayoutItem( scalebar1A ); + scalebar1A->setLinkedMap( map1 ); + scalebar1A->setTextFormat( QgsTextFormat::fromQFont( QgsFontUtils::getStandardTestFont() ) ); + scalebar1A->setUnits( Qgis::DistanceUnit::Kilometers ); + scalebar1A->setUnitsPerSegment( 10000 ); + scalebar1A->setNumberOfSegmentsLeft( 0 ); + scalebar1A->setNumberOfSegments( 2 ); + scalebar1A->setHeight( 5 ); + scalebar1A->setSubdivisionsHeight( 25 ); //ensure subdivisionsHeight is non used in non tick-style scalebars + scalebar1A->setMethod( Qgis::ScaleCalculationMethod::HorizontalAverage ); + Q_NOWARN_DEPRECATED_PUSH + scalebar1A->setLineWidth( 1.0 ); + Q_NOWARN_DEPRECATED_POP + qgis::down_cast< QgsBasicNumericFormat *>( const_cast< QgsNumericFormat * >( scalebar1A->numericFormat() ) )->setShowThousandsSeparator( false ); + scalebar1A->setStyle( QStringLiteral( "Single Box" ) ); + + QgsLayoutItemMap *map2 = new QgsLayoutItemMap( &l ); + map2->attemptSetSceneRect( QRectF( 20, 20, 150, 150 ) ); + map2->setFrameEnabled( false ); + map2->setVisibility( false ); + l.addLayoutItem( map2 ); + // only scale at top of map can be calculated + map2->setExtent( QgsRectangle( -100, -280, 100, -80 ) ); + map2->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) ); + + QgsLayoutItemScaleBar *scalebar2 = new QgsLayoutItemScaleBar( &l ); + scalebar2->attemptSetSceneRect( QRectF( 20, 50, 50, 20 ) ); + l.addLayoutItem( scalebar2 ); + scalebar2->setLinkedMap( map2 ); + scalebar2->setTextFormat( QgsTextFormat::fromQFont( QgsFontUtils::getStandardTestFont() ) ); + scalebar2->setUnits( Qgis::DistanceUnit::Kilometers ); + scalebar2->setUnitsPerSegment( 1000 ); + scalebar2->setNumberOfSegmentsLeft( 0 ); + scalebar2->setNumberOfSegments( 2 ); + scalebar2->setHeight( 5 ); + scalebar2->setSubdivisionsHeight( 25 ); //ensure subdivisionsHeight is non used in non tick-style scalebars + scalebar2->setMethod( Qgis::ScaleCalculationMethod::HorizontalTop ); + Q_NOWARN_DEPRECATED_PUSH + scalebar2->setLineWidth( 1.0 ); + Q_NOWARN_DEPRECATED_POP + qgis::down_cast< QgsBasicNumericFormat *>( const_cast< QgsNumericFormat * >( scalebar2->numericFormat() ) )->setShowThousandsSeparator( false ); + scalebar2->setStyle( QStringLiteral( "Single Box" ) ); + + QgsLayoutItemScaleBar *scalebar2A = new QgsLayoutItemScaleBar( &l ); + scalebar2A->attemptSetSceneRect( QRectF( 20, 70, 50, 20 ) ); + l.addLayoutItem( scalebar2A ); + scalebar2A->setLinkedMap( map2 ); + scalebar2A->setTextFormat( QgsTextFormat::fromQFont( QgsFontUtils::getStandardTestFont() ) ); + scalebar2A->setUnits( Qgis::DistanceUnit::Kilometers ); + scalebar2A->setUnitsPerSegment( 1000 ); + scalebar2A->setNumberOfSegmentsLeft( 0 ); + scalebar2A->setNumberOfSegments( 2 ); + scalebar2A->setHeight( 5 ); + scalebar2A->setSubdivisionsHeight( 25 ); //ensure subdivisionsHeight is non used in non tick-style scalebars + scalebar2A->setMethod( Qgis::ScaleCalculationMethod::HorizontalAverage ); + Q_NOWARN_DEPRECATED_PUSH + scalebar2A->setLineWidth( 1.0 ); + Q_NOWARN_DEPRECATED_POP + qgis::down_cast< QgsBasicNumericFormat *>( const_cast< QgsNumericFormat * >( scalebar2A->numericFormat() ) )->setShowThousandsSeparator( false ); + scalebar2A->setStyle( QStringLiteral( "Single Box" ) ); + + QgsLayoutItemMap *map3 = new QgsLayoutItemMap( &l ); + map3->attemptSetSceneRect( QRectF( 20, 90, 150, 150 ) ); + map3->setFrameEnabled( false ); + map3->setVisibility( false ); + l.addLayoutItem( map3 ); + // only scale at bottom of map can be calculated + map3->setExtent( QgsRectangle( -100, 80, 100, 280 ) ); + map3->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) ); + + QgsLayoutItemScaleBar *scalebar3 = new QgsLayoutItemScaleBar( &l ); + scalebar3->attemptSetSceneRect( QRectF( 20, 90, 50, 20 ) ); + l.addLayoutItem( scalebar3 ); + scalebar3->setLinkedMap( map3 ); + scalebar3->setTextFormat( QgsTextFormat::fromQFont( QgsFontUtils::getStandardTestFont() ) ); + scalebar3->setUnits( Qgis::DistanceUnit::Kilometers ); + scalebar3->setUnitsPerSegment( 1000 ); + scalebar3->setNumberOfSegmentsLeft( 0 ); + scalebar3->setNumberOfSegments( 2 ); + scalebar3->setHeight( 5 ); + scalebar3->setSubdivisionsHeight( 25 ); //ensure subdivisionsHeight is non used in non tick-style scalebars + scalebar3->setMethod( Qgis::ScaleCalculationMethod::HorizontalBottom ); + Q_NOWARN_DEPRECATED_PUSH + scalebar3->setLineWidth( 1.0 ); + Q_NOWARN_DEPRECATED_POP + qgis::down_cast< QgsBasicNumericFormat *>( const_cast< QgsNumericFormat * >( scalebar3->numericFormat() ) )->setShowThousandsSeparator( false ); + scalebar3->setStyle( QStringLiteral( "Single Box" ) ); + + QgsLayoutItemScaleBar *scalebar3A = new QgsLayoutItemScaleBar( &l ); + scalebar3A->attemptSetSceneRect( QRectF( 20, 110, 50, 20 ) ); + l.addLayoutItem( scalebar3A ); + scalebar3A->setLinkedMap( map3 ); + scalebar3A->setTextFormat( QgsTextFormat::fromQFont( QgsFontUtils::getStandardTestFont() ) ); + scalebar3A->setUnits( Qgis::DistanceUnit::Kilometers ); + scalebar3A->setUnitsPerSegment( 1000 ); + scalebar3A->setNumberOfSegmentsLeft( 0 ); + scalebar3A->setNumberOfSegments( 2 ); + scalebar3A->setHeight( 5 ); + scalebar3A->setSubdivisionsHeight( 25 ); //ensure subdivisionsHeight is non used in non tick-style scalebars + scalebar3A->setMethod( Qgis::ScaleCalculationMethod::HorizontalAverage ); + Q_NOWARN_DEPRECATED_PUSH + scalebar3A->setLineWidth( 1.0 ); + Q_NOWARN_DEPRECATED_POP + qgis::down_cast< QgsBasicNumericFormat *>( const_cast< QgsNumericFormat * >( scalebar3A->numericFormat() ) )->setShowThousandsSeparator( false ); + scalebar3A->setStyle( QStringLiteral( "Single Box" ) ); + + QgsLayoutItemMap *map4 = new QgsLayoutItemMap( &l ); + map4->attemptSetSceneRect( QRectF( 20, 90, 150, 150 ) ); + map4->setFrameEnabled( false ); + map4->setVisibility( false ); + l.addLayoutItem( map4 ); + // scale is valid everywhere + map4->setExtent( QgsRectangle( -80, -80, 80, 80 ) ); + map4->setCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) ); + + QgsLayoutItemScaleBar *scalebar4 = new QgsLayoutItemScaleBar( &l ); + scalebar4->attemptSetSceneRect( QRectF( 20, 130, 50, 20 ) ); + l.addLayoutItem( scalebar4 ); + scalebar4->setLinkedMap( map4 ); + scalebar4->setTextFormat( QgsTextFormat::fromQFont( QgsFontUtils::getStandardTestFont() ) ); + scalebar4->setUnits( Qgis::DistanceUnit::Kilometers ); + scalebar4->setUnitsPerSegment( 5000 ); + scalebar4->setNumberOfSegmentsLeft( 0 ); + scalebar4->setNumberOfSegments( 2 ); + scalebar4->setHeight( 5 ); + scalebar4->setSubdivisionsHeight( 25 ); //ensure subdivisionsHeight is non used in non tick-style scalebars + scalebar4->setMethod( Qgis::ScaleCalculationMethod::HorizontalAverage ); + Q_NOWARN_DEPRECATED_PUSH + scalebar4->setLineWidth( 1.0 ); + Q_NOWARN_DEPRECATED_POP + qgis::down_cast< QgsBasicNumericFormat *>( const_cast< QgsNumericFormat * >( scalebar4->numericFormat() ) )->setShowThousandsSeparator( false ); + scalebar4->setStyle( QStringLiteral( "Single Box" ) ); + + QGSVERIFYLAYOUTCHECK( QStringLiteral( "scalebar_method" ), &l ); +} + QGSTEST_MAIN( TestQgsLayoutScaleBar ) #include "testqgslayoutscalebar.moc" diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_datadefined/expected_layoutscalebar_datadefined_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_datadefined/expected_layoutscalebar_datadefined_mask.png index 93566aa2206c..4990cd291320 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_datadefined/expected_layoutscalebar_datadefined_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_datadefined/expected_layoutscalebar_datadefined_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox/expected_layoutscalebar_doublebox_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox/expected_layoutscalebar_doublebox_mask.png index 59c95b79e55a..65c7ef15b491 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox/expected_layoutscalebar_doublebox_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox/expected_layoutscalebar_doublebox_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_fillsymbol/expected_layoutscalebar_doublebox_fillsymbol_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_fillsymbol/expected_layoutscalebar_doublebox_fillsymbol_mask.png index 918f31d2ca38..4cb6b6150681 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_fillsymbol/expected_layoutscalebar_doublebox_fillsymbol_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_fillsymbol/expected_layoutscalebar_doublebox_fillsymbol_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_labelcenteredsegment/expected_layoutscalebar_doublebox_labelcenteredsegment_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_labelcenteredsegment/expected_layoutscalebar_doublebox_labelcenteredsegment_mask.png index 4952ca2ef441..0f04a2b39b68 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_labelcenteredsegment/expected_layoutscalebar_doublebox_labelcenteredsegment_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_labelcenteredsegment/expected_layoutscalebar_doublebox_labelcenteredsegment_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_linesymbol/expected_layoutscalebar_doublebox_linesymbol_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_linesymbol/expected_layoutscalebar_doublebox_linesymbol_mask.png index 020e4122d5fc..871eb9909dfe 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_linesymbol/expected_layoutscalebar_doublebox_linesymbol_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_linesymbol/expected_layoutscalebar_doublebox_linesymbol_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_hollow/expected_layoutscalebar_hollow_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_hollow/expected_layoutscalebar_hollow_mask.png index 9830e5ac2318..93cce6ee89f3 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_hollow/expected_layoutscalebar_hollow_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_hollow/expected_layoutscalebar_hollow_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_numericformat/expected_layoutscalebar_numericformat_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_numericformat/expected_layoutscalebar_numericformat_mask.png index 9d1bfac59154..053705a4c2ca 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_numericformat/expected_layoutscalebar_numericformat_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_numericformat/expected_layoutscalebar_numericformat_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox/expected_layoutscalebar_singlebox_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox/expected_layoutscalebar_singlebox_mask.png index b41467505df9..81b3871059f2 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox/expected_layoutscalebar_singlebox_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox/expected_layoutscalebar_singlebox_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_alpha/expected_layoutscalebar_singlebox_alpha_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_alpha/expected_layoutscalebar_singlebox_alpha_mask.png index 73a53ee95fdf..1d06663375b8 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_alpha/expected_layoutscalebar_singlebox_alpha_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_alpha/expected_layoutscalebar_singlebox_alpha_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_fillsymbol/expected_layoutscalebar_singlebox_fillsymbol_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_fillsymbol/expected_layoutscalebar_singlebox_fillsymbol_mask.png index 6486ff12525b..a2422396dae5 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_fillsymbol/expected_layoutscalebar_singlebox_fillsymbol_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_fillsymbol/expected_layoutscalebar_singlebox_fillsymbol_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_labelbelowsegment/expected_layoutscalebar_singlebox_labelbelowsegment_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_labelbelowsegment/expected_layoutscalebar_singlebox_labelbelowsegment_mask.png index 6ded1142fcef..b0ee38892686 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_labelbelowsegment/expected_layoutscalebar_singlebox_labelbelowsegment_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_labelbelowsegment/expected_layoutscalebar_singlebox_labelbelowsegment_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_linesymbol/expected_layoutscalebar_singlebox_linesymbol_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_linesymbol/expected_layoutscalebar_singlebox_linesymbol_mask.png index 2ad1d477ba9d..a5142a95b3c6 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_linesymbol/expected_layoutscalebar_singlebox_linesymbol_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_linesymbol/expected_layoutscalebar_singlebox_linesymbol_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_stepped/expected_layoutscalebar_stepped_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_stepped/expected_layoutscalebar_stepped_mask.png index 8487446b97bc..fcfc6e1e2a98 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_stepped/expected_layoutscalebar_stepped_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_stepped/expected_layoutscalebar_stepped_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_textformat/expected_layoutscalebar_textformat_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_textformat/expected_layoutscalebar_textformat_mask.png index 30ce3c2b1371..6b1414a083b5 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_textformat/expected_layoutscalebar_textformat_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_textformat/expected_layoutscalebar_textformat_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick/expected_layoutscalebar_tick_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick/expected_layoutscalebar_tick_mask.png index bd930ff0fce3..24d80fb3ea11 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick/expected_layoutscalebar_tick_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick/expected_layoutscalebar_tick_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick_linesymbol/expected_layoutscalebar_tick_linesymbol_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick_linesymbol/expected_layoutscalebar_tick_linesymbol_mask.png index c7706f9efee3..065c5fa514df 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick_linesymbol/expected_layoutscalebar_tick_linesymbol_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick_linesymbol/expected_layoutscalebar_tick_linesymbol_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick_subdivisions/expected_layoutscalebar_tick_subdivisions_mask.png b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick_subdivisions/expected_layoutscalebar_tick_subdivisions_mask.png index 21268c2e5921..cf51ccd26f01 100644 Binary files a/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick_subdivisions/expected_layoutscalebar_tick_subdivisions_mask.png and b/tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick_subdivisions/expected_layoutscalebar_tick_subdivisions_mask.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_scalebar_method/expected_scalebar_method.png b/tests/testdata/control_images/layout_scalebar/expected_scalebar_method/expected_scalebar_method.png new file mode 100644 index 000000000000..7f20b9695d41 Binary files /dev/null and b/tests/testdata/control_images/layout_scalebar/expected_scalebar_method/expected_scalebar_method.png differ diff --git a/tests/testdata/control_images/layout_scalebar/expected_scalebar_method/expected_scalebar_method_mask.png b/tests/testdata/control_images/layout_scalebar/expected_scalebar_method/expected_scalebar_method_mask.png new file mode 100644 index 000000000000..47e44097ed55 Binary files /dev/null and b/tests/testdata/control_images/layout_scalebar/expected_scalebar_method/expected_scalebar_method_mask.png differ