diff --git a/ecoscope/analysis/seasons.py b/ecoscope/analysis/seasons.py index d6c3a87e..73f87ab0 100644 --- a/ecoscope/analysis/seasons.py +++ b/ecoscope/analysis/seasons.py @@ -19,11 +19,11 @@ def _min_max_scaler(x): return x_std -def std_ndvi_vals(aoi=None, img_coll=None, band=None, img_scale=1, start=None, end=None): +def std_ndvi_vals(aoi=None, img_coll=None, nir_band=None, red_band=None, img_scale=1, start=None, end=None): coll = ( ee.ImageCollection(img_coll) - .select(band) + .select([nir_band, red_band]) .filterDate(start, end) .map(lambda x: x.multiply(ee.Image(img_scale)).set("system:time_start", x.get("system:time_start"))) ) @@ -33,11 +33,14 @@ def std_ndvi_vals(aoi=None, img_coll=None, band=None, img_scale=1, start=None, e else: geo = None + img_dates = pandas.to_datetime(coll.aggregate_array("system:time_start").getInfo(), unit="ms", utc=True) + + coll = coll.map(lambda x: x.normalizedDifference([nir_band, red_band])) ndvi_vals = coll.toBands().reduceRegion("mean", geo, bestEffort=True).values().getInfo() df = pandas.DataFrame( { - "img_date": pandas.to_datetime(coll.aggregate_array("system:time_start").getInfo(), unit="ms", utc=True), + "img_date": img_dates, "NDVI": ndvi_vals, } ).dropna(axis=0) @@ -110,13 +113,13 @@ def seasonal_windows(ndvi_vals, cuts, season_labels): def add_seasonal_index( - df, index_name, start_date, end_date, aoi_geom_filter=None, seasons=2, season_labels=["dry", "wet"] + df, index_name, start_date, end_date, time_col, aoi_geom_filter=None, seasons=2, season_labels=["dry", "wet"] ): aoi_ = None try: aoi_ = aoi_geom_filter.dissolve().iloc[0]["geometry"] - except: + except AttributeError: aoi_ = aoi_geom_filter if len(season_labels) != seasons: @@ -124,7 +127,7 @@ def add_seasonal_index( f"Parameter value 'seasons' ({seasons}) must match the number of 'season_labels' elements ({season_labels})" ) # extract the standardized NDVI ndvi_vals within the AOI - ndvi_vals = std_ndvi_vals(aoi_, start=since_filter.isoformat(), end=until_filter.isoformat()) + ndvi_vals = std_ndvi_vals(aoi_, start=start_date.isoformat(), end=end_date.isoformat()) # calculate the seasonal transition point cuts = val_cuts(ndvi_vals, seasons) diff --git a/notebooks/05. Environmental Analyses/Seasonal Calculation.ipynb b/notebooks/05. Environmental Analyses/Seasonal Calculation.ipynb index 349585bc..a021513a 100644 --- a/notebooks/05. Environmental Analyses/Seasonal Calculation.ipynb +++ b/notebooks/05. Environmental Analyses/Seasonal Calculation.ipynb @@ -155,15 +155,16 @@ "metadata": {}, "outputs": [], "source": [ - "img_coll = \"MODIS/061/MOD13A1\"\n", + "img_coll = \"MODIS/061/MCD43A4\"\n", "band = \"NDVI\"\n", - "img_scale = 0.0001\n", - "since_filter = \"2010-01-01\"\n", + "img_scale = 1\n", + "since_filter = \"2020-01-01\"\n", "until_filter = \"2022-06-18\"\n", "\n", "ndvi_vals = seasons.std_ndvi_vals(\n", " img_coll=img_coll,\n", - " band=band,\n", + " nir_band=\"Nadir_Reflectance_Band2\",\n", + " red_band=\"Nadir_Reflectance_Band1\",\n", " img_scale=img_scale,\n", " aoi=aoi,\n", " start=since_filter,\n", diff --git a/tests/test_seasons.py b/tests/test_seasons.py index f2a547d9..a39c4051 100644 --- a/tests/test_seasons.py +++ b/tests/test_seasons.py @@ -10,14 +10,21 @@ ) -@pytest.mark.skip(reason="this has been failing since May 2022; will be fixed in a follow-up pull") +@pytest.mark.skipif(not pytest.earthengine, reason="No connection to EarthEngine.") def test_seasons(): gdf = gpd.read_file("tests/sample_data/vector/AOI_sites.gpkg").to_crs(4326) aoi = gdf.geometry.iat[0] # Extract the standardized NDVI ndvi_vals within the AOI - ndvi_vals = ecoscope.analysis.seasons.std_ndvi_vals(aoi, start="2010-01-01", end="2021-01-01") + ndvi_vals = ecoscope.analysis.seasons.std_ndvi_vals( + aoi, + img_coll="MODIS/061/MCD43A4", + nir_band="Nadir_Reflectance_Band2", + red_band="Nadir_Reflectance_Band1", + start="2010-01-01", + end="2021-01-01", + ) # Calculate the seasonal transition point cuts = ecoscope.analysis.seasons.val_cuts(ndvi_vals, 2)