From 2cf92658ee447d1a0ea74ce86210e8f2e492fcf4 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Wed, 16 Oct 2024 12:06:11 -0400 Subject: [PATCH] Improve add_vector methods --- leafmap/foliumap.py | 122 +++++++++++++-------------------- leafmap/leafmap.py | 162 ++++++++++++++------------------------------ 2 files changed, 99 insertions(+), 185 deletions(-) diff --git a/leafmap/foliumap.py b/leafmap/foliumap.py index dde3da1751..25dc02bfa3 100644 --- a/leafmap/foliumap.py +++ b/leafmap/foliumap.py @@ -1357,35 +1357,14 @@ def add_shp( Raises: FileNotFoundError: The provided shapefile could not be found. """ - import glob + import geopandas as gpd - if in_shp.startswith("http") and in_shp.endswith(".zip"): - out_dir = os.path.abspath("./cache/shp") - if not os.path.exists(out_dir): - os.makedirs(out_dir) - basename = os.path.basename(in_shp) - filename = os.path.join(out_dir, basename) - # download_from_url(in_shp, out_dir=out_dir, verbose=False) - download_file(in_shp, filename) - files = list(glob.glob(os.path.join(out_dir, "*.shp"))) - if len(files) > 0: - in_shp = files[0] - else: - raise FileNotFoundError( - "The downloaded zip file does not contain any shapefile in the root directory." - ) - else: - in_shp = os.path.abspath(in_shp) - if not os.path.exists(in_shp): - raise FileNotFoundError("The provided shapefile could not be found.") - - data = shp_to_geojson(in_shp) - - self.add_geojson( - data, - layer_name=layer_name, - info_mode=info_mode, - zoom_to_layer=zoom_to_layer, + gdf = gpd.read_file(in_shp) + self.add_gdf( + gdf, + layer_name, + zoom_to_layer, + info_mode, **kwargs, ) @@ -1412,8 +1391,10 @@ def add_geojson( FileNotFoundError: The provided GeoJSON file could not be found. """ import json - import requests import random + import geopandas as gpd + + gdf = None try: if isinstance(in_geojson, str): @@ -1430,24 +1411,29 @@ def add_geojson( with open(output, "r") as fd: data = json.load(fd) else: - in_geojson = github_raw_url(in_geojson) - data = requests.get(in_geojson).json() + gdf = gpd.read_file(in_geojson, encoding=encoding) + else: - in_geojson = os.path.abspath(in_geojson) - if not os.path.exists(in_geojson): - raise FileNotFoundError( - "The provided GeoJSON file could not be found." - ) - - with open(in_geojson, encoding=encoding) as f: - data = json.load(f) + gdf = gpd.read_file(in_geojson, encoding=encoding) + elif isinstance(in_geojson, dict): - data = in_geojson + gdf = gpd.GeoDataFrame.from_features(in_geojson) + elif isinstance(in_geojson, gpd.GeoDataFrame): + gdf = in_geojson else: raise TypeError("The input geojson must be a type of str or dict.") except Exception as e: raise Exception(e) + if gdf.crs is None: + print( + f"Warning: The dataset does not have a CRS defined. Assuming EPSG:4326." + ) + gdf.crs = "EPSG:4326" + elif gdf.crs != "EPSG:4326": + gdf = gdf.to_crs("EPSG:4326") + data = gdf.__geo_interface__ + # interchangeable parameters between ipyleaflet and folium. if "style_callback" in kwargs: @@ -1515,7 +1501,7 @@ def random_color(feature): geojson.add_to(self) if zoom_to_layer: - bounds = get_bounds(data) + bounds = bounds = gdf.total_bounds self.zoom_to_bounds(bounds) def add_gdf( @@ -1540,26 +1526,14 @@ def add_gdf( if gdf[col].dtype in ["datetime64[ns]", "datetime64[ns, UTC]"]: gdf[col] = gdf[col].astype(str) - data = gdf_to_geojson(gdf, epsg="4326") - self.add_geojson( - data, + gdf, layer_name=layer_name, info_mode=info_mode, zoom_to_layer=zoom_to_layer, **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_gdf_from_postgis( self, sql: str, @@ -1626,9 +1600,7 @@ def add_kml( if not os.path.exists(in_kml): raise FileNotFoundError("The provided KML could not be found.") - data = kml_to_geojson(in_kml) - - self.add_geojson(data, layer_name=layer_name, info_mode=info_mode, **kwargs) + self.add_vector(in_kml, layer_name, info_mode=info_mode, **kwargs) def add_vector( self, @@ -1653,31 +1625,33 @@ def add_vector( zoom_to_layer (bool, optional): Whether to zoom to the layer. Defaults to True. """ - if not filename.startswith("http"): - filename = os.path.abspath(filename) + import fiona + import geopandas as gpd - ext = os.path.splitext(filename)[1].lower() - if ext == ".shp": - self.add_shp(filename, layer_name, **kwargs) - elif ext in [".json", ".geojson"]: - self.add_geojson(filename, layer_name, **kwargs) + if isinstance(filename, str) and filename.endswith(".kml"): + fiona.drvsupport.supported_drivers["KML"] = "rw" + gdf = gpd.read_file( + filename, + bbox=bbox, + mask=mask, + rows=rows, + driver="KML", + ) else: - geojson = vector_to_geojson( + gdf = gpd.read_file( filename, bbox=bbox, mask=mask, rows=rows, - epsg="4326", - **kwargs, ) - self.add_geojson( - geojson, - layer_name, - info_mode=info_mode, - zoom_to_layer=zoom_to_layer, - **kwargs, - ) + self.add_gdf( + gdf, + layer_name, + zoom_to_layer, + info_mode, + **kwargs, + ) def add_planet_by_month( self, diff --git a/leafmap/leafmap.py b/leafmap/leafmap.py index 1e3b0b05f7..0369403204 100644 --- a/leafmap/leafmap.py +++ b/leafmap/leafmap.py @@ -2537,6 +2537,7 @@ def add_shp( info_mode: Optional[str] = "on_hover", zoom_to_layer: Optional[bool] = False, encoding: Optional[str] = "utf-8", + **kwargs, ) -> None: """Adds a shapefile to the map. @@ -2555,30 +2556,11 @@ def add_shp( FileNotFoundError: The provided shapefile could not be found. """ - import glob - - if in_shp.startswith("http") and in_shp.endswith(".zip"): - out_dir = os.path.dirname(temp_file_path(".shp")) - if not os.path.exists(out_dir): - os.makedirs(out_dir) - basename = os.path.basename(in_shp) - filename = os.path.join(out_dir, basename) - download_file(in_shp, filename) - files = list(glob.glob(os.path.join(out_dir, "*.shp"))) - if len(files) > 0: - in_shp = files[0] - else: - raise FileNotFoundError( - "The downloaded zip file does not contain any shapefile in the root directory." - ) - else: - in_shp = os.path.abspath(in_shp) - if not os.path.exists(in_shp): - raise FileNotFoundError("The provided shapefile could not be found.") + import geopandas as gpd - geojson = shp_to_geojson(in_shp, encoding=encoding) - self.add_geojson( - geojson, + gdf = gpd.read_file(in_shp, encoding=encoding) + self.add_gdf( + gdf, layer_name, style, hover_style, @@ -2587,6 +2569,7 @@ def add_shp( info_mode, zoom_to_layer, encoding, + **kwargs, ) def add_geojson( @@ -2630,12 +2613,9 @@ def add_geojson( """ import json import random - import requests - - # style_callback_only = False + import geopandas as gpd - # if fill_colors is not None: - # style_callback_only = True + gdf = None try: if isinstance(in_geojson, str): @@ -2652,25 +2632,30 @@ def add_geojson( with open(output, "r") as fd: data = json.load(fd) else: - in_geojson = github_raw_url(in_geojson) - data = requests.get(in_geojson).json() + gdf = gpd.read_file(in_geojson, encoding=encoding) + else: - in_geojson = os.path.abspath(in_geojson) - if not os.path.exists(in_geojson): - raise FileNotFoundError( - "The provided GeoJSON file could not be found." - ) + gdf = gpd.read_file(in_geojson, encoding=encoding) - with open(in_geojson, encoding=encoding) as f: - data = json.load(f) elif isinstance(in_geojson, dict): - data = in_geojson + gdf = gpd.GeoDataFrame.from_features(in_geojson) + elif isinstance(in_geojson, gpd.GeoDataFrame): + gdf = in_geojson else: raise TypeError("The input geojson must be a type of str or dict.") except Exception as e: raise Exception(e) - geom_type = get_geometry_type(data) + if gdf.crs is None: + print( + f"Warning: The dataset does not have a CRS defined. Assuming EPSG:4326." + ) + gdf.crs = "EPSG:4326" + elif gdf.crs != "EPSG:4326": + gdf = gdf.to_crs("EPSG:4326") + data = gdf.__geo_interface__ + + geom_type = gdf.geom_type[0] if style is None and (style_callback is None): style = { @@ -2818,17 +2803,8 @@ def update_html(feature, fields=fields, **kwargs): if zoom_to_layer: try: - import numpy as np - import geopandas as gpd - - gdf = gpd.GeoDataFrame.from_features(data) - if gdf.crs is None: - gdf.crs = "EPSG:4326" - 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"]) + bounds = gdf.total_bounds + west, south, east, north = bounds self.fit_bounds([[south, east], [north, west]]) except Exception as e: print(e) @@ -2903,10 +2879,8 @@ def add_gdf( except: pass - data = gdf_to_geojson(gdf, epsg="4326") - self.add_geojson( - data, + gdf, layer_name, style, hover_style, @@ -2918,16 +2892,6 @@ def add_gdf( **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_gdf_from_postgis( self, sql: str, @@ -2976,6 +2940,7 @@ def add_kml( style_callback: Optional[Callable] = None, fill_colors: Optional[list[str]] = None, info_mode: Optional[str] = "on_hover", + **kwargs, ) -> None: """Adds a KML file to the map. @@ -3018,6 +2983,7 @@ def add_kml( style_callback=style_callback, fill_colors=fill_colors, info_mode=info_mode, + **kwargs, ) def add_vector( @@ -3068,55 +3034,29 @@ def add_vector( encoding (str, optional): The encoding to use to read the file. Defaults to "utf-8". """ - if not filename.startswith("http"): - filename = os.path.abspath(filename) - else: - filename = github_raw_url(filename) - ext = os.path.splitext(filename)[1].lower() - if ext == ".shp": - self.add_shp( - filename, - layer_name, - style, - hover_style, - style_callback, - fill_colors, - info_mode, - encoding, - ) - elif ext in [".json", ".geojson"]: - self.add_geojson( - filename, - layer_name, - style, - hover_style, - style_callback, - fill_colors, - info_mode, - zoom_to_layer, - encoding, - ) - else: - geojson = vector_to_geojson( - filename, - bbox=bbox, - mask=mask, - rows=rows, - epsg="4326", - **kwargs, - ) + import fiona + import geopandas as gpd - self.add_geojson( - geojson, - layer_name, - style, - hover_style, - style_callback, - fill_colors, - info_mode, - zoom_to_layer, - encoding, - ) + if isinstance(filename, str) and filename.endswith(".kml"): + fiona.drvsupport.supported_drivers["KML"] = "rw" + kwargs["driver"] = "KML" + + gdf = gpd.read_file( + filename, bbox=bbox, mask=mask, rows=rows, encoding=encoding, **kwargs + ) + + self.add_gdf( + gdf, + layer_name, + style, + hover_style, + style_callback, + fill_colors, + info_mode, + zoom_to_layer, + encoding, + **kwargs, + ) def add_xy_data( self,