diff --git a/geetools/ee_image_collection.py b/geetools/ee_image_collection.py index c30abcc8..51f605be 100644 --- a/geetools/ee_image_collection.py +++ b/geetools/ee_image_collection.py @@ -989,11 +989,7 @@ def groupInterval(self, unit: str = "month", duration: int = 1) -> ee.List: print(split.getInfo()) """ sizeName = "__geetools_generated_size__" # set generated properties name - - # create an ic variable to avoid calling self._obj multiple times - # and extract the property names to copy ic = self._obj - toCopy = ic.first().propertyNames() # transform the interval into a duration in milliseconds # I can use the DateRangeAccessor as it's imported earlier in the __init__.py file @@ -1008,13 +1004,7 @@ def add_size(ic): ic = ee.ImageCollection(ic) return ic.set({sizeName: ic.size()}) - def delete_size_property(ic): - ic = ee.ImageCollection(ic) - return ee.ImageCollection(ic.copyProperties(ic, properties=toCopy)) - - imageCollectionList = ( - imageCollectionList.map(add_size).filter(ee.Filter.gt(sizeName, 0)).map(delete_size_property) - ) + imageCollectionList = imageCollectionList.map(add_size).filter(ee.Filter.gt(sizeName, 0)) return ee.List(imageCollectionList) @@ -1024,6 +1014,7 @@ def reduceInterval( unit: str = "month", duration: int = 1, keep_original_names: bool = True, + count_images_per_interval: bool = False, ) -> ee.ImageCollection: """Reduce the images included in the same duration interval using the provided reducer. @@ -1037,6 +1028,7 @@ def reduceInterval( unit: The unit of time to split the collection. Available units: ``year``, ``month``, ``week``, ``day``, ``hour``, ``minute`` or ``second``. duration: The duration of each split. keep_original_names: Whether to keep the original band names or not. This is a workaround to preserve older behaviour, it should disappear in the future. + count_images_per_interval: Whether to keep the property `__geetools_generated_size__` with the number of images used for the reduction in each interval. Returns: A new :py:class:`ee.ImageCollection` with the reduced images. @@ -1057,6 +1049,9 @@ def reduceInterval( reduced = collection.geetools.reduceInterval("mean", "month", 1) print(reduced.getInfo()) """ + # Default property name that keeps the number of images used for the reduction in each interval + sizeName = "__geetools_generated_size__" + # create a list of image collections to be reduced # Every subcollection is sorted in case one use the "first" reducer imageCollectionList = self.groupInterval(unit, duration) @@ -1089,13 +1084,19 @@ def reduce(ic): ic = ee.ImageCollection(ic) start = ic.aggregate_min("system:time_start") end = ic.aggregate_max("system:time_end") + count = ic.get(sizeName) firstImg = ic.first() propertyNames = firstImg.propertyNames() image = ic.reduce(red).rename(bandNames).copyProperties(firstImg, propertyNames) - return image.set("system:time_start", start, "system:time_end", end) + return image.set("system:time_start", start, "system:time_end", end, sizeName, count) reducedImagesList = imageCollectionList.map(reduce) + if not count_images_per_interval: + reducedImagesList = reducedImagesList.map( + lambda i: ee.Image(i).copyProperties(i, exclude=[sizeName]) + ) + # set back the original properties propertyNames = self._obj.propertyNames() ic = ee.ImageCollection(reducedImagesList).copyProperties(self._obj, propertyNames) diff --git a/tests/test_ImageCollection.py b/tests/test_ImageCollection.py index 6bdc48b5..2f5fa731 100644 --- a/tests/test_ImageCollection.py +++ b/tests/test_ImageCollection.py @@ -11,6 +11,8 @@ from jsonschema import validate from matplotlib import pyplot as plt +import geetools as geetools + def reduce( collection: ee.ImageCollection, geometry: ee.Geometry | None = None, reducer: str = "first" @@ -373,6 +375,32 @@ def test_reduce_interval_image_with_system_id(self, s2_sr): firstImg = ic.first() assert "system:id" in firstImg.propertyNames().getInfo() + def test_reduce_interval_without_count_images_per_interval(self, jaxa_rainfall): + # get 3 month worth of data and group it with default parameters + ic = jaxa_rainfall.filterDate("2020-01-01", "2020-01-02") + reduced = ic.geetools.reduceInterval("mean", duration=1, unit="day", count_images_per_interval=False) + firstImg = reduced.first() + assert "__geetools_generated_size__" not in firstImg.propertyNames().getInfo() + + def test_reduce_interval_with_count_images_per_interval(self, jaxa_rainfall): + # get 3 month worth of data and group it with default parameters + ic = jaxa_rainfall.filterDate("2020-01-01", "2020-01-02") + reduced = ic.geetools.reduceInterval("mean", duration=1, unit="day", count_images_per_interval=True) + firstImg = reduced.first() + assert "__geetools_generated_size__" in firstImg.propertyNames().getInfo() + + def test_reduce_interval_with_count_images_per_interval_count(self, jaxa_rainfall): + ic = jaxa_rainfall.filterDate("2020-01-01", "2020-01-02") + reduced = ic.geetools.reduceInterval("mean", duration=1, unit="day", count_images_per_interval=True) + firstImg = reduced.first() + assert firstImg.get("__geetools_generated_size__").getInfo() == 24 + + def test_reduce_interval_with_count_images_per_interval_empty_days(self, s2_sr): + ic = s2_sr.filterDate("2021-01-01", "2021-01-07") + reduced = ic.geetools.reduceInterval("mean", duration=1, unit="day", count_images_per_interval=True) + count_images_per_interval = reduced.aggregate_array("__geetools_generated_size__").getInfo() + assert count_images_per_interval == [17, 8, 11] + class TestClosestDate: """Test the ``closestDate`` method."""