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

Fix layout corruption because of bad scalebar calculations #58677

Closed
wants to merge 4 commits into from

Conversation

nyalldawson
Copy link
Collaborator

Avoids print layouts getting corrupted because of scalebars where the scale can't be calculated, which result in a massive 99999999999 cm wide scalebar object which effectively destroys a layout (page size calculation goes wonky, item placement breaks, etc).

This involves a few parts:

  1. QgsDistanceArea methods now raise QgsCsException when errors occur. Previously, they just swallowed the error and returned an incorrect area/length of 0, and there was no way to detect that the measurement was bad. While this could be considered an API break, I think it's the safest approach. We could alternatively return NaN or a negative value, but those changes are likely to result in broken plugin logic and corrupted measurements too. I think we're best with explicitly raising an error and requiring the plugin dev/user to handle this situation appropriately.
  2. When a scalebar scale cannot be calculated (eg because of transform errors), show a warning over the scalebar in the layout designer:
    image
  3. Rework layout scale calculation logic. Instead of only calculating the scale at the bottom of the linked map, calculate at the top, middle and bottom and average the results. Measuring along the bottom of the map alone was rather arbitrary, and we get a slightly more representative scale of the map via the averaging approach.

In future I would like to extend (3), and show an explicit warning when the calculated scale across the top/middle/bottom of the map differs by more than some threshold (eg 5%), advising the user that a scalebar is an inappropriate choice for their map and that a graticule should be used instead...

Eg when trying to show a 1m scalebar for a global map
Instead of just silently return "0", which is misleading and
can result in data corruption/incorrect analysis results. Better
to be safe and force callers to handle transformation errors
appropriately.

In this case we:

- raise QgsProcessingExceptions when the failed measurement is coming
from a processing tool, so that the user is forced to deal with the
issue and we aren't providing meaningless/misleading measurements
- report evaluation errors if the measurement is coming from a
QGIS expression, so the user must appropriately handle the situation
- for all other cases we currently just write a console error and
maintain the current behavior of treating the measurement length
as 0. TODO notes have been added to handle this cases better.
Instead of rendering a corrupted zero width scale bar
and creating a layout item with 99999999999999mm width, which
breaks the layout.
Instead of only calculating the scale at the bottom of the linked
map, calculate at the top, middle and bottom and average the results
@github-actions github-actions bot added this to the 3.40.0 milestone Sep 12, 2024
// measure across top, center, and bottom of map, and return average of these calculated lengths
int validMeasureCount = 0;
double sumValidMeasures = 0;
for ( const double y : { mapExtent.yMinimum(), mapExtent.yMaximum(), ( mapExtent.yMaximum() - mapExtent.yMinimum() ) * 0.5 } )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for ( const double y : { mapExtent.yMinimum(), mapExtent.yMaximum(), ( mapExtent.yMaximum() - mapExtent.yMinimum() ) * 0.5 } )
for ( const double y : { mapExtent.yMinimum(), mapExtent.yMaximum(), ( mapExtent.yMaximum() + mapExtent.yMinimum() ) * 0.5 } )

Shouldn't it be ( mapExtent.yMaximum() + mapExtent.yMinimum() ) * 0.5?

@agiudiceandrea
Copy link
Contributor

agiudiceandrea commented Sep 12, 2024

3. Rework layout scale calculation logic. Instead of only calculating the scale at the bottom of the linked map, calculate at the top, middle and bottom and average the results.

Wouldn't it be better to make the new calculation logic optional, in order to avoid to break previous layout? Well aware map makers may have also specified in the layout: "scale calculated at the bottom of the map", and this will no longer be true printing the layout with newer QGIS versions.

Anyway, I think the exact way the scale is calculated (which is different between layout and decorations) should be explicitly indicated in the docs.

Copy link

Tests failed for Qt 5

One or more tests failed using the build from commit cf2be36

layoutscalebar_singlebox (singleBox)

layoutscalebar_singlebox

