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

Optimizing raster.icrop #660

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

Conversation

vschaffn
Copy link
Contributor

@vschaffn vschaffn commented Mar 4, 2025

Resolves #659

Description

  • Changes order of icrop bounding box to match with bouding box of other objects.
  • Removes dependancy on crop to avoid useless coordinates conversion.

Tests

  • Update icrop tests.

@adebardo
Copy link

Are we sure that the DEM is not completely load before crop ?

@@ -173,7 +173,7 @@ def _outside_image(
"""See description of Raster.outside_image."""

if not index:
xi, xj = _xy2ij(xi, yj, transform=transform, area_or_point=area_or_point)
xi, yj = _xy2ij(xi, yj, transform=transform, area_or_point=area_or_point)
Copy link
Member

Choose a reason for hiding this comment

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

Good catch! 😮

@@ -2464,15 +2480,29 @@ def icrop(
if len(bbox) != 4:
raise ValueError("bbox must be a list or tuple of four integers: (xmin, ymin, xmax, ymax).")

# Convert pixel coordinates to georeferenced coordinates using the transform
Copy link
Member

@rhugonnet rhugonnet Mar 16, 2025

Choose a reason for hiding this comment

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

The argument description above was ambiguous for me:

    :param bbox: Pixel-based bounding box as (xmin, ymin, xmax, ymax) in pixel coordinates.

Maybe similar language as in the function description "Bounding box based on indices of the raster array".

We could also add a note in the general description to clarify why this is strikingly different, something like: "Note that, by convention, a raster has the Y axis as first in its array, and that this Y axis is also flipped (first index corresponds to the highest-value coordinate)."

# Crop the raster data based on the given bounding box (bbox), limit the range to the borders of the raster
crop_img = self.data[..., bbox[0] : min(self.height, bbox[2]), bbox[1] : min(bbox[3], self.width)]

# Create a new affine transform that corresponds to the cropped area
Copy link
Member

Choose a reason for hiding this comment

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

Took me a little while to understand why this transform looked like a translation!
When we crop within the extent, the new array size determines the new height/width, so it is only a translation for the affine transform!
In this case, let's use our tested _translate function to make it easier to read/debug for future us 😉:

@@ -1818,28 +1818,33 @@ def test_icrop(self, example: str, bbox: list[int] | tuple[int, ...]) -> None:
raster = gu.Raster(example)

# Call the icrop method with the given bounding box, returning a cropped raster
raster_cropped = raster.icrop(bbox=bbox)
raster_icropped = raster.icrop(bbox=bbox)

# Compute the expected affine transform for the cropped raster
transformed_cropped = rio.transform.Affine(
Copy link
Member

Choose a reason for hiding this comment

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

Can use _translate here too.

Copy link
Member

@rhugonnet rhugonnet left a comment

Choose a reason for hiding this comment

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

Perfect, thanks!

bottom_left = self.transform * (bbox[0], self.height - bbox[1])
top_right = self.transform * (bbox[2], self.height - bbox[3])
# Crop the raster data based on the given bounding box (bbox), limit the range to the borders of the raster
crop_img = self.data[..., bbox[0] : min(self.height, bbox[2]), bbox[1] : min(bbox[3], self.width)]
Copy link
Member

Choose a reason for hiding this comment

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

Ah sorry I didn't notice this line and @adebardo's comment, good catch.
Indeed, this will load the entire array, then subset it. To circumvent this, we need to use crop() as before.

Copy link
Member

Choose a reason for hiding this comment

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

We should add a test checking if Raster.is_loaded is still False after calling icrop().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fix raster.icrop
3 participants