From 7fad85de6c1a5c514cc598eec40526a766a18a1e Mon Sep 17 00:00:00 2001 From: Martin Raspaud Date: Wed, 28 Jun 2023 14:03:39 +0200 Subject: [PATCH 1/4] Deprecate frequency arg for bbox methods --- pyresample/geometry.py | 80 +++++++++++++++++++++++++++--------------- 1 file changed, 51 insertions(+), 29 deletions(-) diff --git a/pyresample/geometry.py b/pyresample/geometry.py index a25839945..d2e483457 100644 --- a/pyresample/geometry.py +++ b/pyresample/geometry.py @@ -296,11 +296,12 @@ def get_boundary_lonlats(self): return (SimpleBoundary(s1_lon.squeeze(), s2_lon.squeeze(), s3_lon.squeeze(), s4_lon.squeeze()), SimpleBoundary(s1_lat.squeeze(), s2_lat.squeeze(), s3_lat.squeeze(), s4_lat.squeeze())) - def get_bbox_lonlats(self, frequency: Optional[int] = None, force_clockwise: bool = True) -> tuple: + def get_bbox_lonlats(self, bbox_shape: Optional[int] = None, force_clockwise: bool = True, + frequency: Optional[int] = None) -> tuple: """Return the bounding box lons and lats. Args: - frequency: + bbox_shape (formerly frequency): The number of points to provide for each side. By default (None) the full width and height will be provided. force_clockwise: @@ -328,15 +329,19 @@ def get_bbox_lonlats(self, frequency: Optional[int] = None, force_clockwise: boo pyresample (ex. :class:`pyresample.spherical.SphPolygon`). """ - lons, lats = self._get_bbox_elements(self.get_lonlats, frequency) + if frequency is not None: + warnings.warn("The `frequency` argument is pending deprecation, use `bbox_shape` instead", + PendingDeprecationWarning, stacklevel=2) + bbox_shape = bbox_shape or frequency + lons, lats = self._get_bbox_elements(self.get_lonlats, bbox_shape) if force_clockwise and not self._corner_is_clockwise( lons[0][-2], lats[0][-2], lons[0][-1], lats[0][-1], lons[1][1], lats[1][1]): # going counter-clockwise lons, lats = self._reverse_boundaries(lons, lats) return lons, lats - def _get_bbox_elements(self, coord_fun, frequency: Optional[int] = None) -> tuple: - s1_slice, s2_slice, s3_slice, s4_slice = self._get_bbox_slices(frequency) + def _get_bbox_elements(self, coord_fun, bbox_shape: Optional[int] = None) -> tuple: + s1_slice, s2_slice, s3_slice, s4_slice = self._get_bbox_slices(bbox_shape) s1_dim1, s1_dim2 = coord_fun(data_slice=s1_slice) s2_dim1, s2_dim2 = coord_fun(data_slice=s2_slice) s3_dim1, s3_dim2 = coord_fun(data_slice=s3_slice) @@ -365,14 +370,14 @@ def _filter_bbox_nans( new_dim2_sides.append(dim2_side[is_valid_mask]) return new_dim1_sides, new_dim2_sides - def _get_bbox_slices(self, frequency): + def _get_bbox_slices(self, bbox_shape): height, width = self.shape - if frequency is None: + if bbox_shape is None: row_num = height col_num = width else: - row_num = frequency - col_num = frequency + row_num = bbox_shape + col_num = bbox_shape s1_slice = (0, np.linspace(0, width - 1, col_num, dtype=int)) s2_slice = (np.linspace(0, height - 1, row_num, dtype=int), -1) s3_slice = (-1, np.linspace(width - 1, 0, col_num, dtype=int)) @@ -415,24 +420,33 @@ def _corner_is_clockwise(lon1, lat1, corner_lon, corner_lat, lon2, lat2): is_clockwise = -np.pi < angle < 0 return is_clockwise - def get_edge_lonlats(self, frequency=None): + def get_edge_lonlats(self, bbox_shape=None, frequency=None): """Get the concatenated boundary of the current swath.""" - lons, lats = self.get_bbox_lonlats(frequency=frequency, force_clockwise=False) + if frequency is not None: + warnings.warn("The `frequency` argument is pending deprecation, use `bbox_shape` instead", + PendingDeprecationWarning, stacklevel=2) + bbox_shape = bbox_shape or frequency + lons, lats = self.get_bbox_lonlats(bbox_shape=bbox_shape, force_clockwise=False) blons = np.ma.concatenate(lons) blats = np.ma.concatenate(lats) return blons, blats - def get_edge_bbox_in_projection_coordinates(self, frequency: Optional[int] = None): + def get_edge_bbox_in_projection_coordinates(self, bbox_shape: Optional[int] = None, + frequency: Optional[int] = None): """Return the bounding box in projection coordinates.""" - x, y = self._get_bbox_elements(self.get_proj_coords, frequency) + if frequency is not None: + warnings.warn("The `frequency` argument is pending deprecation, use `bbox_shape` instead", + PendingDeprecationWarning, stacklevel=2) + bbox_shape = bbox_shape or frequency + x, y = self._get_bbox_elements(self.get_proj_coords, bbox_shape) return np.hstack(x), np.hstack(y) - def boundary(self, frequency=None, force_clockwise=False): + def boundary(self, bbox_shape=None, force_clockwise=False, frequency=None): """Retrieve the AreaBoundary object. Parameters ---------- - frequency: + bbox_shape (formerly frequency): The number of points to provide for each side. By default (None) the full width and height will be provided. force_clockwise: @@ -445,7 +459,11 @@ def boundary(self, frequency=None, force_clockwise=False): Default is False. """ from pyresample.boundary import AreaBoundary - lon_sides, lat_sides = self.get_bbox_lonlats(frequency=frequency, + if frequency is not None: + warnings.warn("The `frequency` argument is pending deprecation, use `bbox_shape` instead", + PendingDeprecationWarning, stacklevel=2) + bbox_shape = bbox_shape or frequency + lon_sides, lat_sides = self.get_bbox_lonlats(bbox_shape=bbox_shape, force_clockwise=force_clockwise) return AreaBoundary.from_lonlat_sides(lon_sides, lat_sides) @@ -1565,20 +1583,20 @@ def is_geostationary(self): return False return 'geostationary' in coord_operation.method_name.lower() - def _get_geo_boundary_sides(self, frequency=None): + def _get_geo_boundary_sides(self, bbox_shape=None): """Retrieve the boundary sides list for geostationary projections.""" # Define default frequency - if frequency is None: - frequency = 50 + if bbox_shape is None: + bbox_shape = 50 # Ensure at least 4 points are used - if frequency < 4: - frequency = 4 + if bbox_shape < 4: + bbox_shape = 4 # Ensure an even number of vertices for side creation - if (frequency % 2) != 0: - frequency = frequency + 1 - lons, lats = get_geostationary_bounding_box_in_lonlats(self, nb_points=frequency) + if (bbox_shape % 2) != 0: + bbox_shape = bbox_shape + 1 + lons, lats = get_geostationary_bounding_box_in_lonlats(self, nb_points=bbox_shape) # Retrieve dummy sides for GEO (side1 and side3 always of length 2) - side02_step = int(frequency / 2) - 1 + side02_step = int(bbox_shape / 2) - 1 lon_sides = [lons[slice(0, side02_step + 1)], lons[slice(side02_step, side02_step + 1 + 1)], lons[slice(side02_step + 1, side02_step * 2 + 1 + 1)], @@ -1591,12 +1609,12 @@ def _get_geo_boundary_sides(self, frequency=None): ] return lon_sides, lat_sides - def boundary(self, frequency=None, force_clockwise=False): + def boundary(self, bbox_shape=None, force_clockwise=False, frequency=None): """Retrieve the AreaBoundary object. Parameters ---------- - frequency: + bbox_shape(formerly frequency): The number of points to provide for each side. By default (None) the full width and height will be provided, except for geostationary projection where by default only 50 points are selected. @@ -1610,10 +1628,14 @@ def boundary(self, frequency=None, force_clockwise=False): Default is False. """ from pyresample.boundary import AreaBoundary + if frequency is not None: + warnings.warn("The `frequency` argument is pending deprecation, use `bbox_shape` instead", + PendingDeprecationWarning, stacklevel=2) + bbox_shape = bbox_shape or frequency if self.is_geostationary: - lon_sides, lat_sides = self._get_geo_boundary_sides(frequency=frequency) + lon_sides, lat_sides = self._get_geo_boundary_sides(bbox_shape=bbox_shape) else: - lon_sides, lat_sides = self.get_bbox_lonlats(frequency=frequency, + lon_sides, lat_sides = self.get_bbox_lonlats(bbox_shape=bbox_shape, force_clockwise=force_clockwise) boundary = AreaBoundary.from_lonlat_sides(lon_sides, lat_sides) return boundary From 1f99e80c281cddf7c1d5c3ceb5bebd78a533291f Mon Sep 17 00:00:00 2001 From: Martin Raspaud Date: Fri, 7 Jul 2023 14:21:06 +0200 Subject: [PATCH 2/4] Rename bbox_shape to vertices_per_side --- pyresample/geometry.py | 82 ++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/pyresample/geometry.py b/pyresample/geometry.py index d2e483457..d478527c1 100644 --- a/pyresample/geometry.py +++ b/pyresample/geometry.py @@ -296,14 +296,16 @@ def get_boundary_lonlats(self): return (SimpleBoundary(s1_lon.squeeze(), s2_lon.squeeze(), s3_lon.squeeze(), s4_lon.squeeze()), SimpleBoundary(s1_lat.squeeze(), s2_lat.squeeze(), s3_lat.squeeze(), s4_lat.squeeze())) - def get_bbox_lonlats(self, bbox_shape: Optional[int] = None, force_clockwise: bool = True, + def get_bbox_lonlats(self, vertices_per_side: Optional[int] = None, force_clockwise: bool = True, frequency: Optional[int] = None) -> tuple: """Return the bounding box lons and lats. Args: - bbox_shape (formerly frequency): + vertices_per_side: The number of points to provide for each side. By default (None) the full width and height will be provided. + frequency: + Deprecated, use vertices_per_side force_clockwise: Perform minimal checks and reordering of coordinates to ensure that the returned coordinates follow a clockwise direction. @@ -330,18 +332,18 @@ def get_bbox_lonlats(self, bbox_shape: Optional[int] = None, force_clockwise: bo """ if frequency is not None: - warnings.warn("The `frequency` argument is pending deprecation, use `bbox_shape` instead", + warnings.warn("The `frequency` argument is pending deprecation, use `vertices_per_side` instead", PendingDeprecationWarning, stacklevel=2) - bbox_shape = bbox_shape or frequency - lons, lats = self._get_bbox_elements(self.get_lonlats, bbox_shape) + vertices_per_side = vertices_per_side or frequency + lons, lats = self._get_bbox_elements(self.get_lonlats, vertices_per_side) if force_clockwise and not self._corner_is_clockwise( lons[0][-2], lats[0][-2], lons[0][-1], lats[0][-1], lons[1][1], lats[1][1]): # going counter-clockwise lons, lats = self._reverse_boundaries(lons, lats) return lons, lats - def _get_bbox_elements(self, coord_fun, bbox_shape: Optional[int] = None) -> tuple: - s1_slice, s2_slice, s3_slice, s4_slice = self._get_bbox_slices(bbox_shape) + def _get_bbox_elements(self, coord_fun, vertices_per_side: Optional[int] = None) -> tuple: + s1_slice, s2_slice, s3_slice, s4_slice = self._get_bbox_slices(vertices_per_side) s1_dim1, s1_dim2 = coord_fun(data_slice=s1_slice) s2_dim1, s2_dim2 = coord_fun(data_slice=s2_slice) s3_dim1, s3_dim2 = coord_fun(data_slice=s3_slice) @@ -370,14 +372,14 @@ def _filter_bbox_nans( new_dim2_sides.append(dim2_side[is_valid_mask]) return new_dim1_sides, new_dim2_sides - def _get_bbox_slices(self, bbox_shape): + def _get_bbox_slices(self, vertices_per_side): height, width = self.shape - if bbox_shape is None: + if vertices_per_side is None: row_num = height col_num = width else: - row_num = bbox_shape - col_num = bbox_shape + row_num = vertices_per_side + col_num = vertices_per_side s1_slice = (0, np.linspace(0, width - 1, col_num, dtype=int)) s2_slice = (np.linspace(0, height - 1, row_num, dtype=int), -1) s3_slice = (-1, np.linspace(width - 1, 0, col_num, dtype=int)) @@ -420,33 +422,33 @@ def _corner_is_clockwise(lon1, lat1, corner_lon, corner_lat, lon2, lat2): is_clockwise = -np.pi < angle < 0 return is_clockwise - def get_edge_lonlats(self, bbox_shape=None, frequency=None): + def get_edge_lonlats(self, vertices_per_side=None, frequency=None): """Get the concatenated boundary of the current swath.""" if frequency is not None: - warnings.warn("The `frequency` argument is pending deprecation, use `bbox_shape` instead", + warnings.warn("The `frequency` argument is pending deprecation, use `vertices_per_side` instead", PendingDeprecationWarning, stacklevel=2) - bbox_shape = bbox_shape or frequency - lons, lats = self.get_bbox_lonlats(bbox_shape=bbox_shape, force_clockwise=False) + vertices_per_side = vertices_per_side or frequency + lons, lats = self.get_bbox_lonlats(vertices_per_side=vertices_per_side, force_clockwise=False) blons = np.ma.concatenate(lons) blats = np.ma.concatenate(lats) return blons, blats - def get_edge_bbox_in_projection_coordinates(self, bbox_shape: Optional[int] = None, + def get_edge_bbox_in_projection_coordinates(self, vertices_per_side: Optional[int] = None, frequency: Optional[int] = None): """Return the bounding box in projection coordinates.""" if frequency is not None: - warnings.warn("The `frequency` argument is pending deprecation, use `bbox_shape` instead", + warnings.warn("The `frequency` argument is pending deprecation, use `vertices_per_side` instead", PendingDeprecationWarning, stacklevel=2) - bbox_shape = bbox_shape or frequency - x, y = self._get_bbox_elements(self.get_proj_coords, bbox_shape) + vertices_per_side = vertices_per_side or frequency + x, y = self._get_bbox_elements(self.get_proj_coords, vertices_per_side) return np.hstack(x), np.hstack(y) - def boundary(self, bbox_shape=None, force_clockwise=False, frequency=None): + def boundary(self, vertices_per_side=None, force_clockwise=False, frequency=None): """Retrieve the AreaBoundary object. Parameters ---------- - bbox_shape (formerly frequency): + vertices_per_side (formerly frequency): The number of points to provide for each side. By default (None) the full width and height will be provided. force_clockwise: @@ -460,10 +462,10 @@ def boundary(self, bbox_shape=None, force_clockwise=False, frequency=None): """ from pyresample.boundary import AreaBoundary if frequency is not None: - warnings.warn("The `frequency` argument is pending deprecation, use `bbox_shape` instead", + warnings.warn("The `frequency` argument is pending deprecation, use `vertices_per_side` instead", PendingDeprecationWarning, stacklevel=2) - bbox_shape = bbox_shape or frequency - lon_sides, lat_sides = self.get_bbox_lonlats(bbox_shape=bbox_shape, + vertices_per_side = vertices_per_side or frequency + lon_sides, lat_sides = self.get_bbox_lonlats(vertices_per_side=vertices_per_side, force_clockwise=force_clockwise) return AreaBoundary.from_lonlat_sides(lon_sides, lat_sides) @@ -1583,20 +1585,20 @@ def is_geostationary(self): return False return 'geostationary' in coord_operation.method_name.lower() - def _get_geo_boundary_sides(self, bbox_shape=None): + def _get_geo_boundary_sides(self, vertices_per_side=None): """Retrieve the boundary sides list for geostationary projections.""" # Define default frequency - if bbox_shape is None: - bbox_shape = 50 + if vertices_per_side is None: + vertices_per_side = 50 # Ensure at least 4 points are used - if bbox_shape < 4: - bbox_shape = 4 + if vertices_per_side < 4: + vertices_per_side = 4 # Ensure an even number of vertices for side creation - if (bbox_shape % 2) != 0: - bbox_shape = bbox_shape + 1 - lons, lats = get_geostationary_bounding_box_in_lonlats(self, nb_points=bbox_shape) + if (vertices_per_side % 2) != 0: + vertices_per_side = vertices_per_side + 1 + lons, lats = get_geostationary_bounding_box_in_lonlats(self, nb_points=vertices_per_side) # Retrieve dummy sides for GEO (side1 and side3 always of length 2) - side02_step = int(bbox_shape / 2) - 1 + side02_step = int(vertices_per_side / 2) - 1 lon_sides = [lons[slice(0, side02_step + 1)], lons[slice(side02_step, side02_step + 1 + 1)], lons[slice(side02_step + 1, side02_step * 2 + 1 + 1)], @@ -1609,15 +1611,17 @@ def _get_geo_boundary_sides(self, bbox_shape=None): ] return lon_sides, lat_sides - def boundary(self, bbox_shape=None, force_clockwise=False, frequency=None): + def boundary(self, vertices_per_side=None, force_clockwise=False, frequency=None): """Retrieve the AreaBoundary object. Parameters ---------- - bbox_shape(formerly frequency): + vertices_per_side: The number of points to provide for each side. By default (None) the full width and height will be provided, except for geostationary projection where by default only 50 points are selected. + frequency: + Deprecated, use vertices_per_side force_clockwise: Perform minimal checks and reordering of coordinates to ensure that the returned coordinates follow a clockwise direction. @@ -1629,13 +1633,13 @@ def boundary(self, bbox_shape=None, force_clockwise=False, frequency=None): """ from pyresample.boundary import AreaBoundary if frequency is not None: - warnings.warn("The `frequency` argument is pending deprecation, use `bbox_shape` instead", + warnings.warn("The `frequency` argument is pending deprecation, use `vertices_per_side` instead", PendingDeprecationWarning, stacklevel=2) - bbox_shape = bbox_shape or frequency + vertices_per_side = vertices_per_side or frequency if self.is_geostationary: - lon_sides, lat_sides = self._get_geo_boundary_sides(bbox_shape=bbox_shape) + lon_sides, lat_sides = self._get_geo_boundary_sides(vertices_per_side=vertices_per_side) else: - lon_sides, lat_sides = self.get_bbox_lonlats(bbox_shape=bbox_shape, + lon_sides, lat_sides = self.get_bbox_lonlats(vertices_per_side=vertices_per_side, force_clockwise=force_clockwise) boundary = AreaBoundary.from_lonlat_sides(lon_sides, lat_sides) return boundary From e8177002451944e49e8450b01314bf1d2e9b90b7 Mon Sep 17 00:00:00 2001 From: Martin Raspaud Date: Mon, 14 Aug 2023 08:56:27 +0200 Subject: [PATCH 3/4] Fix docstring --- pyresample/geometry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyresample/geometry.py b/pyresample/geometry.py index d478527c1..9ca992585 100644 --- a/pyresample/geometry.py +++ b/pyresample/geometry.py @@ -448,8 +448,8 @@ def boundary(self, vertices_per_side=None, force_clockwise=False, frequency=None Parameters ---------- - vertices_per_side (formerly frequency): - The number of points to provide for each side. By default (None) + vertices_per_side: + (formerly `frequency`) The number of points to provide for each side. By default (None) the full width and height will be provided. force_clockwise: Perform minimal checks and reordering of coordinates to ensure From bea20503be3a8d46888cd4e5cdb007c66ae7f839 Mon Sep 17 00:00:00 2001 From: Martin Raspaud Date: Mon, 14 Aug 2023 08:57:24 +0200 Subject: [PATCH 4/4] Make `boundary` arguments keywords only --- pyresample/geometry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyresample/geometry.py b/pyresample/geometry.py index 9ca992585..ac0678efe 100644 --- a/pyresample/geometry.py +++ b/pyresample/geometry.py @@ -1611,7 +1611,7 @@ def _get_geo_boundary_sides(self, vertices_per_side=None): ] return lon_sides, lat_sides - def boundary(self, vertices_per_side=None, force_clockwise=False, frequency=None): + def boundary(self, *, vertices_per_side=None, force_clockwise=False, frequency=None): """Retrieve the AreaBoundary object. Parameters