Test failed at singleBox at tests/src/core/testqgslayoutscalebar.cpp:119

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox/expected_layoutscalebar_singlebox.png (found 1074 pixels different)

layoutscalebar_singlebox_linesymbol (singleBoxLineSymbol)

layoutscalebar_singlebox_linesymbol

Test failed at singleBoxLineSymbol at tests/src/core/testqgslayoutscalebar.cpp:161

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_linesymbol/expected_layoutscalebar_singlebox_linesymbol.png (found 7548 pixels different)

layoutscalebar_singlebox_fillsymbol (singleBoxFillSymbol)

layoutscalebar_singlebox_fillsymbol

Test failed at singleBoxFillSymbol at tests/src/core/testqgslayoutscalebar.cpp:202

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_fillsymbol/expected_layoutscalebar_singlebox_fillsymbol.png (found 4421 pixels different)

layoutscalebar_singlebox_labelbelowsegment (singleBoxLabelBelowSegment)

layoutscalebar_singlebox_labelbelowsegment

Test failed at singleBoxLabelBelowSegment at tests/src/core/testqgslayoutscalebar.cpp:232

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_labelbelowsegment/expected_layoutscalebar_singlebox_labelbelowsegment.png (found 2555 pixels different)

layoutscalebar_singlebox_alpha (singleBoxAlpha)

layoutscalebar_singlebox_alpha

Test failed at singleBoxAlpha at tests/src/core/testqgslayoutscalebar.cpp:271

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_alpha/expected_layoutscalebar_singlebox_alpha.png (found 1233 pixels different)

layoutscalebar_doublebox (doubleBox)

layoutscalebar_doublebox

Test failed at doubleBox at tests/src/core/testqgslayoutscalebar.cpp:307

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox/expected_layoutscalebar_doublebox.png (found 1053 pixels different)

layoutscalebar_doublebox_linesymbol (doubleBoxLineSymbol)

layoutscalebar_doublebox_linesymbol

Test failed at doubleBoxLineSymbol at tests/src/core/testqgslayoutscalebar.cpp:349

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_linesymbol/expected_layoutscalebar_doublebox_linesymbol.png (found 6947 pixels different)

layoutscalebar_doublebox_fillsymbol (doubleBoxFillSymbol)

layoutscalebar_doublebox_fillsymbol

Test failed at doubleBoxFillSymbol at tests/src/core/testqgslayoutscalebar.cpp:390

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_fillsymbol/expected_layoutscalebar_doublebox_fillsymbol.png (found 4429 pixels different)

layoutscalebar_doublebox_labelcenteredsegment (doubleBoxLabelCenteredSegment)

layoutscalebar_doublebox_labelcenteredsegment

Test failed at doubleBoxLabelCenteredSegment at tests/src/core/testqgslayoutscalebar.cpp:429

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_labelcenteredsegment/expected_layoutscalebar_doublebox_labelcenteredsegment.png (found 4332 pixels different)

layoutscalebar_tick (tick)

layoutscalebar_tick

Test failed at tick at tests/src/core/testqgslayoutscalebar.cpp:497

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick/expected_layoutscalebar_tick.png (found 1076 pixels different)

layoutscalebar_tick_linesymbol (tickLineSymbol)

layoutscalebar_tick_linesymbol

Test failed at tickLineSymbol at tests/src/core/testqgslayoutscalebar.cpp:543

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick_linesymbol/expected_layoutscalebar_tick_linesymbol.png (found 9594 pixels different)

layoutscalebar_datadefined (dataDefined)

layoutscalebar_datadefined

Test failed at dataDefined at tests/src/core/testqgslayoutscalebar.cpp:615

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_datadefined/expected_layoutscalebar_datadefined.png (found 3477 pixels different)

layoutscalebar_textformat (textFormat)

layoutscalebar_textformat

Test failed at textFormat at tests/src/core/testqgslayoutscalebar.cpp:672

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_textformat/expected_layoutscalebar_textformat.png (found 2007 pixels different)

layoutscalebar_numericformat (numericFormat)

layoutscalebar_numericformat

