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

Support for the positive scale in xee added. #154

Merged
merged 18 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from 13 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
25 changes: 16 additions & 9 deletions xee/ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def __init__(
default_scale = self.SCALE_UNITS.get(self.scale_units, 1)
if scale is None:
scale = default_scale
default_transform = affine.Affine.scale(scale, -1 * scale)
default_transform = affine.Affine.scale(scale, scale)

transform = affine.Affine(*proj.get('transform', default_transform)[:6])
self.scale_x, self.scale_y = transform.a, transform.e
Expand Down Expand Up @@ -420,13 +420,23 @@ def project(self, bbox: types.BBox) -> types.Grid:
appropriate region of data to return according to the Array's configured
projection and scale.
"""
# The origin of the image is in the top left corner. X is the minimum value
# and Y is the maximum value.
x_origin, _, _, y_origin = self.bounds # x_min, x_max, y_min, y_max
# The origin of the image is in the top left corner.
dabhicusp marked this conversation as resolved.
Show resolved Hide resolved
x_min, y_min, x_max, y_max = self.bounds
x_start, y_start, x_end, y_end = bbox
width = x_end - x_start
height = y_end - y_start

# Found the actual coordinates of the first or last point of the bbox based on the pos & neg scale in the actual image.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment needs some line-rewrapping. Consider re-writing to capture the weirdness of EE bounding boxes:

Origin (transform translation) cannot be found by simply identifying the min and max extents of the EE bounding box. The bounding boxes can be flipped (negative scale).

translateX = (
dabhicusp marked this conversation as resolved.
Show resolved Hide resolved
x_min + x_start * self.scale_x
if self.scale_x > 0
else x_max + self.scale_x * x_start
)
translateY = (
y_min + y_start * self.scale_y
if self.scale_y > 0
else y_max + self.scale_y * y_start
)
return {
# The size of the bounding box. The affine transform and project will be
# applied, so we can think in terms of pixels.
Expand All @@ -435,11 +445,8 @@ def project(self, bbox: types.BBox) -> types.Grid:
'height': height,
},
'affineTransform': {
# Since the origin is in the top left corner, we want to translate
# the start of the grid to the positive direction for X and the
# negative direction for Y.
'translateX': x_origin + self.scale_x * x_start,
dabhicusp marked this conversation as resolved.
Show resolved Hide resolved
'translateY': y_origin + self.scale_y * y_start,
'translateX': translateX,
'translateY': translateY,
# Define the scale for each pixel (e.g. the number of meters between
# each value).
'scaleX': self.scale_x,
Expand Down
24 changes: 21 additions & 3 deletions xee/ext_integration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,8 @@ def test_open_dataset__sanity_check(self):
ds = self.entry.open_dataset(
pathlib.Path('LANDSAT') / 'LC08' / 'C01' / 'T1',
drop_variables=tuple(f'B{i}' for i in range(3, 12)),
scale=25.0, # in degrees
n_images=3,
projection=ee.Projection('EPSG:4326', [25, 0, 0, 0, -25, 0]),
)
self.assertEqual(dict(ds.dims), {'time': 3, 'lon': 14, 'lat': 7})
self.assertNotEmpty(dict(ds.coords))
Expand All @@ -352,6 +352,24 @@ def test_open_dataset__sanity_check(self):
self.assertFalse(v.isnull().all(), 'All values are null!')
dabhicusp marked this conversation as resolved.
Show resolved Hide resolved
self.assertEqual(v.shape, (3, 14, 7))

def test_open_dataset__sanity_check_with_negative_scale(self):
ds = self.entry.open_dataset(
pathlib.Path('LANDSAT') / 'LC08' / 'C01' / 'T1',
drop_variables=tuple(f'B{i}' for i in range(3, 12)),
scale=-25.0, # in degrees
n_images=3,
)
self.assertEqual(dict(ds.dims), {'time': 3, 'lon': 14, 'lat': 7})
self.assertNotEmpty(dict(ds.coords))
self.assertEqual(
list(ds.data_vars.keys()),
[f'B{i}' for i in range(1, 3)] + ['BQA'],
)
for v in ds.values():
self.assertIsNotNone(v.data)
self.assertTrue(v.isnull().all(), 'All values must be null!')
self.assertEqual(v.shape, (3, 14, 7))

def test_open_dataset__n_images(self):
ds = self.entry.open_dataset(
pathlib.Path('LANDSAT') / 'LC08' / 'C01' / 'T1',
Expand Down Expand Up @@ -516,9 +534,9 @@ def test_write_projected_dataset_to_raster(self):
ds = xr.open_dataset(
col,
engine=xee.EarthEngineBackendEntrypoint,
scale=10,
crs=crs,
geometry=geom,
projection=ee.Projection('EPSG:4326', [10, 0, 0, 0, -10, 0]),
)

ds = ds.isel(time=0).transpose('Y', 'X')
Expand Down Expand Up @@ -553,8 +571,8 @@ def test_write_dataset_to_raster(self):
ds = xr.open_dataset(
col,
engine=xee.EarthEngineBackendEntrypoint,
scale=0.0025,
geometry=geom,
projection=ee.Projection('EPSG:4326', [0.0025, 0, 0, 0, -0.0025, 0]),
)

ds = ds.isel(time=0).transpose('lat', 'lon')
Expand Down
Loading