diff --git a/geemap/common.py b/geemap/common.py index 573dc31f0d..b68a0e245b 100644 --- a/geemap/common.py +++ b/geemap/common.py @@ -8737,3 +8737,64 @@ def create_download_button( return except Exception as e: raise Exception(e) + + +def gdf_to_geojson(gdf, out_geojson=None, epsg=None): + """Converts a GeoDataFame to GeoJSON. + + Args: + gdf (GeoDataFrame): A GeoPandas GeoDataFrame. + out_geojson (str, optional): File path to he output GeoJSON. Defaults to None. + epsg (str, optional): An EPSG string, e.g., "4326". Defaults to None. + + Raises: + TypeError: When the output file extension is incorrect. + Exception: When the conversion fails. + + Returns: + dict: When the out_json is None returns a dict. + """ + check_package(name="geopandas", URL="https://geopandas.org") + + try: + if epsg is not None: + gdf = gdf.to_crs(epsg=epsg) + geojson = gdf.__geo_interface__ + + if out_geojson is None: + return geojson + else: + ext = os.path.splitext(out_geojson)[1] + if ext.lower() not in [".json", ".geojson"]: + raise TypeError( + "The output file extension must be either .json or .geojson" + ) + out_dir = os.path.dirname(out_geojson) + if not os.path.exists(out_dir): + os.makedirs(out_dir) + + gdf.to_file(out_geojson, driver="GeoJSON") + except Exception as e: + raise Exception(e) + + +def temp_file_path(extension): + """Returns a temporary file path. + + Args: + extension (str): The file extension. + + Returns: + str: The temporary file path. + """ + + import tempfile + import os + import uuid + + if not extension.startswith("."): + extension = "." + extension + file_id = str(uuid.uuid4()) + file_path = os.path.join(tempfile.gettempdir(), f"{file_id}{extension}") + + return file_path diff --git a/geemap/foliumap.py b/geemap/foliumap.py index 71fe5e9af0..ff907e0df6 100644 --- a/geemap/foliumap.py +++ b/geemap/foliumap.py @@ -364,6 +364,15 @@ def zoom_to_bounds(self, bounds): # The folium fit_bounds method takes lat/lon bounds in the form [[south, west], [north, east]]. self.fit_bounds([[bounds[1], bounds[0]], [bounds[3], bounds[2]]]) + def zoom_to_gdf(self, gdf): + """Zooms to the bounding box of a GeoPandas GeoDataFrame. + + Args: + gdf (GeoDataFrame): A GeoPandas GeoDataFrame. + """ + bounds = gdf.total_bounds + self.zoom_to_bounds(bounds) + def center_object(self, ee_object, zoom=10): """Centers the map view on a given object. @@ -916,6 +925,30 @@ def add_kml(self, in_kml, layer_name="Untitled", **kwargs): geo_json.add_to(self) # os.remove(out_json) + def add_gdf(self, gdf, layer_name="Untitled", zoom_to_layer=True, **kwargs): + """Adds a GeoPandas GeoDataFrameto the map. + + Args: + gdf (GeoDataFrame): A GeoPandas GeoDataFrame. + layer_name (str, optional): The layer name to be used. Defaults to "Untitled". + zoom_to_layer (bool, optional): Whether to zoom to the layer. + + """ + + data = gdf_to_geojson(gdf, epsg="4326") + + self.add_geojson(data, layer_name=layer_name, **kwargs) + + if zoom_to_layer: + import numpy as np + + bounds = gdf.to_crs(epsg="4326").bounds + west = np.min(bounds["minx"]) + south = np.min(bounds["miny"]) + east = np.max(bounds["maxx"]) + north = np.max(bounds["maxy"]) + self.fit_bounds([[south, east], [north, west]]) + def add_osm( self, query,