Test failed at numericFormat at tests/src/core/testqgslayoutscalebar.cpp:706

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_numericformat/expected_layoutscalebar_numericformat.png (found 1394 pixels different)

layoutscalebar_stepped (steppedLine)

layoutscalebar_stepped

Test failed at steppedLine at tests/src/core/testqgslayoutscalebar.cpp:749

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_stepped/expected_layoutscalebar_stepped.png (found 8398 pixels different)

layoutscalebar_hollow (hollow)

layoutscalebar_hollow

Test failed at hollow at tests/src/core/testqgslayoutscalebar.cpp:806

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_hollow/expected_layoutscalebar_hollow.png (found 7401 pixels different)

layoutscalebar_tick_subdivisions (tickSubdivisions)

layoutscalebar_tick_subdivisions

Test failed at tickSubdivisions at tests/src/core/testqgslayoutscalebar.cpp:906

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick_subdivisions/expected_layoutscalebar_tick_subdivisions.png (found 4491 pixels different)

The full test report (included comparison of rendered vs expected images) can be found here.

Further documentation on the QGIS test infrastructure can be found in the Developer's Guide.

Copy link

Tests failed for Qt 6

One or more tests failed using the build from commit cf2be36

layoutscalebar_singlebox (singleBox)

layoutscalebar_singlebox

Test failed at singleBox at tests/src/core/testqgslayoutscalebar.cpp:119

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox/expected_layoutscalebar_singlebox.png (found 1067 pixels different)

layoutscalebar_singlebox_linesymbol (singleBoxLineSymbol)

layoutscalebar_singlebox_linesymbol

Test failed at singleBoxLineSymbol at tests/src/core/testqgslayoutscalebar.cpp:161

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_linesymbol/expected_layoutscalebar_singlebox_linesymbol.png (found 7554 pixels different)

layoutscalebar_singlebox_fillsymbol (singleBoxFillSymbol)

layoutscalebar_singlebox_fillsymbol

Test failed at singleBoxFillSymbol at tests/src/core/testqgslayoutscalebar.cpp:202

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_fillsymbol/expected_layoutscalebar_singlebox_fillsymbol.png (found 4448 pixels different)

layoutscalebar_singlebox_labelbelowsegment (singleBoxLabelBelowSegment)

layoutscalebar_singlebox_labelbelowsegment

Test failed at singleBoxLabelBelowSegment at tests/src/core/testqgslayoutscalebar.cpp:232

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_labelbelowsegment/expected_layoutscalebar_singlebox_labelbelowsegment.png (found 2559 pixels different)

layoutscalebar_singlebox_alpha (singleBoxAlpha)

layoutscalebar_singlebox_alpha

Test failed at singleBoxAlpha at tests/src/core/testqgslayoutscalebar.cpp:271

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_singlebox_alpha/expected_layoutscalebar_singlebox_alpha.png (found 1226 pixels different)

layoutscalebar_doublebox (doubleBox)

layoutscalebar_doublebox

Test failed at doubleBox at tests/src/core/testqgslayoutscalebar.cpp:307

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox/expected_layoutscalebar_doublebox.png (found 1049 pixels different)

layoutscalebar_doublebox_linesymbol (doubleBoxLineSymbol)

layoutscalebar_doublebox_linesymbol

Test failed at doubleBoxLineSymbol at tests/src/core/testqgslayoutscalebar.cpp:349

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_linesymbol/expected_layoutscalebar_doublebox_linesymbol.png (found 6939 pixels different)

layoutscalebar_doublebox_fillsymbol (doubleBoxFillSymbol)

layoutscalebar_doublebox_fillsymbol

Test failed at doubleBoxFillSymbol at tests/src/core/testqgslayoutscalebar.cpp:390

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_fillsymbol/expected_layoutscalebar_doublebox_fillsymbol.png (found 4460 pixels different)

layoutscalebar_doublebox_labelcenteredsegment (doubleBoxLabelCenteredSegment)

layoutscalebar_doublebox_labelcenteredsegment

