Skip to content

Conversation

hsteptoe
Copy link
Contributor

🚀 Pull Request

Description

Fixes #6126 by exposing the shape geometry to the user. Adds to docs and docstrings to make it clearer how to use this.


Consult Iris pull request check list


Add any of the below labels to trigger actions on this PR:

  • benchmark_this Request that this pull request be benchmarked to check if it introduces performance shifts

@pp-mo
Copy link
Member

pp-mo commented Aug 28, 2024

From @SciTools/peloton : discussed what we are seeing here and in #6126.
We think this is looking useful, but the two of you seem to have it covered @hsteptoe @acchamber ?
Please shout if you need help.

@hsteptoe
Copy link
Contributor Author

Discussion with @acchamber notes:

  • Don't move imports out of functions as this creates circular import errors
  • Check target_crs assignment for occasion when cube doesn't have a CRS
  • Re-write conversion of 0-360 -> -180-180 coords in case of degree based CRSs

@hsteptoe
Copy link
Contributor Author

What are @SciTools/peloton (@pp-mo) thoughts about adding rasterio as a Iris dependency?

This would facilitate better handling of shapes to rasters (which is essentially the problem we're solving) with the added bonus that we could also mask to other shape types, like lines, to extract a trajectory from a Cube, for example.

@acchamber
Copy link
Contributor

I've had a few other discussions with other users now where they can't use this function with OSGB shapefiles which this PR would fix. Where are we on being able to include it in the next release?

@hsteptoe @pp-mo

@hsteptoe
Copy link
Contributor Author

hsteptoe commented Dec 2, 2024

I've had a few other discussions with other users now where they can't use this function with OSGB shapefiles which this PR would fix. Where are we on being able to include it in the next release?

@hsteptoe @pp-mo

I was planning on going to the AVD surgery this week to discuss some changes and/or make the case for rasterio

@trexfeathers
Copy link
Contributor

Thanks for your patience everyone. It's sometimes a struggle to balance everything and we just haven't had an opportunity to discuss this. Coming to the Surgery (UK Met Office) is an ideal next step.

@stephenworsley
Copy link
Contributor

@pp-mo, @trexfeathers and @stephenworsley have discussed this at the Surgery and agree in principle that we could consider adding rasterio as an optional dependency.

@trexfeathers
Copy link
Contributor

From @SciTools/peloton: would @hsteptoe and @acchamber appreciate any more input from core devs at this point?

@hsteptoe
Copy link
Contributor Author

hsteptoe commented Feb 5, 2025

From @SciTools/peloton: would @hsteptoe and @acchamber appreciate any more input from core devs at this point?

I just need to find more time, which is in short supply as we get near the end of FY...

@hsteptoe
Copy link
Contributor Author

hsteptoe commented Feb 14, 2025

This is now ready for some initial (alpha?) testing 🎉. @acchamber are you available to try out the new features? (Updates to docs etc. will follow if this is successful) Tests are incomplete are should not be review yet.

This is a fairly substantial re-write. New/improved features include:

  • Automatic CRS transforms. Will handle situation where the cube and geometry have different CRSs by transforming the geometry.
  • Handles all shape types, including Points, Lines and MultiPolygons.

There are some breaking changes, principally moving away from having a minimum_weight keyword, replacing it with an all_touched keyword. The rational here is for consistency across shape types. Minimum weight doesn't make sense for Points and Lines. Instead, Line pixilation is based on Bresenham's line algorithm. Polygon pixilation is based on either: (a) pixel centres that intersect the polygon, or (b) all pixels touched by the polygon irrespective of the area of intersection.

My informal tests so far show working behaviour for iris test data from rotated_pole.nc, toa_brightness_stereographic.nc, E1_north_america.nc, air_temp.pp. Know limitation (not-working) for semi-structured model grids, such as orca2_votemper.nc. Some test shapefiles 👉test_shapefiles.zip

@bjlittle bjlittle added this to the v3.13 milestone Feb 17, 2025
@scitools-ci scitools-ci bot removed this from 🚴 Peloton Sep 24, 2025
@CLAassistant
Copy link

CLAassistant commented Oct 6, 2025

CLA assistant check
All committers have signed the CLA.

@hsteptoe
Copy link
Contributor Author

hsteptoe commented Oct 7, 2025

@stephenworsley @trexfeathers Can you advise on apparently nox related errors that I'm seeing in the current CI failures, giving an error message: CondaHTTPError: HTTP 401 CONNECTION FAILED for url...

And, do you know why my local pre-commit version of numpydoc-validation might pass, but the github action one fails?

@trexfeathers
Copy link
Contributor

@stephenworsley @trexfeathers Can you advise on apparently nox related errors that I'm seeing in the current CI failures, giving an error message: CondaHTTPError: HTTP 401 CONNECTION FAILED for url...

And, do you know why my local pre-commit version of numpydoc-validation might pass, but the github action one fails?

@hsteptoe really sorry for missing this. There is a MO-specific step you will need to do for the lockfiles, will message you offline.

I'm less sure about pre-commit. There is some nuance about only testing files which have been edited; perhaps the merge from main means that your PR is changing more than your local branch was.

@trexfeathers trexfeathers moved this to 👀 In Review in 🦌 Iris 3.14 Oct 15, 2025
@trexfeathers trexfeathers changed the title Bugfix shape masking Refactor shape masking Oct 15, 2025
@hsteptoe
Copy link
Contributor Author

@stephenworsley @trexfeathers Can you advise on apparently nox related errors that I'm seeing in the current CI failures, giving an error message: CondaHTTPError: HTTP 401 CONNECTION FAILED for url...
And, do you know why my local pre-commit version of numpydoc-validation might pass, but the github action one fails?

@hsteptoe really sorry for missing this. There is a MO-specific step you will need to do for the lockfiles, will message you offline.

I'm less sure about pre-commit. There is some nuance about only testing files which have been edited; perhaps the merge from main means that your PR is changing more than your local branch was.

pre-commit related errors may have been something wrong going on with my pre-commit cache. Did a pre-commit clean and re-ran and that seemed to resolve the inconsistency issues.

Copy link
Contributor

@trexfeathers trexfeathers left a comment

Choose a reason for hiding this comment

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

Still left to review:

  • _shapefiles.py
  • tests
  • lock files

pyproject.toml Outdated
Comment on lines 55 to 57
[project.optional-dependencies]
RASTERIO = ["rasterio"]

Copy link
Contributor

Choose a reason for hiding this comment

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

We didn't. However we are attempting to standardise across our repositories and we don't have an agreed way to handle optional dependencies, so please remove this entry.

Comment on lines +2229 to +2234
cube: iris.cube.Cube,
shape: shapely.Geometry,
shape_crs: cartopy.crs | pyproj.CRS,
in_place: bool = False,
minimum_weight: float = 0.0,
**kwargs,
Copy link
Contributor

Choose a reason for hiding this comment

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

You've changed the order of parameters. Anyone calling this without using keywords e.g. mask_cube_from_shapefile(my_cube, my_shape, 0.5, True) will get a failure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reverted back to depreciated function call, but have had to add a few extra lines to pull in a shape_crs as per https://github.com/SciTools/iris/blob/fa6d61dd5f358c9e6b585a132cc213acebf95b70/lib/iris/_shapefiles.py#L142C18-L144C6

A number between 0-1 describing what % of a cube cell area must the shape overlap to be masked.
Only applied to polygon shapes. If the shape is a line or point then this is ignored.
Other Parameters
Copy link
Contributor

Choose a reason for hiding this comment

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

How did you decide which ones were kwargs and which ones were named parameters?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I decided that kwargs would be parameters that are specifically used by rasterio functions only, but I'm happy to expose them if that's clearer?

Extract a rectangular region covering the UK from a stereographic projection cube:
>>> cube = iris.load_cube(iris.sample_data_path("toa_brightness_stereographic.nc"))
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be great to print some one line Cube summaries to demonstrate the effect that masking is having.

Example:

Suggested change
>>> cube = iris.load_cube(iris.sample_data_path("toa_brightness_stereographic.nc"))
>>> cube = iris.load_cube(iris.sample_data_path("toa_brightness_stereographic.nc"))
>>> print(cube.summary(shorten=True))
toa_brightness_temperature / (K) (projection_y_coordinate: 160; projection_x_coordinate: 256)

Copy link
Contributor

@trexfeathers trexfeathers left a comment

Choose a reason for hiding this comment

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

Still left to review:

  • tests
  • lock files
  • confirm stable behaviour given the heavy refactor

Comment on lines +35 to +36
# to "OFF" in the environment. Having PROJ_NETWORK = "ON"
# can lead to some coordinate transformations resulting in Inf values.
Copy link
Contributor

Choose a reason for hiding this comment

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

What a pointless feature! I'd be interested to know why.

Comment on lines 27 to 30
if "iris.cube" in sys.modules:
import iris.cube
if "iris.analysis.cartography" in sys.modules:
import iris.analysis.cartography
Copy link
Contributor

Choose a reason for hiding this comment

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

This is unprecedented. Something larger is wrong if these modules are not available.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can't remember anymore why I added this (!) so it's now removed.

w = len(x_coord.points)
h = len(y_coord.points)
# Mask by weight if minimum_weight > 0.0
if minimum_weight > 0:
Copy link
Contributor

Choose a reason for hiding this comment

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

We discussed preserving existing behaviour with minimum_weight - #6129 (comment) - to avoid breaking changes. I'm having a hard time confirming this:

  • The code for this behaviour has been heavily refactored
  • The existing tests have been rewritten and can no longer be run in their original form due to API changes

Can you help? Do you have anything 'ready-made' that I can use to confirm stable behaviour? Otherwise I'll write something myself. Thanks

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The integration tests lib/iris/tests/integration/test_mask_cube_from_shape.py include tests from the previous iteration of this function (which was itself benchmarking against ASCEND). Specifically, the tests test_4d_global_proj_brazil, test_rotated_pole_proj_germany_weighted_area and test_transverse_mercator_proj_uk are testing against identical conditions. Would this be sufficient to confirm stable behaviour?

I have added warning text from _shapefiles into utils to emphasise behaviour change that may be seen as breaking behaviour (although this wasn't stable behaviour in the first place):

Because shape vectors are inherently Cartesian in nature, they contain no inherent
understanding of the spherical geometry underpinning geographic coordinate systems.
For this reason, **shapefiles or shape vectors that cross the antimeridian or poles
are not supported by this function to avoid unexpected masking behaviour**.

Comment on lines 205 to 214
# Define raster transform based on cube
# This maps the geometry domain onto the cube domain
tr = _make_raster_cube_transform(cube)
# Generate mask from geometry
mask_template = rfeatures.geometry_mask(
geometries=shapely.get_parts(geometry),
out_shape=(h, w),
transform=tr,
all_touched=all_touched,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you be up for giving me a 5 minute lesson about this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

benchmark_this Request that this pull request be benchmarked to check if it introduces performance shifts Type: Bug

Projects

Status: No status
Status: 👀 In Review

Development

Successfully merging this pull request may close these issues.

_transform_coord_system geometry is always assume to be None

9 participants