From 056de7d685285e507403aa43e2b3a5417f2f0bb0 Mon Sep 17 00:00:00 2001 From: "Rodrigo E. Principe" Date: Mon, 21 Oct 2024 15:44:05 -0300 Subject: [PATCH 1/7] feat: single band image from list of images + test --- geetools/Image.py | 15 +++++++++++++++ tests/test_Image.py | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/geetools/Image.py b/geetools/Image.py index 8ab03c44..8f9d3d73 100644 --- a/geetools/Image.py +++ b/geetools/Image.py @@ -1427,3 +1427,18 @@ def plot( gdf = gpd.GeoDataFrame.from_features(fc.getInfo()["features"]) gdf = gdf.set_crs("EPSG:4326").to_crs(crs) gdf.boundary.plot(ax=ax, color=color) + + @classmethod + def fromList(cls, image_list: ee_list): + """Create a single image by passing a list of images. + + Parameters: + image_list: a list of ee.Image + + Returns: + A single ee.Image with one band per image in the passed list + """ + ilist = ee.List(image_list) + i0 = ee.Image(ilist.get(0)) + rest = ee.List(ilist.slice(1)) + return i0.geetools.merge(rest) diff --git a/tests/test_Image.py b/tests/test_Image.py index 9796b003..ce9522b4 100644 --- a/tests/test_Image.py +++ b/tests/test_Image.py @@ -641,3 +641,27 @@ def test_plot_with_crs(self, s2_sr_vatican_2020, vatican, image_regression): fig.savefig(image_byte, format="png") image_byte.seek(0) image_regression.check(image_byte.getvalue()) + + +class TestFromList: + """Test ``fromList`` method.""" + + def test_from_list(self): + """This test is meant to fail.""" + esa = ee.ImageCollection("ESA/WorldCover/v200") + esai = esa.first() + values = [10, 20, 30] + names = ["trees", "shrubs", "grass"] + + def over_values_names(zipped): + z = ee.List(zipped) + value = ee.Number(z.get(0)) + name = ee.String(z.get(1)) + mask = esai.eq(value).rename(name) + return mask + + combined = ee.List(values).zip(ee.List(names)) + images = combined.map(over_values_names) + final = ee.Image.geetools.fromList(images) + bands = final.bandNames().getInfo() + assert bands == names From 38e4fbd698807eee1221bf45a6a748ff7d74c4e3 Mon Sep 17 00:00:00 2001 From: "Rodrigo E. Principe" Date: Wed, 23 Oct 2024 16:30:16 -0300 Subject: [PATCH 2/7] fix: consistent param name and typing --- geetools/Image.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/geetools/Image.py b/geetools/Image.py index 8f9d3d73..04a952f2 100644 --- a/geetools/Image.py +++ b/geetools/Image.py @@ -1429,7 +1429,7 @@ def plot( gdf.boundary.plot(ax=ax, color=color) @classmethod - def fromList(cls, image_list: ee_list): + def fromList(cls, images: ee.List | list): """Create a single image by passing a list of images. Parameters: @@ -1438,7 +1438,7 @@ def fromList(cls, image_list: ee_list): Returns: A single ee.Image with one band per image in the passed list """ - ilist = ee.List(image_list) + ilist = ee.List(images) i0 = ee.Image(ilist.get(0)) rest = ee.List(ilist.slice(1)) return i0.geetools.merge(rest) From ac184a000bdc5e9f086c5867f30b27660c99b786 Mon Sep 17 00:00:00 2001 From: "Rodrigo E. Principe" Date: Wed, 23 Oct 2024 16:31:25 -0300 Subject: [PATCH 3/7] fix: change test to more simple (and flexible) approach and add an extra test --- tests/test_Image.py | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/tests/test_Image.py b/tests/test_Image.py index ce9522b4..b473f0bb 100644 --- a/tests/test_Image.py +++ b/tests/test_Image.py @@ -646,22 +646,16 @@ def test_plot_with_crs(self, s2_sr_vatican_2020, vatican, image_regression): class TestFromList: """Test ``fromList`` method.""" - def test_from_list(self): - """This test is meant to fail.""" - esa = ee.ImageCollection("ESA/WorldCover/v200") - esai = esa.first() - values = [10, 20, 30] - names = ["trees", "shrubs", "grass"] - - def over_values_names(zipped): - z = ee.List(zipped) - value = ee.Number(z.get(0)) - name = ee.String(z.get(1)) - mask = esai.eq(value).rename(name) - return mask - - combined = ee.List(values).zip(ee.List(names)) - images = combined.map(over_values_names) - final = ee.Image.geetools.fromList(images) - bands = final.bandNames().getInfo() - assert bands == names + def test_from_list_unique(self): + """Test using a list of unique band names.""" + sequence = ee.List([1, 2, 3]) + images = sequence.map(lambda i: ee.Image(ee.Number(i)).rename(ee.Number(i).int().format())) + image = ee.Image.geetools.fromList(images) + assert image.bandNames().getInfo() == ["1", "2", "3"] + + def test_from_list_repeated(self): + """Test using a list of repeated band names.""" + sequence = ee.List([1, 2, 2, 3]) + images = sequence.map(lambda i: ee.Image(ee.Number(i)).rename(ee.Number(i).int().format())) + image = ee.Image.geetools.fromList(images) + assert image.bandNames().getInfo() == ["1", "2", "2_1", "3"] From a1e0a8f988efc8f4a199d9d69e893909ded02751 Mon Sep 17 00:00:00 2001 From: "Rodrigo E. Principe" Date: Wed, 23 Oct 2024 16:38:39 -0300 Subject: [PATCH 4/7] fix: add new test --- tests/test_Image.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_Image.py b/tests/test_Image.py index b473f0bb..eb195fd0 100644 --- a/tests/test_Image.py +++ b/tests/test_Image.py @@ -659,3 +659,14 @@ def test_from_list_repeated(self): images = sequence.map(lambda i: ee.Image(ee.Number(i)).rename(ee.Number(i).int().format())) image = ee.Image.geetools.fromList(images) assert image.bandNames().getInfo() == ["1", "2", "2_1", "3"] + + def test_from_list_multiband(self): + """Test using a list of multiband images.""" + images = ee.List( + [ + ee.Image([1, 2, 3]).rename(["1", "2", "3"]), + ee.Image([3, 4, 5]).rename(["3", "4", "5"]), + ] + ) + image = ee.Image.geetools.fromList(images) + assert image.bandNames().getInfo() == ["1", "2", "3", "3_1", "4", "5"] From d34a9378cf13bc7686d3f03a2d6ff671c9fadb49 Mon Sep 17 00:00:00 2001 From: "Rodrigo E. Principe" Date: Thu, 24 Oct 2024 10:54:58 -0300 Subject: [PATCH 5/7] fix: change approach and add examples and warnings --- geetools/Image.py | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/geetools/Image.py b/geetools/Image.py index 04a952f2..42684eda 100644 --- a/geetools/Image.py +++ b/geetools/Image.py @@ -1432,13 +1432,38 @@ def plot( def fromList(cls, images: ee.List | list): """Create a single image by passing a list of images. + Warning: The bands cannot have repeated names, if so, it will throw an error (see examples). + Parameters: - image_list: a list of ee.Image + images: a list of ee.Image Returns: A single ee.Image with one band per image in the passed list + + Examples: + .. code-block:: python + + import ee, geetools + + ee.Initialize() + + sequence = ee.List([1, 2, 3]) + images = sequence.map(lambda i: ee.Image(ee.Number(i)).rename(ee.Number(i).int().format())) + image = ee.Image.geetools.fromList(images) + print(image.bandNames().getInfo()) + + .. code-block:: python + + import ee, geetools + + ee.Initialize() + + sequence = ee.List([1, 2, 2, 3]) + images = sequence.map(lambda i: ee.Image(ee.Number(i)).rename(ee.Number(i).int().format())) + image = ee.Image.geetools.fromList(images) + print(image.bandNames().getInfo()) + > ee.ee_exception.EEException: Image.rename: Can't add a band named '2' to image because a band with this name already exists. Existing bands: [1, 2]. """ - ilist = ee.List(images) - i0 = ee.Image(ilist.get(0)) - rest = ee.List(ilist.slice(1)) - return i0.geetools.merge(rest) + bandNames = images.map(lambda i: ee.Image(i).bandNames()).flatten() + ic = ee.ImageCollection.fromImages(images) + return ic.toBands().rename(bandNames) From 251706c00f166425f8d725f4fe5afc697e567613 Mon Sep 17 00:00:00 2001 From: "Rodrigo E. Principe" Date: Thu, 24 Oct 2024 10:55:26 -0300 Subject: [PATCH 6/7] fix: remove one test that is failing with the new approach (on purpose) --- tests/test_Image.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/tests/test_Image.py b/tests/test_Image.py index eb195fd0..998904ae 100644 --- a/tests/test_Image.py +++ b/tests/test_Image.py @@ -653,20 +653,13 @@ def test_from_list_unique(self): image = ee.Image.geetools.fromList(images) assert image.bandNames().getInfo() == ["1", "2", "3"] - def test_from_list_repeated(self): - """Test using a list of repeated band names.""" - sequence = ee.List([1, 2, 2, 3]) - images = sequence.map(lambda i: ee.Image(ee.Number(i)).rename(ee.Number(i).int().format())) - image = ee.Image.geetools.fromList(images) - assert image.bandNames().getInfo() == ["1", "2", "2_1", "3"] - def test_from_list_multiband(self): """Test using a list of multiband images.""" images = ee.List( [ ee.Image([1, 2, 3]).rename(["1", "2", "3"]), - ee.Image([3, 4, 5]).rename(["3", "4", "5"]), + ee.Image([4, 5]).rename(["4", "5"]), ] ) image = ee.Image.geetools.fromList(images) - assert image.bandNames().getInfo() == ["1", "2", "3", "3_1", "4", "5"] + assert image.bandNames().getInfo() == ["1", "2", "3", "4", "5"] From 1047d607294a722ae6b167e3bea14638426278a2 Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Thu, 24 Oct 2024 20:36:29 +0000 Subject: [PATCH 7/7] fix: make sure images is a list first --- geetools/Image.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/geetools/Image.py b/geetools/Image.py index baf11d56..8c0fab07 100644 --- a/geetools/Image.py +++ b/geetools/Image.py @@ -1431,32 +1431,32 @@ def fromList(cls, images: ee.List | list): Returns: A single ee.Image with one band per image in the passed list - + Examples: .. code-block:: python import ee, geetools ee.Initialize() - + sequence = ee.List([1, 2, 3]) images = sequence.map(lambda i: ee.Image(ee.Number(i)).rename(ee.Number(i).int().format())) image = ee.Image.geetools.fromList(images) print(image.bandNames().getInfo()) - + .. code-block:: python import ee, geetools ee.Initialize() - + sequence = ee.List([1, 2, 2, 3]) images = sequence.map(lambda i: ee.Image(ee.Number(i)).rename(ee.Number(i).int().format())) image = ee.Image.geetools.fromList(images) print(image.bandNames().getInfo()) > ee.ee_exception.EEException: Image.rename: Can't add a band named '2' to image because a band with this name already exists. Existing bands: [1, 2]. """ - bandNames = images.map(lambda i: ee.Image(i).bandNames()).flatten() + bandNames = ee.List(images).map(lambda i: ee.Image(i).bandNames()).flatten() ic = ee.ImageCollection.fromImages(images) return ic.toBands().rename(bandNames)