Test failed at doubleBoxLabelCenteredSegment at tests/src/core/testqgslayoutscalebar.cpp:429

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_doublebox_labelcenteredsegment/expected_layoutscalebar_doublebox_labelcenteredsegment.png (found 4337 pixels different)

layoutscalebar_tick (tick)

layoutscalebar_tick

Test failed at tick at tests/src/core/testqgslayoutscalebar.cpp:497

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick/expected_layoutscalebar_tick.png (found 1067 pixels different)

layoutscalebar_tick_linesymbol (tickLineSymbol)

layoutscalebar_tick_linesymbol

Test failed at tickLineSymbol at tests/src/core/testqgslayoutscalebar.cpp:543

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick_linesymbol/expected_layoutscalebar_tick_linesymbol.png (found 9604 pixels different)

layoutscalebar_datadefined (dataDefined)

layoutscalebar_datadefined

Test failed at dataDefined at tests/src/core/testqgslayoutscalebar.cpp:615

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_datadefined/expected_layoutscalebar_datadefined.png (found 3487 pixels different)

layoutscalebar_textformat (textFormat)

layoutscalebar_textformat

Test failed at textFormat at tests/src/core/testqgslayoutscalebar.cpp:672

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_textformat/expected_layoutscalebar_textformat.png (found 2013 pixels different)

layoutscalebar_numericformat (numericFormat)

layoutscalebar_numericformat

Test failed at numericFormat at tests/src/core/testqgslayoutscalebar.cpp:706

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_numericformat/expected_layoutscalebar_numericformat.png (found 1411 pixels different)

layoutscalebar_stepped (steppedLine)

layoutscalebar_stepped

Test failed at steppedLine at tests/src/core/testqgslayoutscalebar.cpp:749

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_stepped/expected_layoutscalebar_stepped.png (found 8424 pixels different)

layoutscalebar_hollow (hollow)

layoutscalebar_hollow

Test failed at hollow at tests/src/core/testqgslayoutscalebar.cpp:806

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_hollow/expected_layoutscalebar_hollow.png (found 7400 pixels different)

layoutscalebar_tick_subdivisions (tickSubdivisions)

layoutscalebar_tick_subdivisions

Test failed at tickSubdivisions at tests/src/core/testqgslayoutscalebar.cpp:906

Rendered image did not match tests/testdata/control_images/layout_scalebar/expected_layoutscalebar_tick_subdivisions/expected_layoutscalebar_tick_subdivisions.png (found 4517 pixels different)

The full test report (included comparison of rendered vs expected images) can be found here.

Further documentation on the QGIS test infrastructure can be found in the Developer's Guide.

Copy link

The QGIS project highly values your contribution and would love to see this work merged! Unfortunately this PR has not had any activity in the last 14 days and is being automatically marked as "stale". If you think this pull request should be merged, please check

  • that all unit tests are passing

  • that all comments by reviewers have been addressed

  • that there is enough information for reviewers, in particular

    • link to any issues which this pull request fixes

    • add a description of workflows which this pull request fixes

    • add screenshots if applicable

  • that you have written unit tests where possible
    In case you should have any uncertainty, please leave a comment and we will be happy to help you proceed with this pull request.
    If there is no further activity on this pull request, it will be closed in a week.

@github-actions github-actions bot added the stale Uh oh! Seems this work is abandoned, and the PR is about to close. label Sep 27, 2024
Copy link

github-actions bot commented Oct 5, 2024

While we hate to see this happen, this PR has been automatically closed because it has not had any activity in the last 21 days. If this pull request should be reconsidered, please follow the guidelines in the previous comment and reopen this pull request. Or, if you have any further questions, just ask! We love to help, and if there's anything the QGIS project can do to help push this PR forward please let us know how we can assist.

@github-actions github-actions bot closed this Oct 5, 2024
@nyalldawson nyalldawson deleted the bad_scalebar_size branch October 27, 2024 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stale Uh oh! Seems this work is abandoned, and the PR is about to close.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants