From 16615427a6d9863b40441545f76cc35c96c902f8 Mon Sep 17 00:00:00 2001 From: "Rodrigo E. Principe" Date: Tue, 28 Jan 2025 20:39:34 -0300 Subject: [PATCH 1/2] fix: add an arg to indicate the property that contains the band names + test --- geetools/ee_image_collection.py | 40 ++++++++++++++++++++++----------- tests/conftest.py | 10 +++++++++ tests/test_ImageCollection.py | 12 ++++++++++ 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/geetools/ee_image_collection.py b/geetools/ee_image_collection.py index 8225654a..6005d0bf 100644 --- a/geetools/ee_image_collection.py +++ b/geetools/ee_image_collection.py @@ -793,12 +793,18 @@ def validPixel(self, band: str | ee.String = "") -> ee.Image: validPct = validPixel.divide(self._obj.size()).multiply(100).rename("pct_valid") return validPixel.addBands(validPct) - def containsBandNames(self, bandNames: list[str] | ee.List, filter: str) -> ee.ImageCollection: + def containsBandNames( + self, + bandNames: list[str] | ee.List, + filter: str, + bandNamesProperty: str | ee.String = "system:band_names", + ) -> ee.ImageCollection: """Filter the :py:class:`ee.ImageCollection` by band names using the provided filter. Args: bandNames: List of band names to filter. filter: Type of filter to apply. To keep images that contains all the specified bands use ``"ALL"``. To get the images including at least one of the specified band use ``"ANY"``. + bandNamesProperty: the name of the property that contains the band names. Defaults to 'system:band_name' because GEE stores them there. Returns: A filtered :py:class:`ee.ImageCollection` @@ -821,31 +827,34 @@ def containsBandNames(self, bandNames: list[str] | ee.List, filter: str) -> ee.I filter = {"ALL": "Filter.and", "ANY": "Filter.or"}[filter] bandNames = ee.List(bandNames) - # add bands as metadata in a temporary property - band_name = uuid.uuid4().hex - ic = self._obj.map(lambda i: i.set(band_name, i.bandNames())) - # create a filter by combining a listContain filter over all the band names from the # user list. Combine them with a "Or" to get a "any" filter and "And" to get a "all". # We use a workaround until this is solved: https://issuetracker.google.com/issues/322838709 - filterList = bandNames.map(lambda b: ee.Filter.listContains(band_name, b)) + filterList = bandNames.map(lambda b: ee.Filter.listContains(bandNamesProperty, b)) filterCombination = apifunction.ApiFunction.call_(filter, ee.List(filterList)) # apply this filter and remove the temporary property. Exclude parameter is additive so # we do a blank multiplication to remove all the properties beforhand - ic = ee.ImageCollection(ic.filter(filterCombination)) - ic = ic.map(lambda i: ee.Image(i.multiply(1).copyProperties(i, exclude=[band_name]))) + ic = ee.ImageCollection(self._obj.filter(filterCombination)) + ic = ic.map( + lambda i: ee.Image(i.multiply(1).copyProperties(i, exclude=[bandNamesProperty])) + ) return ee.ImageCollection(ic) - def containsAllBands(self, bandNames: list[str] | ee.List) -> ee.ImageCollection: + def containsAllBands( + self, + bandNames: list[str] | ee.List, + bandNamesProperty: str | ee.String = "system:band_names", + ) -> ee.ImageCollection: """Filter the :py:class:`ee.ImageCollection` keeping only the images with all the provided bands. Args: bandNames: List of band names to filter. + bandNamesProperty: the name of the property that contains the band names. Defaults to 'system:band_name' because GEE stores them there. Returns: - A filtered :py:class:`ee.ImageCollection` + A filtered :py:class:`ee.ImageCollection`. Examples: .. code-block:: @@ -863,13 +872,18 @@ def containsAllBands(self, bandNames: list[str] | ee.List) -> ee.ImageCollection filtered = collection.geetools.containsAllBands(["B1", "B2"]) print(filtered.getInfo()) """ - return self.containsBandNames(bandNames, "ALL") + return self.containsBandNames(bandNames, "ALL", bandNamesProperty) - def containsAnyBands(self, bandNames: list[str] | ee.List) -> ee.ImageCollection: + def containsAnyBands( + self, + bandNames: list[str] | ee.List, + bandNamesProperty: str | ee.String = "system:band_names", + ) -> ee.ImageCollection: """Filter the :py:class:`ee.ImageCollection` keeping only the images with any of the provided bands. Args: bandNames: List of band names to filter. + bandNamesProperty: the name of the property that contains the band names. Defaults to 'system:band_name' because GEE stores them there. Returns: A filtered :py:class:`ee.ImageCollection` @@ -890,7 +904,7 @@ def containsAnyBands(self, bandNames: list[str] | ee.List) -> ee.ImageCollection filtered = collection.geetools.containsAnyBands(["B1", "B2"]) print(filtered.getInfo()) """ - return self.containsBandNames(bandNames, "ANY") + return self.containsBandNames(bandNames, "ANY", bandNamesProperty) def aggregateArray(self, properties: list[str] | ee.List | None = None) -> ee.Dictionary: """Aggregate the :py:class:`ee.ImageCollection` selected properties into a dictionary. diff --git a/tests/conftest.py b/tests/conftest.py index c71f89d4..87356da9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -77,6 +77,16 @@ def s2_sr(amazonas) -> ee.ImageCollection: ) +@pytest.fixture +def aster(vatican) -> ee.ImageCollection: + """Aster collection in Vatican City for year 2020.""" + return ( + ee.ImageCollection("ASTER/AST_L1T_003") + .filterBounds(vatican.geometry()) + .filterDate("2020-01-01", "2021-01-01") + ) + + @pytest.fixture def vatican(): """Return the vatican city.""" diff --git a/tests/test_ImageCollection.py b/tests/test_ImageCollection.py index 39d48e3f..d59f139c 100644 --- a/tests/test_ImageCollection.py +++ b/tests/test_ImageCollection.py @@ -238,6 +238,12 @@ def test_contains_all(self, s2_sr): ic = ic.geetools.containsAllBands(["B2", "B3"]) assert ic.size().getInfo() == 2449 + def test_contains_all_property_name(self, aster): + ic = aster.geetools.containsAllBands( + ["B3N", "B02", "B01"], bandNamesProperty="ORIGINAL_BANDS_PRESENT" + ) + assert ic.size().getInfo() == 2 + def test_contains_all_mismatch(self, s2_sr): ic = s2_sr.select(["B2", "B3", "B4"]) ic = ic.geetools.containsAllBands(["B2", "B3", "B5"]) @@ -248,6 +254,12 @@ def test_contains_any(self, s2_sr): ic = ic.geetools.containsAnyBands(["B2", "B3", "B5"]) assert ic.size().getInfo() == 2449 + def test_contains_any_property_name(self, aster): + ic = aster.geetools.containsAnyBands( + ["B3N", "B02", "B01"], bandNamesProperty="ORIGINAL_BANDS_PRESENT" + ) + assert ic.size().getInfo() == 2 + def test_contains_any_mismatch(self, s2_sr): ic = s2_sr.select(["B2", "B3", "B4"]) ic = ic.geetools.containsAnyBands(["B5", "B6"]) From 656f5c07be15725b87dab0469b65066fa74f36da Mon Sep 17 00:00:00 2001 From: "Rodrigo E. Principe" Date: Tue, 4 Feb 2025 17:32:30 -0300 Subject: [PATCH 2/2] fix: docstring as PR's suggestion --- geetools/ee_image_collection.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/geetools/ee_image_collection.py b/geetools/ee_image_collection.py index 6005d0bf..b108f717 100644 --- a/geetools/ee_image_collection.py +++ b/geetools/ee_image_collection.py @@ -804,7 +804,7 @@ def containsBandNames( Args: bandNames: List of band names to filter. filter: Type of filter to apply. To keep images that contains all the specified bands use ``"ALL"``. To get the images including at least one of the specified band use ``"ANY"``. - bandNamesProperty: the name of the property that contains the band names. Defaults to 'system:band_name' because GEE stores them there. + bandNamesProperty: the name of the property that contains the band names. Defaults to GEE native default: 'system:band_name'. Returns: A filtered :py:class:`ee.ImageCollection` @@ -851,7 +851,7 @@ def containsAllBands( Args: bandNames: List of band names to filter. - bandNamesProperty: the name of the property that contains the band names. Defaults to 'system:band_name' because GEE stores them there. + bandNamesProperty: the name of the property that contains the band names. Defaults to GEE native default: 'system:band_name'. Returns: A filtered :py:class:`ee.ImageCollection`. @@ -883,7 +883,7 @@ def containsAnyBands( Args: bandNames: List of band names to filter. - bandNamesProperty: the name of the property that contains the band names. Defaults to 'system:band_name' because GEE stores them there. + bandNamesProperty: the name of the property that contains the band names. Defaults to GEE native default: 'system:band_name'. Returns: A filtered :py:class:`ee.ImageCollection`