diff --git a/city_metrix/layers/albedo.py b/city_metrix/layers/albedo.py index 2577c70..5cb7aaa 100644 --- a/city_metrix/layers/albedo.py +++ b/city_metrix/layers/albedo.py @@ -2,7 +2,7 @@ import xarray from dask.diagnostics import ProgressBar -from .layer import Layer, get_utm_zone_epsg, get_image_collection, set_resampling_method +from .layer import Layer, get_utm_zone_epsg, get_image_collection, set_resampling_for_continuous_raster class Albedo(Layer): @@ -15,8 +15,8 @@ class Albedo(Layer): threshold: threshold value for filtering the retrieval """ - def __init__(self, start_date="2021-01-01", end_date="2022-01-01", spatial_resolution=10, - resampling_method='bilinear', threshold=None, **kwargs): + def __init__(self, start_date="2021-01-01", end_date="2022-01-01", spatial_resolution:int=10, + resampling_method:str='bilinear', threshold=None, **kwargs): super().__init__(**kwargs) self.start_date = start_date self.end_date = end_date @@ -24,7 +24,7 @@ def __init__(self, start_date="2021-01-01", end_date="2022-01-01", spatial_resol self.resampling_method = resampling_method self.threshold = threshold - def get_data(self, bbox): + def get_data(self, bbox: tuple[float, float, float, float]): S2 = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED") S2C = ee.ImageCollection("COPERNICUS/S2_CLOUD_PROBABILITY") @@ -121,15 +121,17 @@ def calc_s2_albedo(image): ## S2 MOSAIC AND ALBEDO dataset = get_masked_s2_collection(ee.Geometry.BBox(*bbox), self.start_date, self.end_date) s2_albedo = dataset.map(calc_s2_albedo) - if self.resampling_method is not None: - albedo_mean = (s2_albedo - .map(lambda x: set_resampling_method(x, self.resampling_method)) - .reduce(ee.Reducer.mean()) - ) - else: - albedo_mean = (s2_albedo - .reduce(ee.Reducer.mean()) - ) + + albedo_mean = (s2_albedo + .map(lambda x: + set_resampling_for_continuous_raster(x, + self.resampling_method, + self.spatial_resolution, + bbox + ) + ) + .reduce(ee.Reducer.mean()) + ) albedo_mean_ic = ee.ImageCollection(albedo_mean) data = get_image_collection( diff --git a/city_metrix/layers/alos_dsm.py b/city_metrix/layers/alos_dsm.py index 6b9ceff..f9601eb 100644 --- a/city_metrix/layers/alos_dsm.py +++ b/city_metrix/layers/alos_dsm.py @@ -3,7 +3,7 @@ import xarray as xr -from .layer import Layer, get_image_collection, set_resampling_method +from .layer import Layer, get_image_collection, set_resampling_for_continuous_raster class AlosDSM(Layer): @@ -13,29 +13,28 @@ class AlosDSM(Layer): resampling_method: interpolation method used by Google Earth Engine. Default is 'bilinear'. All options are: ('bilinear', 'bicubic', None). """ - def __init__(self, spatial_resolution=30, resampling_method='bilinear', **kwargs): + def __init__(self, spatial_resolution:int=30, resampling_method:str='bilinear', **kwargs): super().__init__(**kwargs) self.spatial_resolution = spatial_resolution self.resampling_method = resampling_method - def get_data(self, bbox): + def get_data(self, bbox: tuple[float, float, float, float]): alos_dsm = ee.ImageCollection("JAXA/ALOS/AW3D30/V3_2") - if self.resampling_method is not None: - alos_dsm_ic = ee.ImageCollection( - alos_dsm - .filterBounds(ee.Geometry.BBox(*bbox)) - .map(lambda x: set_resampling_method(x, self.resampling_method), ) - .select('DSM') - .mean() - ) - else: - alos_dsm_ic = ee.ImageCollection( - alos_dsm - .filterBounds(ee.Geometry.BBox(*bbox)) - .select('DSM') - .mean() - ) + alos_dsm_ic = ee.ImageCollection( + alos_dsm + .filterBounds(ee.Geometry.BBox(*bbox)) + .select('DSM') + .map(lambda x: + set_resampling_for_continuous_raster(x, + self.resampling_method, + self.spatial_resolution, + bbox + ) + ) + .mean() + ) + data = get_image_collection( alos_dsm_ic, diff --git a/city_metrix/layers/layer.py b/city_metrix/layers/layer.py index 57341d3..635ec25 100644 --- a/city_metrix/layers/layer.py +++ b/city_metrix/layers/layer.py @@ -325,14 +325,25 @@ def get_stats_funcs(stats_func): return [stats_func] -def set_resampling_method(image: ee.Image, resampling_method: str): - valid_raster_resampling_methods = ['bilinear', 'bicubic'] +def set_resampling_for_continuous_raster(image: ee.Image, resampling_method: str, resolution: int, + bbox: tuple[float, float, float, float]): + """ + Function sets the resampling method on the GEE query dictionary for use on continuous raster layers. + GEE only supports bilinear and bicubic interpolation methods. + """ + valid_raster_resampling_methods = ['bilinear', 'bicubic', None] if resampling_method not in valid_raster_resampling_methods: raise ValueError(f"Invalid resampling method ('{resampling_method}'). " f"Valid methods: {valid_raster_resampling_methods}") - data = image.resample(resampling_method) + if resampling_method is None: + data = image + else: + crs = get_utm_zone_epsg(bbox) + data = (image + .resample(resampling_method) + .reproject(crs=crs, scale=resolution)) return data diff --git a/city_metrix/layers/nasa_dem.py b/city_metrix/layers/nasa_dem.py index 006347a..8f065cc 100644 --- a/city_metrix/layers/nasa_dem.py +++ b/city_metrix/layers/nasa_dem.py @@ -2,7 +2,7 @@ import xee import xarray as xr -from .layer import Layer, get_image_collection, set_resampling_method +from .layer import Layer, get_image_collection, set_resampling_for_continuous_raster class NasaDEM(Layer): @@ -12,27 +12,26 @@ class NasaDEM(Layer): resampling_method: interpolation method used by Google Earth Engine. Default is 'bilinear'. All options are: ('bilinear', 'bicubic', None). """ - def __init__(self, spatial_resolution=30, resampling_method='bilinear', **kwargs): + def __init__(self, spatial_resolution:int=30, resampling_method:str='bilinear', **kwargs): super().__init__(**kwargs) self.spatial_resolution = spatial_resolution self.resampling_method = resampling_method - def get_data(self, bbox): + def get_data(self, bbox: tuple[float, float, float, float]): nasa_dem = ee.Image("NASA/NASADEM_HGT/001") - if self.resampling_method is not None: - nasa_dem_elev = (ee.ImageCollection(nasa_dem) - .filterBounds(ee.Geometry.BBox(*bbox)) - .map(lambda x: set_resampling_method(x, self.resampling_method), ) - .select('elevation') - .mean() - ) - else: - nasa_dem_elev = (ee.ImageCollection(nasa_dem) - .filterBounds(ee.Geometry.BBox(*bbox)) - .select('elevation') - .mean() - ) + nasa_dem_elev = (ee.ImageCollection(nasa_dem) + .filterBounds(ee.Geometry.BBox(*bbox)) + .select('elevation') + .map(lambda x: + set_resampling_for_continuous_raster(x, + self.resampling_method, + self.spatial_resolution, + bbox + ) + ) + .mean() + ) nasa_dem_elev_ic = ee.ImageCollection(nasa_dem_elev) data = get_image_collection( diff --git a/tests/resources/layer_dumps/test_write_layers_using_fixed_resolution.py b/tests/resources/layer_dumps/test_write_layers_using_fixed_resolution.py index d0998fd..bf19991 100644 --- a/tests/resources/layer_dumps/test_write_layers_using_fixed_resolution.py +++ b/tests/resources/layer_dumps/test_write_layers_using_fixed_resolution.py @@ -7,6 +7,7 @@ TARGET_RESOLUTION = 5 TARGET_RESAMPLING_METHOD = 'bilinear' +# TARGET_RESAMPLING_METHOD = None @pytest.mark.skipif(RUN_DUMPS == False, reason='Skipping since RUN_DUMPS set to False') def test_write_albedo_fixed_res(target_folder, bbox_info, target_spatial_resolution_multiplier): diff --git a/tests/test_layer_metrics.py b/tests/test_layer_metrics.py index 78d924d..94d558a 100644 --- a/tests/test_layer_metrics.py +++ b/tests/test_layer_metrics.py @@ -53,7 +53,7 @@ def test_albedo_metrics_default_resampling(): # Bounding values expected_min_value = _convert_fraction_to_rounded_percent(0.03) - expected_max_value = _convert_fraction_to_rounded_percent(0.32) + expected_max_value = _convert_fraction_to_rounded_percent(0.34) actual_min_value = _convert_fraction_to_rounded_percent(data.values.min()) actual_max_value = _convert_fraction_to_rounded_percent(data.values.max())