From d536d4ddf24b6db6d599bc18d2b861291a135f04 Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Wed, 28 Oct 2026 17:10:52 +0000 Subject: [PATCH 1/8] feat: add grid management to the crop monitor --- geetools/ee_feature.py | 51 ++++++++++++++++ geetools/ee_feature_collection.py | 60 +++++++++++++++++++ pyproject.toml | 1 + tests/test_Feature.py | 23 +++++++ tests/test_FeatureCollection.py | 19 ++++++ .../serialized_test_split.yml | 12 ++-- 6 files changed, 160 insertions(+), 6 deletions(-) diff --git a/geetools/ee_feature.py b/geetools/ee_feature.py index 696cf72e..69e5397e 100644 --- a/geetools/ee_feature.py +++ b/geetools/ee_feature.py @@ -14,6 +14,57 @@ def __init__(self, obj: ee.Feature): """Initialize the class.""" self._obj = obj + @classmethod + def cell( + cls, + size: int | ee.Number, + ix: int | ee.Number = 0, + iy: int | ee.Number = 0, + crs: str | ee.String = "EPSG:4326", + ) -> ee.Feature: + """Create a feature representing one cell in a regularly spaced grid. + + Args: + size: The cell side length in the units of the CRS. + ix: The cell index in the x direction, starting at the CRS area-of-use minimum. + iy: The cell index in the y direction, starting at the CRS area-of-use minimum. + crs: The CRS of the grid. + + Returns: + A feature representing the requested grid cell. + """ + from pyproj import CRS, Transformer + + size, crs = ee.Number(size), ee.String(crs) + crs_value = crs.getInfo() + # Earth Engine cannot currently compute projection bounds server-side; pyproj supplies the area of use. + # https://gis.stackexchange.com/questions/471100/how-to-get-the-bounds-of-a-projection-in-gee + # https://issuetracker.google.com/issues/313937471 + crs_x_min, crs_y_min, crs_x_max, crs_y_max = CRS(crs_value).area_of_use.bounds + transformer = Transformer.from_crs(4326, crs_value, always_xy=True) + x_min, y_min = transformer.transform(crs_x_min, crs_y_min) + x_max, y_max = transformer.transform(crs_x_max, crs_y_max) + + x_min, y_min = ee.Number(x_min).toFloat(), ee.Number(y_min).toFloat() + x_max, y_max = ee.Number(x_max).toFloat(), ee.Number(y_max).toFloat() + longitudes = ee.List.sequence(x_min, x_max, size).add(x_max).distinct() + latitudes = ee.List.sequence(y_min, y_max, size).add(y_max).distinct() + ix, iy = ee.Number(ix).toInt(), ee.Number(iy).toInt() + + cell_id = ee.String("{proj}_{size}_E{ix}-S{iy}").geetools.format( + { + "proj": ee.Projection(crs).crs(), + "size": size.format(), + "ix": ix.format("%02d"), + "iy": iy.format("%02d"), + } + ) + rectangle = ee.Geometry.Rectangle( + [longitudes.get(ix), latitudes.get(iy), longitudes.get(ix.add(1)), latitudes.get(iy.add(1))], + crs, + ) + return ee.Feature(rectangle, {"id": cell_id}) + def toFeatureCollection(self) -> ee.FeatureCollection: """Convert a :py:class:`ee.Feature` composed of a multiGeometry geometry into a :py:class:`ee.FeatureCollection`. diff --git a/geetools/ee_feature_collection.py b/geetools/ee_feature_collection.py index 05d77504..0321d444 100644 --- a/geetools/ee_feature_collection.py +++ b/geetools/ee_feature_collection.py @@ -8,6 +8,7 @@ import shapely from matplotlib import pyplot as plt from matplotlib.axes import Axes +from pyproj import CRS, Transformer from .accessors import register_class_accessor from .utils import plot_data @@ -30,6 +31,65 @@ def __init__(self, obj: ee.FeatureCollection): """Initialize the :py:class:`ee.FeatureCollection` class.""" self._obj = obj + @classmethod + def grid(cls, size: int | float | ee.Number, crs: str | ee.String = "EPSG:4326") -> ee.FeatureCollection: + """Create a global feature grid with square cells in the requested CRS. + + Args: + size: The cell side length in the units of the CRS. + crs: The CRS of the grid. Defaults to ``"EPSG:4326"``. + + Returns: + A feature collection whose cells have an ``id`` property. + """ + size, crs = ee.Number(size), ee.String(crs) + crs_value = crs.getInfo() + # Earth Engine cannot currently compute projection bounds server-side; pyproj supplies the area of use. + # https://gis.stackexchange.com/questions/471100/how-to-get-the-bounds-of-a-projection-in-gee + # https://issuetracker.google.com/issues/313937471 + crs_x_min, crs_y_min, crs_x_max, crs_y_max = CRS(crs_value).area_of_use.bounds + transformer = Transformer.from_crs(4326, crs_value, always_xy=True) + x_min, y_min = transformer.transform(crs_x_min, crs_y_min) + x_max, y_max = transformer.transform(crs_x_max, crs_y_max) + + distance_transformer = Transformer.from_crs(crs_value, 3857, always_xy=True) + x1 = distance_transformer.transform(x_min, 0)[0] + x2 = distance_transformer.transform(x_min + size.getInfo(), 0)[0] + equator_cell_size = int(abs(x2 - x1)) + + x_min, y_min = ee.Number(x_min).toFloat(), ee.Number(y_min).toFloat() + x_max, y_max = ee.Number(x_max).toFloat(), ee.Number(y_max).toFloat() + longitudes = ee.List.sequence(x_min, x_max, size).add(x_max).distinct() + latitudes = ee.List.sequence(y_min, y_max, size).add(y_max).distinct() + longitude_indexes = ee.List.sequence(0, longitudes.size().subtract(2)) + latitude_indexes = ee.List.sequence(0, latitudes.size().subtract(2)) + + def rectangle(i: ee.Number, j: ee.Number) -> ee.Feature: + i, j = ee.Number(i).toInt(), ee.Number(j).toInt() + coordinates = [ + longitudes.get(i), + latitudes.get(j), + longitudes.get(i.add(1)), + latitudes.get(j.add(1)), + ] + cell_id = ee.String("{proj}_{size}_E{ix}-S{iy}").geetools.format( + { + "proj": ee.Projection(crs).crs(), + "size": size.format(), + "ix": i.format("%02d"), + "iy": j.format("%02d"), + } + ) + return ee.Feature( + ee.Geometry.Rectangle(coordinates, crs), + {"id": cell_id, "equator_cell_size_m": equator_cell_size}, + ) + + def column(i: ee.Number) -> ee.List: + return latitude_indexes.map(lambda j: rectangle(i, j)) + + return ee.FeatureCollection(ee.List(ee.List(longitude_indexes).map(column)).flatten()) + def toImage( self, color: str | ee.String | int | ee.Number = 0, diff --git a/pyproject.toml b/pyproject.toml index 161751ca..59e0d010 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ dependencies = [ "requests", "pandas", "geopandas", + "pyproj", "deprecated", "xee>=0.1.0", # xee change API prototype for geometry setting "yamlable", diff --git a/tests/test_Feature.py b/tests/test_Feature.py index b33870b9..14f8f012 100644 --- a/tests/test_Feature.py +++ b/tests/test_Feature.py @@ -4,6 +4,29 @@ import geetools # noqa: F401 +class TestCell: + """Test the ``cell`` method.""" + + def test_cell(self): + cell = ee.Feature.geetools.cell(20) + assert cell.get("id").getInfo() == "EPSG:4326_20_E00-S00" + assert cell.geometry().coordinates().getInfo() == [ + [[-180, -90], [-160, -90], [-160, -70], [-180, -70], [-180, -90]] + ] + + def test_cell_with_indexes(self): + cell = ee.Feature.geetools.cell(20, ix=5, iy=5) + assert cell.get("id").getInfo() == "EPSG:4326_20_E05-S05" + + def test_cell_with_size_10_and_indexes(self): + cell = ee.Feature.geetools.cell(10, ix=10, iy=10) + assert cell.get("id").getInfo() == "EPSG:4326_10_E10-S10" + + def test_cell_with_projected_crs(self): + cell = ee.Feature.geetools.cell(1000, crs="EPSG:3857") + assert cell.get("id").getInfo() == "EPSG:3857_1000_E00-S00" + + class TestToFeatureCollection: """Test the ``toFeatureCollection`` method.""" diff --git a/tests/test_FeatureCollection.py b/tests/test_FeatureCollection.py index 9315efda..7ea4f288 100644 --- a/tests/test_FeatureCollection.py +++ b/tests/test_FeatureCollection.py @@ -33,6 +33,25 @@ def vatican(self): return ee.Geometry.Point([12.453386, 41.903282]).buffer(1) +class TestGrid: + """Test the ``grid`` method.""" + + def test_grid_size_and_ids(self): + grid = ee.FeatureCollection.geetools.grid(20) + assert grid.size().getInfo() == 162 + assert grid.first().get("id").getInfo() == "EPSG:4326_20_E00-S00" + + def test_grid_with_float_size(self): + grid = ee.FeatureCollection.geetools.grid(60.0) + assert grid.size().getInfo() == 18 + assert grid.first().get("id").getInfo() == "EPSG:4326_60_E00-S00" + + def test_grid_projected_crs(self): + grid = ee.FeatureCollection.geetools.grid(10_000_000, "EPSG:3857") + assert grid.size().getInfo() > 0 + assert grid.first().get("id").getInfo() == "EPSG:3857_10000000_E00-S00" + + class TestReduceColumnsGroup: """Test the ``reduceColumnsGroup`` method.""" diff --git a/tests/test_FeatureCollection/serialized_test_split.yml b/tests/test_FeatureCollection/serialized_test_split.yml index a5112557..f0f1a319 100644 --- a/tests/test_FeatureCollection/serialized_test_split.yml +++ b/tests/test_FeatureCollection/serialized_test_split.yml @@ -174,15 +174,15 @@ values: count: functionInvocationValue: arguments: - left: + input: functionInvocationValue: arguments: - input: + left: constantValue: 3 - functionName: Number.toInt - right: - constantValue: 1 - functionName: Number.add + right: + constantValue: 1 + functionName: Number.add + functionName: Number.toInt end: constantValue: 1 start: From c163fbe6f87090a7166b43078e96e2ba12b6b2a2 Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Wed, 28 Oct 2026 17:22:09 +0000 Subject: [PATCH 2/8] feat: add warning for costly grid generation in FeatureCollectionAccessor --- geetools/ee_feature_collection.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/geetools/ee_feature_collection.py b/geetools/ee_feature_collection.py index 0321d444..3689cacb 100644 --- a/geetools/ee_feature_collection.py +++ b/geetools/ee_feature_collection.py @@ -35,6 +35,10 @@ def __init__(self, obj: ee.FeatureCollection): def grid(cls, size: int | float | ee.Number, crs: str | ee.String = "EPSG:4326") -> ee.FeatureCollection: """Create a global feature grid with square cells in the requested CRS. + Warning: + Generating this grid is a very costly operation. It is strongly recommended to export the + resulting feature collection to an Earth Engine asset before using it in other computations. + Args: size: The cell side length in the units of the CRS. crs: The CRS of the grid. Defaults to ``"EPSG:4326"``. From 996e2b9fa75e05273819cf468fe3aba53fb21dc3 Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Thu, 29 Oct 2026 09:50:25 +0000 Subject: [PATCH 3/8] feat: update FeatureCollectionAccessor for improved CRS handling and add test for polar CRS grid --- geetools/ee_feature_collection.py | 7 +++---- tests/test_FeatureCollection.py | 4 ++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/geetools/ee_feature_collection.py b/geetools/ee_feature_collection.py index 3689cacb..8c215bd4 100644 --- a/geetools/ee_feature_collection.py +++ b/geetools/ee_feature_collection.py @@ -51,10 +51,9 @@ def grid(cls, size: int | float | ee.Number, crs: str | ee.String = "EPSG:4326") # Earth Engine cannot currently compute projection bounds server-side; pyproj supplies the area of use. # https://gis.stackexchange.com/questions/471100/how-to-get-the-bounds-of-a-projection-in-gee # https://issuetracker.google.com/issues/313937471 - crs_x_min, crs_y_min, crs_x_max, crs_y_max = CRS(crs_value).area_of_use.bounds + geographic_bounds = CRS(crs_value).area_of_use.bounds transformer = Transformer.from_crs(4326, crs_value, always_xy=True) - x_min, y_min = transformer.transform(crs_x_min, crs_y_min) - x_max, y_max = transformer.transform(crs_x_max, crs_y_max) + x_min, y_min, x_max, y_max = transformer.transform_bounds(*geographic_bounds, densify_pts=100) distance_transformer = Transformer.from_crs(crs_value, 3857, always_xy=True) x1 = distance_transformer.transform(x_min, 0)[0] @@ -85,7 +84,7 @@ def rectangle(i: ee.Number, j: ee.Number) -> ee.Feature: } ) return ee.Feature( - ee.Geometry.Rectangle(coordinates, crs), + ee.Geometry.Rectangle(coordinates, crs, geodesic=False, evenOdd=True), {"id": cell_id, "equator_cell_size_m": equator_cell_size}, ) diff --git a/tests/test_FeatureCollection.py b/tests/test_FeatureCollection.py index 7ea4f288..88e494ae 100644 --- a/tests/test_FeatureCollection.py +++ b/tests/test_FeatureCollection.py @@ -51,6 +51,10 @@ def test_grid_projected_crs(self): assert grid.size().getInfo() > 0 assert grid.first().get("id").getInfo() == "EPSG:3857_10000000_E00-S00" + def test_grid_polar_crs(self): + grid = ee.FeatureCollection.geetools.grid(2_000_000, "EPSG:3031") + assert grid.size().getInfo() == 16 + class TestReduceColumnsGroup: """Test the ``reduceColumnsGroup`` method.""" From 3994061a848cbee3351a1586bd7690b3aa5f48bf Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Thu, 29 Oct 2026 09:54:20 +0000 Subject: [PATCH 4/8] feat: improve CRS handling in FeatureAccessor and add test for polar CRS --- geetools/ee_feature.py | 7 ++++--- tests/test_Feature.py | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/geetools/ee_feature.py b/geetools/ee_feature.py index 69e5397e..fd2ad354 100644 --- a/geetools/ee_feature.py +++ b/geetools/ee_feature.py @@ -40,10 +40,9 @@ def cell( # Earth Engine cannot currently compute projection bounds server-side; pyproj supplies the area of use. # https://gis.stackexchange.com/questions/471100/how-to-get-the-bounds-of-a-projection-in-gee # https://issuetracker.google.com/issues/313937471 - crs_x_min, crs_y_min, crs_x_max, crs_y_max = CRS(crs_value).area_of_use.bounds + geographic_bounds = CRS(crs_value).area_of_use.bounds transformer = Transformer.from_crs(4326, crs_value, always_xy=True) - x_min, y_min = transformer.transform(crs_x_min, crs_y_min) - x_max, y_max = transformer.transform(crs_x_max, crs_y_max) + x_min, y_min, x_max, y_max = transformer.transform_bounds(*geographic_bounds, densify_pts=100) x_min, y_min = ee.Number(x_min).toFloat(), ee.Number(y_min).toFloat() x_max, y_max = ee.Number(x_max).toFloat(), ee.Number(y_max).toFloat() @@ -62,6 +61,8 @@ def cell( rectangle = ee.Geometry.Rectangle( [longitudes.get(ix), latitudes.get(iy), longitudes.get(ix.add(1)), latitudes.get(iy.add(1))], crs, + geodesic=False, + evenOdd=True, ) return ee.Feature(rectangle, {"id": cell_id}) diff --git a/tests/test_Feature.py b/tests/test_Feature.py index 14f8f012..0349d100 100644 --- a/tests/test_Feature.py +++ b/tests/test_Feature.py @@ -26,6 +26,11 @@ def test_cell_with_projected_crs(self): cell = ee.Feature.geetools.cell(1000, crs="EPSG:3857") assert cell.get("id").getInfo() == "EPSG:3857_1000_E00-S00" + def test_cell_with_polar_crs(self): + cell = ee.Feature.geetools.cell(2_000_000, crs="EPSG:3031") + assert cell.get("id").getInfo() == "EPSG:3031_2000000_E00-S00" + assert cell.geometry().coordinates().getInfo() + class TestToFeatureCollection: """Test the ``toFeatureCollection`` method.""" From ca7d449485b49f46a92072efc5c75c5bb7c79bc6 Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Thu, 29 Oct 2026 09:56:07 +0000 Subject: [PATCH 5/8] feat: update toDouble for improved precision in FeatureAccessor and FeatureCollectionAccessor --- geetools/ee_feature.py | 4 ++-- geetools/ee_feature_collection.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/geetools/ee_feature.py b/geetools/ee_feature.py index fd2ad354..45c0eee1 100644 --- a/geetools/ee_feature.py +++ b/geetools/ee_feature.py @@ -44,8 +44,8 @@ def cell( transformer = Transformer.from_crs(4326, crs_value, always_xy=True) x_min, y_min, x_max, y_max = transformer.transform_bounds(*geographic_bounds, densify_pts=100) - x_min, y_min = ee.Number(x_min).toFloat(), ee.Number(y_min).toFloat() - x_max, y_max = ee.Number(x_max).toFloat(), ee.Number(y_max).toFloat() + x_min, y_min = ee.Number(x_min).toDouble(), ee.Number(y_min).toDouble() + x_max, y_max = ee.Number(x_max).toDouble(), ee.Number(y_max).toDouble() longitudes = ee.List.sequence(x_min, x_max, size).add(x_max).distinct() latitudes = ee.List.sequence(y_min, y_max, size).add(y_max).distinct() ix, iy = ee.Number(ix).toInt(), ee.Number(iy).toInt() diff --git a/geetools/ee_feature_collection.py b/geetools/ee_feature_collection.py index 8c215bd4..fa4c7529 100644 --- a/geetools/ee_feature_collection.py +++ b/geetools/ee_feature_collection.py @@ -60,8 +60,8 @@ def grid(cls, size: int | float | ee.Number, crs: str | ee.String = "EPSG:4326") x2 = distance_transformer.transform(x_min + size.getInfo(), 0)[0] equator_cell_size = int(abs(x2 - x1)) - x_min, y_min = ee.Number(x_min).toFloat(), ee.Number(y_min).toFloat() - x_max, y_max = ee.Number(x_max).toFloat(), ee.Number(y_max).toFloat() + x_min, y_min = ee.Number(x_min).toDouble(), ee.Number(y_min).toDouble() + x_max, y_max = ee.Number(x_max).toDouble(), ee.Number(y_max).toDouble() longitudes = ee.List.sequence(x_min, x_max, size).add(x_max).distinct() latitudes = ee.List.sequence(y_min, y_max, size).add(y_max).distinct() longitude_indexes = ee.List.sequence(0, longitudes.size().subtract(2)) From 26b3041ec708e173276f8456e5b057e02b31801f Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Thu, 29 Oct 2026 10:01:35 +0000 Subject: [PATCH 6/8] feat: clamp cell generation to projection bounds in FeatureAccessor and add corresponding test --- geetools/ee_feature.py | 8 +++++--- tests/test_Feature.py | 6 ++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/geetools/ee_feature.py b/geetools/ee_feature.py index 45c0eee1..b441a888 100644 --- a/geetools/ee_feature.py +++ b/geetools/ee_feature.py @@ -46,9 +46,11 @@ def cell( x_min, y_min = ee.Number(x_min).toDouble(), ee.Number(y_min).toDouble() x_max, y_max = ee.Number(x_max).toDouble(), ee.Number(y_max).toDouble() - longitudes = ee.List.sequence(x_min, x_max, size).add(x_max).distinct() - latitudes = ee.List.sequence(y_min, y_max, size).add(y_max).distinct() ix, iy = ee.Number(ix).toInt(), ee.Number(iy).toInt() + x_left = x_min.add(size.multiply(ix)) + x_right = x_left.add(size).min(x_max) + y_bottom = y_min.add(size.multiply(iy)) + y_top = y_bottom.add(size).min(y_max) cell_id = ee.String("{proj}_{size}_E{ix}-S{iy}").geetools.format( { @@ -59,7 +61,7 @@ def cell( } ) rectangle = ee.Geometry.Rectangle( - [longitudes.get(ix), latitudes.get(iy), longitudes.get(ix.add(1)), latitudes.get(iy.add(1))], + [x_left, y_bottom, x_right, y_top], crs, geodesic=False, evenOdd=True, diff --git a/tests/test_Feature.py b/tests/test_Feature.py index 0349d100..1ddee7d2 100644 --- a/tests/test_Feature.py +++ b/tests/test_Feature.py @@ -22,6 +22,12 @@ def test_cell_with_size_10_and_indexes(self): cell = ee.Feature.geetools.cell(10, ix=10, iy=10) assert cell.get("id").getInfo() == "EPSG:4326_10_E10-S10" + def test_cell_clamps_to_projection_bounds(self): + cell = ee.Feature.geetools.cell(100, ix=3, iy=1) + assert cell.geometry().coordinates().getInfo() == [ + [[120, 10], [180, 10], [180, 90], [120, 90], [120, 10]] + ] + def test_cell_with_projected_crs(self): cell = ee.Feature.geetools.cell(1000, crs="EPSG:3857") assert cell.get("id").getInfo() == "EPSG:3857_1000_E00-S00" From 11e378cf0502d3be1a9d9d3d582d1f827bc0e505 Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Thu, 29 Oct 2026 10:16:35 +0000 Subject: [PATCH 7/8] feat: specify minimum version for pyproj in dependencies --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 59e0d010..908ec614 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ dependencies = [ "requests", "pandas", "geopandas", - "pyproj", + "pyproj>=3.1", # include the requires python "deprecated", "xee>=0.1.0", # xee change API prototype for geometry setting "yamlable", From 335805125d27473ddbd8b3ec15958615bef3a974 Mon Sep 17 00:00:00 2001 From: Pierrick Rambaud Date: Thu, 29 Oct 2026 10:54:59 +0000 Subject: [PATCH 8/8] feat: handle equator_cell_size_m as None when size is zero in FeatureCollectionAccessor and add corresponding tests --- geetools/ee_feature_collection.py | 2 +- tests/test_FeatureCollection.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/geetools/ee_feature_collection.py b/geetools/ee_feature_collection.py index fa4c7529..3052116a 100644 --- a/geetools/ee_feature_collection.py +++ b/geetools/ee_feature_collection.py @@ -58,7 +58,7 @@ def grid(cls, size: int | float | ee.Number, crs: str | ee.String = "EPSG:4326") distance_transformer = Transformer.from_crs(crs_value, 3857, always_xy=True) x1 = distance_transformer.transform(x_min, 0)[0] x2 = distance_transformer.transform(x_min + size.getInfo(), 0)[0] - equator_cell_size = int(abs(x2 - x1)) + equator_cell_size = int(abs(x2 - x1)) or None x_min, y_min = ee.Number(x_min).toDouble(), ee.Number(y_min).toDouble() x_max, y_max = ee.Number(x_max).toDouble(), ee.Number(y_max).toDouble() diff --git a/tests/test_FeatureCollection.py b/tests/test_FeatureCollection.py index 88e494ae..135f5b9d 100644 --- a/tests/test_FeatureCollection.py +++ b/tests/test_FeatureCollection.py @@ -40,6 +40,7 @@ def test_grid_size_and_ids(self): grid = ee.FeatureCollection.geetools.grid(20) assert grid.size().getInfo() == 162 assert grid.first().get("id").getInfo() == "EPSG:4326_20_E00-S00" + assert grid.first().get("equator_cell_size_m").getInfo() > 0 def test_grid_with_float_size(self): grid = ee.FeatureCollection.geetools.grid(60.0) @@ -54,6 +55,7 @@ def test_grid_projected_crs(self): def test_grid_polar_crs(self): grid = ee.FeatureCollection.geetools.grid(2_000_000, "EPSG:3031") assert grid.size().getInfo() == 16 + assert grid.first().get("equator_cell_size_m").getInfo() is None class TestReduceColumnsGroup: