Skip to content

Commit 7bb1407

Browse files
author
Xee authors
committed
Merge pull request #97 from boothmanrylan:transform-coordinates
PiperOrigin-RevId: 592884814
2 parents 9b8c50a + 24ff582 commit 7bb1407

3 files changed

Lines changed: 88 additions & 7 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ tests = [
3838
"absl-py",
3939
"pytest",
4040
"pyink",
41+
"rasterio",
42+
"rioxarray",
4143
]
4244
examples = [
4345
"apache_beam[gcp]",

xee/ext.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -547,11 +547,11 @@ def _get_tile_from_ee(
547547
tile_index, band_id = tile_index
548548
bbox = self.project(
549549
(tile_index[0], 0, tile_index[1], 1)
550-
if band_id == 'longitude'
550+
if band_id == 'x'
551551
else (0, tile_index[0], 1, tile_index[1])
552552
)
553553
tile_idx = slice(tile_index[0], tile_index[1])
554-
target_image = ee.Image.pixelLonLat()
554+
target_image = ee.Image.pixelCoordinates(ee.Projection(self.crs_arg))
555555
return tile_idx, self.image_to_array(
556556
target_image, grid=bbox, dtype=np.float32, bandIds=[band_id]
557557
)
@@ -574,9 +574,7 @@ def _process_coordinate_data(
574574
self._get_tile_from_ee,
575575
list(zip(data, itertools.cycle([coordinate_type]))),
576576
):
577-
tiles[i] = (
578-
arr.tolist() if coordinate_type == 'longitude' else arr.tolist()[0]
579-
)
577+
tiles[i] = arr.tolist() if coordinate_type == 'x' else arr.tolist()[0]
580578
return np.concatenate(tiles)
581579

582580
def get_variables(self) -> utils.Frozen[str, xarray.Variable]:
@@ -605,11 +603,11 @@ def get_variables(self) -> utils.Frozen[str, xarray.Variable]:
605603

606604
lon_total_tile = math.ceil(v0.shape[1] / width_chunk)
607605
lon = self._process_coordinate_data(
608-
lon_total_tile, width_chunk, v0.shape[1], 'longitude'
606+
lon_total_tile, width_chunk, v0.shape[1], 'x'
609607
)
610608
lat_total_tile = math.ceil(v0.shape[2] / height_chunk)
611609
lat = self._process_coordinate_data(
612-
lat_total_tile, height_chunk, v0.shape[2], 'latitude'
610+
lat_total_tile, height_chunk, v0.shape[2], 'y'
613611
)
614612

615613
width_coord = np.squeeze(lon)

xee/ext_integration_test.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import json
1717
import os
1818
import pathlib
19+
import tempfile
1920

2021
from absl.testing import absltest
2122
from google.auth import identity_pool
@@ -26,6 +27,13 @@
2627

2728
import ee
2829

30+
_SKIP_RASTERIO_TESTS = False
31+
try:
32+
import rasterio # pylint: disable=g-import-not-at-top
33+
import rioxarray # pylint: disable=g-import-not-at-top,unused-import
34+
except ImportError:
35+
_SKIP_RASTERIO_TESTS = True
36+
2937
_CREDENTIALS_PATH_KEY = 'GOOGLE_APPLICATION_CREDENTIALS'
3038
_SCOPES = [
3139
'https://www.googleapis.com/auth/cloud-platform',
@@ -397,6 +405,79 @@ def test_validate_band_attrs(self):
397405
for _, value in variable.attrs.items():
398406
self.assertIsInstance(value, valid_types)
399407

408+
@absltest.skipIf(_SKIP_RASTERIO_TESTS, 'rioxarray module not loaded')
409+
def test_write_projected_dataset_to_raster(self):
410+
# ensure that a projected dataset written to a raster intersects with the
411+
# point used to create the initial image collection
412+
with tempfile.TemporaryDirectory() as temp_dir:
413+
temp_file = os.path.join(temp_dir, 'test.tif')
414+
415+
crs = 'epsg:32610'
416+
proj = ee.Projection(crs)
417+
point = ee.Geometry.Point([-122.44, 37.78])
418+
geom = point.buffer(1024).bounds()
419+
420+
col = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED')
421+
col = col.filterBounds(point)
422+
col = col.filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 5))
423+
col = col.limit(10)
424+
425+
ds = xr.open_dataset(
426+
col,
427+
engine=xee.EarthEngineBackendEntrypoint,
428+
scale=10,
429+
crs=crs,
430+
geometry=geom,
431+
)
432+
433+
ds = ds.isel(time=0).transpose('Y', 'X')
434+
ds.rio.set_spatial_dims(x_dim='X', y_dim='Y', inplace=True)
435+
ds.rio.write_crs(crs, inplace=True)
436+
ds.rio.reproject(crs, inplace=True)
437+
ds.rio.to_raster(temp_file)
438+
439+
with rasterio.open(temp_file) as raster:
440+
# see https://gis.stackexchange.com/a/407755 for evenOdd explanation
441+
bbox = ee.Geometry.Rectangle(raster.bounds, proj=proj, evenOdd=False)
442+
intersects = bbox.intersects(point, 1, proj=proj)
443+
self.assertTrue(intersects.getInfo())
444+
445+
@absltest.skipIf(_SKIP_RASTERIO_TESTS, 'rioxarray module not loaded')
446+
def test_write_dataset_to_raster(self):
447+
# ensure that a dataset written to a raster intersects with the point used
448+
# to create the initial image collection
449+
with tempfile.TemporaryDirectory() as temp_dir:
450+
temp_file = os.path.join(temp_dir, 'test.tif')
451+
452+
crs = 'EPSG:4326'
453+
proj = ee.Projection(crs)
454+
point = ee.Geometry.Point([-122.44, 37.78])
455+
geom = point.buffer(1024).bounds()
456+
457+
col = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED')
458+
col = col.filterBounds(point)
459+
col = col.filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 5))
460+
col = col.limit(10)
461+
462+
ds = xr.open_dataset(
463+
col,
464+
engine=xee.EarthEngineBackendEntrypoint,
465+
scale=0.0025,
466+
geometry=geom,
467+
)
468+
469+
ds = ds.isel(time=0).transpose('lat', 'lon')
470+
ds.rio.set_spatial_dims(x_dim='lon', y_dim='lat', inplace=True)
471+
ds.rio.write_crs(crs, inplace=True)
472+
ds.rio.reproject(crs, inplace=True)
473+
ds.rio.to_raster(temp_file)
474+
475+
with rasterio.open(temp_file) as raster:
476+
# see https://gis.stackexchange.com/a/407755 for evenOdd explanation
477+
bbox = ee.Geometry.Rectangle(raster.bounds, proj=proj, evenOdd=False)
478+
intersects = bbox.intersects(point, 1, proj=proj)
479+
self.assertTrue(intersects.getInfo())
480+
400481

401482
if __name__ == '__main__':
402483
absltest.main()

0 commit comments

Comments
 (0)