diff --git a/geemap/core.py b/geemap/core.py index 38ad8a3e3e..ccce5a3dc3 100644 --- a/geemap/core.py +++ b/geemap/core.py @@ -502,9 +502,29 @@ def set_zoom(self, value: int) -> None: def get_center(self) -> Sequence: return self.center - def get_bounds(self) -> Sequence: + def get_bounds(self, as_geojson: bool = False) -> Sequence: + """Returns the bounds of the current map view. + + Args: + as_geojson (bool, optional): If true, returns map bounds as + GeoJSON. Defaults to False. + + Returns: + list|dict: A list in the format [west, south, east, north] in + degrees or a GeoJSON dictionary. + """ bounds = self.bounds - return [bounds[0][0], bounds[0][1], bounds[1][0], bounds[1][1]] + if not bounds: + raise RuntimeError( + "Map bounds are undefined. Please display the " "map then try again." + ) + # ipyleaflet returns bounds in the format [[south, west], [north, east]] + # https://ipyleaflet.readthedocs.io/en/latest/map_and_basemaps/map.html#ipyleaflet.Map.fit_bounds + coords = [bounds[0][1], bounds[0][0], bounds[1][1], bounds[1][0]] + + if as_geojson: + return ee.Geometry.BBox(*coords).getInfo() + return coords def get_scale(self) -> float: # Reference: @@ -901,3 +921,4 @@ def _on_layers_change(self, change) -> None: addLayer = add_layer centerObject = center_object setCenter = set_center + getBounds = get_bounds diff --git a/geemap/geemap.py b/geemap/geemap.py index d102e6704d..78cc099706 100644 --- a/geemap/geemap.py +++ b/geemap/geemap.py @@ -1137,17 +1137,7 @@ def get_bounds(self, asGeoJSON=False): Returns: list | dict: A list in the format [west, south, east, north] in degrees. """ - bounds = self.bounds - coords = [bounds[0][1], bounds[0][0], bounds[1][1], bounds[1][0]] - - if asGeoJSON: - return ee.Geometry.BBox( - bounds[0][1], bounds[0][0], bounds[1][1], bounds[1][0] - ).getInfo() - else: - return coords - - getBounds = get_bounds + return super().get_bounds(as_geo_json=asGeoJSON) def add_cog_layer( self, diff --git a/tests/fake_ee.py b/tests/fake_ee.py index 6638b49965..6ec46c39fa 100644 --- a/tests/fake_ee.py +++ b/tests/fake_ee.py @@ -96,6 +96,12 @@ def getInfo(self, *_, **__): "type": "Point", "coordinates": [120, -70], } + if self.type().value == "BBox": + return { + "geodesic": False, + "type": "Polygon", + "coordinates": [[0, 1], [1, 2], [0, 1]], + } raise ValueError("Unexpected geometry type in test: ", self.type().value) def __eq__(self, other: object): diff --git a/tests/test_core.py b/tests/test_core.py index 9864b1cd34..7ac039096c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -89,6 +89,23 @@ def test_center_object(self): with self.assertRaisesRegex(ValueError, "Zoom must be an integer"): self.core_map.center_object(ee.Geometry.Point(), "2") + @unittest.mock.patch.object(core.Map, "bounds") + def test_get_bounds(self, mock_bounds): + """Tests that `get_bounds` returns the bounds of the map.""" + mock_bounds.__get__ = Mock(return_value=[[1, 2], [3, 4]]) + self.assertEqual(self.core_map.get_bounds(), [2, 1, 4, 3]) + self.assertEqual(self.core_map.getBounds(), [2, 1, 4, 3]) + expected_geo_json = { + "geodesic": False, + "type": "Polygon", + "coordinates": [[0, 1], [1, 2], [0, 1]], + } + self.assertEqual(self.core_map.get_bounds(as_geojson=True), expected_geo_json) + + mock_bounds.__get__ = Mock(return_value=()) + with self.assertRaisesRegex(RuntimeError, "Map bounds are undefined"): + self.core_map.get_bounds(as_geojson=True) + def test_add_basic_widget_by_name(self): """Tests that `add` adds widgets by name.""" self._clear_default_widgets()