From 9ba03317a9b3447c1af52dfeb92b3ea86a15ca1a Mon Sep 17 00:00:00 2001 From: lbferreira Date: Thu, 11 Jan 2024 16:49:22 -0600 Subject: [PATCH 1/5] Fixed the logic used to get the bounds of the geometry provided to filter the input data. Previously, the bounds were obtained in epgs:4326 and then reprojected for the target crs. Now the bounds are obtained in the target crs directly. --- xee/ext.py | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/xee/ext.py b/xee/ext.py index 36a5316..ca6150d 100644 --- a/xee/ext.py +++ b/xee/ext.py @@ -186,16 +186,19 @@ def __init__( self.geometry = geometry self.primary_dim_name = primary_dim_name or 'time' self.primary_dim_property = primary_dim_property or 'system:time_start' - + + # If crs_arg is None, we try to get it from projection but if + # projection is None, we use a default value ('EPSG:4326'). + # This logic is implemented in self.get_info(). + self.crs_arg = crs + proj = self.get_info.get('projection', {}) + self.crs_arg = self.get_info['crs_arg'] + self.crs = CRS(self.crs_arg) + self.n_images = self.get_info['size'] self._props = self.get_info['props'] # Metadata should apply to all imgs. self._img_info: types.ImageInfo = self.get_info['first'] - - proj = self.get_info.get('projection', {}) - - self.crs_arg = crs or proj.get('crs', proj.get('wkt', 'EPSG:4326')) - self.crs = CRS(self.crs_arg) # Gets the unit i.e. meter, degree etc. self.scale_units = self.crs.axis_info[0].unit_name # Get the dimensions name based on the CRS (scale units). @@ -224,22 +227,22 @@ def __init__( # used for all internal `computePixels()` calls. try: if isinstance(geometry, ee.Geometry): - x_min_0, y_min_0, x_max_0, y_max_0 = _ee_bounds_to_bounds( + x_min, y_min, x_max, y_max = _ee_bounds_to_bounds( self.get_info['bounds'] ) else: x_min_0, y_min_0, x_max_0, y_max_0 = self.crs.area_of_use.bounds + x_min, y_min = self.transform(x_min_0, y_min_0) + x_max, y_max = self.transform(x_max_0, y_max_0) + self.bounds = x_min, y_min, x_max, y_max except AttributeError: # `area_of_use` is probable `None`. Parse the geometry from the first # image instead (calculated in self.get_info()) - x_min_0, y_min_0, x_max_0, y_max_0 = _ee_bounds_to_bounds( + x_min, y_min, x_max, y_max = _ee_bounds_to_bounds( self.get_info['bounds'] - ) - - x_min, y_min = self.transform(x_min_0, y_min_0) - x_max, y_max = self.transform(x_max_0, y_max_0) + ) self.bounds = x_min, y_min, x_max, y_max - + max_dtype = self._max_itemsize() # TODO(b/291851322): Consider support for laziness when chunks=None. @@ -268,11 +271,18 @@ def get_info(self) -> Dict[str, Any]: if isinstance(self.projection, ee.Projection): rpcs.append(('projection', self.projection)) + + if self.crs_arg is not None: + rpcs.append(('crs_arg', self.crs_arg)) + else: + rpcs_dict = {k: v for k, v in rpcs} + rpcs.append(('crs_arg', rpcs_dict.get('projection', {}).get('wkt', 'EPSG:4326'))) + rpcs_dict = {k: v for k, v in rpcs} if isinstance(self.geometry, ee.Geometry): - rpcs.append(('bounds', self.geometry.bounds())) + rpcs.append(('bounds', self.geometry.bounds(1, rpcs_dict['crs_arg']))) else: - rpcs.append(('bounds', self.image_collection.first().geometry().bounds())) + rpcs.append(('bounds', self.image_collection.first().geometry().bounds(1, rpcs_dict['crs_arg']))) # TODO(#29, #30): This RPC call takes the longest time to compute. This # requires a full scan of the images in the collection, which happens on the From fc48d259c9d8f39327ab83a3f4398ca6ba444770 Mon Sep 17 00:00:00 2001 From: lbferreira Date: Thu, 11 Jan 2024 16:58:43 -0600 Subject: [PATCH 2/5] Changed rpcs from a list of tuples to a dict in the method get_info --- xee/ext.py | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/xee/ext.py b/xee/ext.py index ca6150d..0722e1e 100644 --- a/xee/ext.py +++ b/xee/ext.py @@ -263,26 +263,25 @@ def __init__( def get_info(self) -> Dict[str, Any]: """Make all getInfo() calls to EE at once.""" - rpcs = [ - ('size', self.image_collection.size()), - ('props', self.image_collection.toDictionary()), - ('first', self.image_collection.first()), - ] + rpcs = { + 'size': self.image_collection.size(), + 'props': self.image_collection.toDictionary(), + 'first': self.image_collection.first(), + } if isinstance(self.projection, ee.Projection): - rpcs.append(('projection', self.projection)) + rpcs['projection'] = self.projection if self.crs_arg is not None: - rpcs.append(('crs_arg', self.crs_arg)) + rpcs['crs_arg'] = self.crs_arg else: - rpcs_dict = {k: v for k, v in rpcs} - rpcs.append(('crs_arg', rpcs_dict.get('projection', {}).get('wkt', 'EPSG:4326'))) + rpcs['crs_arg'] = rpcs.get('projection', {}).get('wkt', 'EPSG:4326') - rpcs_dict = {k: v for k, v in rpcs} if isinstance(self.geometry, ee.Geometry): - rpcs.append(('bounds', self.geometry.bounds(1, rpcs_dict['crs_arg']))) + # TODO: Is 1 good enough for the max error? + rpcs['bounds'] = self.geometry.bounds(1, rpcs['crs_arg']) else: - rpcs.append(('bounds', self.image_collection.first().geometry().bounds(1, rpcs_dict['crs_arg']))) + rpcs['bounds'] = self.image_collection.first().geometry().bounds(1, rpcs['crs_arg']) # TODO(#29, #30): This RPC call takes the longest time to compute. This # requires a full scan of the images in the collection, which happens on the @@ -295,18 +294,15 @@ def get_info(self) -> Dict[str, Any]: # client-side. Ideally, this would live behind a xarray-backend-specific # feature flag, since it's not guaranteed that data is this consistent. columns = ['system:id', self.primary_dim_property] - rpcs.append(( - 'properties', - ( - self.image_collection.reduceColumns( - ee.Reducer.toList().repeat(len(columns)), columns - ).get('list') - ), - )) - - info = ee.List([rpc for _, rpc in rpcs]).getInfo() + rpcs['properties'] = ( + self.image_collection.reduceColumns( + ee.Reducer.toList().repeat(len(columns)), columns + ).get('list') + ) - return dict(zip((name for name, _ in rpcs), info)) + info = ee.List([rpc for _, rpc in rpcs.items()]).getInfo() + + return dict(zip((name for name, _ in rpcs.items()), info)) @property def image_collection_properties(self) -> Tuple[List[str], List[str]]: From 24f7eae38de82a541242841ae0a0b6f6e4fbe034 Mon Sep 17 00:00:00 2001 From: lbferreira Date: Thu, 11 Jan 2024 16:59:36 -0600 Subject: [PATCH 3/5] Added a TODO note --- xee/ext.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xee/ext.py b/xee/ext.py index 0722e1e..67bfbc7 100644 --- a/xee/ext.py +++ b/xee/ext.py @@ -231,6 +231,8 @@ def __init__( self.get_info['bounds'] ) else: + # TODO: Maybe converting the area of use to the desired + # crs and them getting the bounds is a better approach. x_min_0, y_min_0, x_max_0, y_max_0 = self.crs.area_of_use.bounds x_min, y_min = self.transform(x_min_0, y_min_0) x_max, y_max = self.transform(x_max_0, y_max_0) From 83cc5d0c620982b8c717eabe0a9686abe57c0a1b Mon Sep 17 00:00:00 2001 From: lbferreira Date: Fri, 12 Jan 2024 11:15:46 -0600 Subject: [PATCH 4/5] Fix handling of projection in the function get_info --- xee/ext.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xee/ext.py b/xee/ext.py index 67bfbc7..18fd4a8 100644 --- a/xee/ext.py +++ b/xee/ext.py @@ -277,7 +277,10 @@ def get_info(self) -> Dict[str, Any]: if self.crs_arg is not None: rpcs['crs_arg'] = self.crs_arg else: - rpcs['crs_arg'] = rpcs.get('projection', {}).get('wkt', 'EPSG:4326') + if 'projection' in rpcs.keys(): + rpcs['crs_arg'] = rpcs['projection'].crs() + else: + rpcs['crs_arg'] = 'EPSG:4326' if isinstance(self.geometry, ee.Geometry): # TODO: Is 1 good enough for the max error? From 9ef5c15b7cbcfebcf77e8ef417cda2b0ba2cf521 Mon Sep 17 00:00:00 2001 From: lbferreira Date: Fri, 12 Jan 2024 11:18:00 -0600 Subject: [PATCH 5/5] Code style improvement --- xee/ext.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/xee/ext.py b/xee/ext.py index 18fd4a8..7471b5e 100644 --- a/xee/ext.py +++ b/xee/ext.py @@ -276,11 +276,10 @@ def get_info(self) -> Dict[str, Any]: if self.crs_arg is not None: rpcs['crs_arg'] = self.crs_arg + elif 'projection' in rpcs.keys(): + rpcs['crs_arg'] = rpcs['projection'].crs() else: - if 'projection' in rpcs.keys(): - rpcs['crs_arg'] = rpcs['projection'].crs() - else: - rpcs['crs_arg'] = 'EPSG:4326' + rpcs['crs_arg'] = 'EPSG:4326' if isinstance(self.geometry, ee.Geometry): # TODO: Is 1 good enough for the max error?