From 6b77983f260c414c9e9fa9de8424cca03847a4a6 Mon Sep 17 00:00:00 2001 From: "Kharude, Sachin" Date: Mon, 14 Jun 2021 19:22:09 +0530 Subject: [PATCH 1/4] Initial changes Signed-off-by: Kharude, Sachin --- .gitignore | 3 +- leafmap/heremap.py | 107 ++++++++++++++++++++++++++++++++++++++++++--- leafmap/leafmap.py | 2 + 3 files changed, 105 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 90820bcf8d..25a015189b 100644 --- a/.gitignore +++ b/.gitignore @@ -105,4 +105,5 @@ ENV/ .mypy_cache/ # IDE settings -.vscode/ \ No newline at end of file +.vscode/ +.idea/ diff --git a/leafmap/heremap.py b/leafmap/heremap.py index 8a25b7e15f..16190e1358 100644 --- a/leafmap/heremap.py +++ b/leafmap/heremap.py @@ -1,6 +1,7 @@ """ This module defines here-map-widget-for-jupyter as backend for leafmap library. -For more details about Here Map Widget for Jupyter please check: https://github.com/heremaps/here-map-widget-for-jupyter +For more details about Here Map Widget for Jupyter +please check: https://github.com/heremaps/here-map-widget-for-jupyter """ import os import json @@ -9,6 +10,7 @@ import here_map_widget import ipywidgets as widgets from .basemaps import here_basemaps +from .common import shp_to_geojson, gdf_to_geojson from here_map_widget import ( FullscreenControl, @@ -50,6 +52,9 @@ def __init__(self, api_key, **kwargs): else: self.layout.height = kwargs["height"] + if "width" in kwargs: + self.layout.width = kwargs["width"] + if kwargs.get("layers_control"): self.add_control(LayersControl(alignment="RIGHT_TOP")) @@ -109,7 +114,8 @@ def add_tile_layer( """Adds a TileLayer to the map. Args: - url (str, optional): The URL of the tile layer. Defaults to 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'. + url (str, optional): The URL of the tile layer. + Defaults to 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'. name (str, optional): The layer name to use for the layer. Defaults to 'Untitled'. attribution (str, optional): The attribution to use. Defaults to ''. opacity (float, optional): The opacity of the layer. Defaults to 1. @@ -141,13 +147,19 @@ def add_geojson( """Adds a GeoJSON file to the map. Args: - in_geojson (str | dict): The file path or http URL to the input GeoJSON or a dictionary containing the geojson. + in_geojson (str | dict): The file path or http URL to the input GeoJSON or a + dictionary containing the geojson. layer_name (str, optional): The layer name to be used.. Defaults to "Untitled". style (dict, optional): A dictionary specifying the style to be used. Defaults to {}. hover_style (dict, optional): Hover style dictionary. Defaults to {}. - style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None. - fill_colors (list, optional): The random colors to use for filling polygons. Defaults to ["black"]. - info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover". + style_callback (function, optional): Styling function that is called for each feature, + and should return the feature style. This styling function takes the feature + as argument. Defaults to None. + fill_colors (list, optional): The random colors to use for filling polygons. + Defaults to ["black"]. + info_mode (str, optional): Displays the attributes by either on_hover or on_click. + Any value other than "on_hover" or "on_click" will be treated as None. + Defaults to "on_hover". Raises: FileNotFoundError: The provided GeoJSON file could not be found. """ @@ -255,3 +267,86 @@ def _update_html(feature, **_): geojson.on_click(_update_html) self.add_layer(geojson) + + def add_shp( + self, + in_shp, + layer_name="Untitled", + style=None, + hover_style=None, + style_callback=None, + fill_colors=None, + info_mode="on_hover", + ): + """Adds a shapefile to the map. + + Args: + in_shp (str): The input file path to the shapefile. + layer_name (str, optional): The layer name to be used.. Defaults to "Untitled". + style (dict, optional): A dictionary specifying the style to be used. Defaults to {}. + hover_style (dict, optional): Hover style dictionary. Defaults to {}. + style_callback (function, optional): Styling function that is called for each feature, + and should return the feature style. This styling function takes the feature as + argument. Defaults to None. + fill_colors (list, optional): The random colors to use for filling polygons. + Defaults to ["black"]. + info_mode (str, optional): Displays the attributes by either on_hover or on_click. + Any value other than "on_hover" or "on_click" will be treated as None. + Defaults to "on_hover". + + Raises: + FileNotFoundError: The provided shapefile could not be found. + """ + in_shp = os.path.abspath(in_shp) + if not os.path.exists(in_shp): + raise FileNotFoundError("The provided shapefile could not be found.") + + geojson = shp_to_geojson(in_shp) + self.add_geojson( + geojson, + layer_name, + style, + hover_style, + style_callback, + fill_colors, + info_mode, + ) + + def add_gdf( + self, + gdf, + layer_name="Untitled", + style=None, + hover_style=None, + style_callback=None, + fill_colors=None, + info_mode="on_hover", + zoom_to_layer=True, + ): + """Adds a GeoJSON file to the map. + + Args: + gdf (GeoDataFrame): A GeoPandas GeoDataFrame. + layer_name (str, optional): The layer name to be used.. Defaults to "Untitled". + style (dict, optional): A dictionary specifying the style to be used. Defaults to {}. + hover_style (dict, optional): Hover style dictionary. Defaults to {}. + style_callback (function, optional): Styling function that is called for each feature, + and should return the feature style. This styling function takes + the feature as argument. Defaults to None. + fill_colors (list, optional): The random colors to use for filling polygons. + Defaults to ["black"]. + info_mode (str, optional): Displays the attributes by either on_hover or on_click. + Any value other than "on_hover" or "on_click" will be treated as None. + Defaults to "on_hover". + zoom_to_layer (bool, optional): Whether to zoom to the layer. + """ + data = gdf_to_geojson(gdf, epsg="4326") + self.add_geojson( + data, + layer_name, + style, + hover_style, + style_callback, + fill_colors, + info_mode, + ) diff --git a/leafmap/leafmap.py b/leafmap/leafmap.py index 9d43f046da..b078618493 100644 --- a/leafmap/leafmap.py +++ b/leafmap/leafmap.py @@ -46,6 +46,8 @@ def __init__(self, **kwargs): self.layout.height = "600px" else: self.layout.height = kwargs["height"] + if "width" in kwargs: + self.layout.width = kwargs["width"] if "layers_control" not in kwargs: kwargs["layers_control"] = False From 17c0be8119e44d4b6b22999d73dd35286281eef5 Mon Sep 17 00:00:00 2001 From: "Kharude, Sachin" Date: Wed, 16 Jun 2021 20:06:48 +0530 Subject: [PATCH 2/4] Added methods to add vector data on map Signed-off-by: Kharude, Sachin --- docs/index.md | 2 +- examples/notebooks/16_heremap.ipynb | 129 ++++++++++++++- leafmap/heremap.py | 242 +++++++++++++++++++++++++--- requirements.txt | 2 +- 4 files changed, 352 insertions(+), 23 deletions(-) diff --git a/docs/index.md b/docs/index.md index d63bfa0000..34aee02b0b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,7 +23,7 @@ ## Introduction -**leafmap** is a Python package for geospatial analysis and interactive mapping in a Jupyter environment. It is a spin-off project of the [geemap](https://geemap.org) Python package, which was designed specifically to work with [Google Earth Engine](https://earthengine.google.com) (GEE). However, not everyone in the geospatial community has a GEE account. **leafmap** is designed to fill this gap for non-GEE users. It enables users to perform advanced geospatial analysis and interactive mapping with minimal coding in a Jupyter environment (e.g., Google Colab, JupyterLab, Jupyter notebook). It is built upon a number of open-source packages, such as [folium](https://github.com/python-visualization/folium) and [ipyleaflet](https://github.com/jupyter-widgets/ipyleaflet) (for creating interactive maps), [WhiteboxTools](https://github.com/jblindsay/whitebox-tools) and [whiteboxgui](https://github.com/giswqs/whiteboxgui) (for analyzing geospatial data), and [ipywidgets](https://github.com/jupyter-widgets/ipywidgets) (for designing interactive graphical user interface). +**leafmap** is a Python package for geospatial analysis and interactive mapping in a Jupyter environment. It is a spin-off project of the [geemap](https://geemap.org) Python package, which was designed specifically to work with [Google Earth Engine](https://earthengine.google.com) (GEE). However, not everyone in the geospatial community has a GEE account. **leafmap** is designed to fill this gap for non-GEE users. It enables users to perform advanced geospatial analysis and interactive mapping with minimal coding in a Jupyter environment (e.g., Google Colab, JupyterLab, Jupyter notebook). It is built upon a number of open-source packages, such as [folium](https://github.com/python-visualization/folium) and [ipyleaflet](https://github.com/jupyter-widgets/ipyleaflet) and [here-map-widget-for-jupyter](https://github.com/heremaps/here-map-widget-for-jupyter) (for creating interactive maps), [WhiteboxTools](https://github.com/jblindsay/whitebox-tools) and [whiteboxgui](https://github.com/giswqs/whiteboxgui) (for analyzing geospatial data), and [ipywidgets](https://github.com/jupyter-widgets/ipywidgets) (for designing interactive graphical user interface). The [WhiteboxTools](https://github.com/jblindsay/whitebox-tools) library currently contains **468** tools, which are each grouped based on their main function into one of the following categories. For a list of available tools with comprehensive documentation and usage details, please see the [WhiteboxTools User Manual](https://jblindsay.github.io/wbt_book/available_tools/index.html). diff --git a/examples/notebooks/16_heremap.ipynb b/examples/notebooks/16_heremap.ipynb index 3274d63448..ada9412897 100644 --- a/examples/notebooks/16_heremap.ipynb +++ b/examples/notebooks/16_heremap.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "a7424420", "metadata": {}, "source": [ "[![image](https://binder.pangeo.io/badge_logo.svg)](https://gishub.org/leafmap-pangeo)\n", @@ -12,6 +13,7 @@ { "cell_type": "code", "execution_count": null, + "id": "eeac34ce", "metadata": {}, "outputs": [], "source": [ @@ -20,6 +22,7 @@ }, { "cell_type": "markdown", + "id": "8c209034", "metadata": {}, "source": [ "## Prerequisites\n", @@ -36,6 +39,7 @@ { "cell_type": "code", "execution_count": null, + "id": "59c8949c", "metadata": {}, "outputs": [], "source": [ @@ -46,6 +50,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4847d0d4", "metadata": {}, "outputs": [], "source": [ @@ -56,6 +61,7 @@ }, { "cell_type": "markdown", + "id": "f005b1ad", "metadata": {}, "source": [ "## HERE default basemap" @@ -64,6 +70,7 @@ { "cell_type": "code", "execution_count": null, + "id": "46779f36", "metadata": {}, "outputs": [], "source": [ @@ -74,6 +81,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4c766874", "metadata": {}, "outputs": [], "source": [ @@ -84,6 +92,7 @@ { "cell_type": "code", "execution_count": null, + "id": "de61460b", "metadata": {}, "outputs": [], "source": [ @@ -94,6 +103,7 @@ { "cell_type": "code", "execution_count": null, + "id": "0db899d8", "metadata": {}, "outputs": [], "source": [ @@ -103,6 +113,7 @@ }, { "cell_type": "markdown", + "id": "7eedd319", "metadata": {}, "source": [ "## Basemaps" @@ -111,6 +122,7 @@ { "cell_type": "code", "execution_count": null, + "id": "194c557c", "metadata": {}, "outputs": [], "source": [ @@ -118,9 +130,28 @@ "m" ] }, + { + "cell_type": "markdown", + "id": "e98ba5e9", + "metadata": {}, + "source": [ + "### zoom to bounds" + ] + }, { "cell_type": "code", "execution_count": null, + "id": "2f45058d", + "metadata": {}, + "outputs": [], + "source": [ + "m.zoom_to_bounds((-9.0882278, -55.3228175, 168.2249543, 72.2460938)) # South West North East" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b89127d1", "metadata": {}, "outputs": [], "source": [ @@ -130,6 +161,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4eac1fe9", "metadata": {}, "outputs": [], "source": [ @@ -140,6 +172,7 @@ }, { "cell_type": "markdown", + "id": "3a3790fe", "metadata": {}, "source": [ "## Add vector data\n", @@ -149,6 +182,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e90cc80a", "metadata": {}, "outputs": [], "source": [ @@ -163,6 +197,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3aae4b8b", "metadata": {}, "outputs": [], "source": [ @@ -178,6 +213,7 @@ { "cell_type": "code", "execution_count": null, + "id": "92da3625", "metadata": {}, "outputs": [], "source": [ @@ -194,6 +230,97 @@ "m.add_geojson(url, layer_name=\"Countries\", style=style, hover_style=hover_style)\n", "m" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1d2c714", + "metadata": {}, + "outputs": [], + "source": [ + "in_shp = '../data/countries.shp'\n", + "in_geojson = '../data/us-states.json'\n", + "in_kml = '../data/us-states.kml'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f933d905", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(api_key=api_key, center=[0, 0], zoom=2)\n", + "m.add_shp(in_shp, layer_name=\"Shapefile\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "971acbff", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(api_key=api_key, center=[40.273502, -86.126976], zoom=4)\n", + "m.add_kml(in_kml, layer_name=\"KML\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3fad34a2", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(api_key=api_key, center=[0, 0], zoom=2)\n", + "url = \"https://raw.githubusercontent.com/giswqs/leafmap/master/examples/data/countries.geojson\"\n", + "m.add_vector(url, layer_name=\"Countries\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "e6f21a6c", + "metadata": {}, + "source": [ + "### Point style for GeoJSON" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ffb5367", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(api_key=api_key, center=[0, 0], zoom=2)\n", + "\n", + "url = \"http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_month.geojson\"\n", + "point_style = {\"strokeColor\": 'white', \"lineWidth\": 1, \"fillColor\": \"red\", \"fillOpacity\": 0.7, \"radius\": 5}\n", + "m.add_geojson(url, layer_name=\"Countries\", point_style=point_style, default_popup=True)\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "144aa0d8", + "metadata": {}, + "outputs": [], + "source": [ + "import geopandas\n", + "import json\n", + "import os\n", + "\n", + "countries = geopandas.read_file(geopandas.datasets.get_path(\"naturalearth_cities\"))\n", + "point_style = {\"strokeColor\": 'white', \"lineWidth\": 1, \"fillColor\": \"blue\", \"fillOpacity\": 0.7, \"radius\": 5}\n", + "\n", + "m = leafmap.Map(api_key=api_key, center=[0, 0], zoom=3)\n", + "m.add_gdf(countries, zoom_to_layer=False, point_style=point_style, default_popup=True)\n", + "m" + ] } ], "metadata": { @@ -213,7 +340,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.9.5" }, "toc": { "base_numbering": 1, diff --git a/leafmap/heremap.py b/leafmap/heremap.py index 16190e1358..0cdd7e3032 100644 --- a/leafmap/heremap.py +++ b/leafmap/heremap.py @@ -10,7 +10,7 @@ import here_map_widget import ipywidgets as widgets from .basemaps import here_basemaps -from .common import shp_to_geojson, gdf_to_geojson +from .common import shp_to_geojson, gdf_to_geojson, vector_to_geojson from here_map_widget import ( FullscreenControl, @@ -87,6 +87,24 @@ def set_center(self, lon, lat, zoom=None): if zoom is not None: self.zoom = zoom + def zoom_to_bounds(self, bounds): + """Zooms to a bounding box in the form of [south, west, north, east]. + + Args: + bounds (list | tuple): A list/tuple containing south, west, north, east values for the + bounds. + """ + self.bounds = tuple(bounds) + + 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 add_basemap(self, basemap="HYBRID"): """Adds a basemap to the map. @@ -109,7 +127,7 @@ def add_tile_layer( name="Untitled", attribution="", opacity=1.0, - **kwargs + **kwargs, ): """Adds a TileLayer to the map. @@ -143,6 +161,8 @@ def add_geojson( style_callback=None, fill_colors=None, info_mode="on_hover", + point_style=None, + default_popup=False, ): """Adds a GeoJSON file to the map. @@ -160,6 +180,10 @@ def add_geojson( info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover". + point_style (dict, optional): style dictionary for Points in GeoJSON. If not provided + default Markers will be shown. + default_popup: If set to True this will disable info_mode and default popup will be + shown on clicking the feature. Raises: FileNotFoundError: The provided GeoJSON file could not be found. """ @@ -220,7 +244,7 @@ def add_geojson( output_widget = widgets.VBox([widgets.HBox([toolbar_button, close_button]), html]) info_control = WidgetControl(widget=output_widget, position="bottomright") - if info_mode in ["on_hover", "on_click"]: + if not default_popup and info_mode in ["on_hover", "on_click"]: self.add_control(info_control) def _toolbar_btn_click(change): @@ -259,12 +283,15 @@ def _update_html(feature, **_): hover_style=hover_style if hover_style else {}, style_callback=style_callback, name=layer_name, + point_style=point_style if point_style else {}, + show_bubble=default_popup, ) - if info_mode == "on_hover": - geojson.on_hover(_update_html) - elif info_mode == "on_click": - geojson.on_click(_update_html) + if not default_popup: + if info_mode == "on_hover": + geojson.on_hover(_update_html) + elif info_mode == "on_click": + geojson.on_click(_update_html) self.add_layer(geojson) @@ -277,6 +304,8 @@ def add_shp( style_callback=None, fill_colors=None, info_mode="on_hover", + point_style=None, + default_popup=False, ): """Adds a shapefile to the map. @@ -293,6 +322,10 @@ def add_shp( info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover". + point_style (dict, optional): style dictionary for Points in GeoJSON. If not provided + default Markers will be shown. + default_popup: If set to True this will disable info_mode and default popup will be + shown on clicking the feature. Raises: FileNotFoundError: The provided shapefile could not be found. @@ -303,13 +336,15 @@ def add_shp( geojson = shp_to_geojson(in_shp) self.add_geojson( - geojson, - layer_name, - style, - hover_style, - style_callback, - fill_colors, - info_mode, + in_geojson=geojson, + layer_name=layer_name, + style=style, + hover_style=hover_style, + style_callback=style_callback, + fill_colors=fill_colors, + info_mode=info_mode, + point_style=point_style, + default_popup=default_popup, ) def add_gdf( @@ -322,6 +357,8 @@ def add_gdf( fill_colors=None, info_mode="on_hover", zoom_to_layer=True, + point_style=None, + default_popup=False, ): """Adds a GeoJSON file to the map. @@ -339,14 +376,179 @@ def add_gdf( Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover". zoom_to_layer (bool, optional): Whether to zoom to the layer. + point_style (dict, optional): style dictionary for Points in GeoJSON. If not provided + default Markers will be shown. + default_popup: If set to True this will disable info_mode and default popup will be + shown on clicking the feature. """ data = gdf_to_geojson(gdf, epsg="4326") self.add_geojson( - data, + in_geojson=data, + layer_name=layer_name, + style=style, + hover_style=hover_style, + style_callback=style_callback, + fill_colors=fill_colors, + info_mode=info_mode, + point_style=point_style, + default_popup=default_popup, + ) + 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"]) + print((south, west, north, east)) + self.bounds = (south, west, north, east) + + def add_kml( + self, + in_kml, + layer_name="Untitled", + style=None, + hover_style=None, + style_callback=None, + fill_colors=None, + info_mode="on_hover", + point_style=None, + default_popup=False, + ): + """Adds a GeoJSON file to the map. + + Args: + in_kml (str): The input file path to the KML. + layer_name (str, optional): The layer name to be used.. Defaults to "Untitled". + style (dict, optional): A dictionary specifying the style to be used. Defaults to {}. + hover_style (dict, optional): Hover style dictionary. Defaults to {}. + style_callback (function, optional): Styling function that is called for each feature, + and should return the feature style. This styling function takes the feature + as argument. Defaults to None. + fill_colors (list, optional): The random colors to use for filling polygons. + Defaults to ["black"]. + info_mode (str, optional): Displays the attributes by either on_hover or on_click. + Any value other than "on_hover" or "on_click" will be treated as None. + Defaults to "on_hover". + point_style (dict, optional): style dictionary for Points in GeoJSON. If not provided + default Markers will be shown. + default_popup: If set to True this will disable info_mode and default popup will be + shown on clicking the feature. + + Raises: + FileNotFoundError: The provided KML file could not be found. + """ + + in_kml = os.path.abspath(in_kml) + if not os.path.exists(in_kml): + raise FileNotFoundError("The provided KML file could not be found.") + self.add_vector( + in_kml, layer_name, - style, - hover_style, - style_callback, - fill_colors, - info_mode, + style=style, + hover_style=hover_style, + style_callback=style_callback, + fill_colors=fill_colors, + info_mode=info_mode, + point_style=point_style, + default_popup=default_popup, ) + + def add_vector( + self, + filename, + layer_name="Untitled", + bbox=None, + mask=None, + rows=None, + style=None, + hover_style=None, + style_callback=None, + fill_colors=None, + info_mode="on_hover", + point_style=None, + default_popup=False, + **kwargs, + ): + """Adds any geopandas-supported vector dataset to the map. + + Args: + filename (str): Either the absolute or relative path to the file or URL to be opened, + or any object with a read() method (such as an open file or StringIO). + layer_name (str, optional): The layer name to use. Defaults to "Untitled". + bbox (tuple | GeoDataFrame or GeoSeries | shapely Geometry, optional): Filter features + by given bounding box, GeoSeries, GeoDataFrame or a shapely geometry. + CRS mis-matches are resolved if given a GeoSeries or GeoDataFrame. + Cannot be used with mask. Defaults to None. + mask (dict | GeoDataFrame or GeoSeries | shapely Geometry, optional): Filter for + features that intersect with the given dict-like geojson geometry, GeoSeries, + GeoDataFrame or shapely geometry. CRS mis-matches are resolved if given a GeoSeries or + GeoDataFrame. Cannot be used with bbox. Defaults to None. + rows (int or slice, optional): Load in specific rows by passing an integer + (first n rows) or a slice() object.. Defaults to None. + style (dict, optional): A dictionary specifying the style to be used. Defaults to {}. + hover_style (dict, optional): Hover style dictionary. Defaults to {}. + style_callback (function, optional): Styling function that is called for each feature, + and should return the feature style. This styling function takes the feature as + argument. Defaults to None. + fill_colors (list, optional): The random colors to use for filling polygons. + Defaults to ["black"]. + info_mode (str, optional): Displays the attributes by either on_hover or on_click. + Any value other than "on_hover" or "on_click" will be treated as None. + Defaults to "on_hover". + point_style (dict, optional): style dictionary for Points in GeoJSON. If not provided + default Markers will be shown. + default_popup: If set to True this will disable info_mode and default popup will be + shown on clicking the feature. + + """ + if not filename.startswith("http"): + filename = os.path.abspath(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, + point_style, + default_popup, + ) + elif ext in [".json", ".geojson"]: + self.add_geojson( + in_geojson=filename, + layer_name=layer_name, + style=style, + hover_style=hover_style, + style_callback=style_callback, + fill_colors=fill_colors, + info_mode=info_mode, + point_style=point_style, + default_popup=default_popup, + ) + else: + geojson = vector_to_geojson( + filename, + bbox=bbox, + mask=mask, + rows=rows, + epsg="4326", + **kwargs, + ) + + self.add_geojson( + in_geojson=geojson, + layer_name=layer_name, + style=style, + hover_style=hover_style, + style_callback=style_callback, + fill_colors=fill_colors, + info_mode=info_mode, + point_style=point_style, + default_popup=default_popup, + ) diff --git a/requirements.txt b/requirements.txt index e1c9ec7e99..424ad5b805 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,4 @@ pyshp>=2.1.3 python-box whitebox whiteboxgui -here-map-widget-for-jupyter>=1.1.0 +here-map-widget-for-jupyter>=1.1.1 From 2d88b9cbf46e907eefa8682e752af35aea4d65f8 Mon Sep 17 00:00:00 2001 From: "Kharude, Sachin" Date: Mon, 14 Jun 2021 19:22:09 +0530 Subject: [PATCH 3/4] Initial changes Signed-off-by: Kharude, Sachin --- .gitignore | 3 +- leafmap/heremap.py | 107 ++++++++++++++++++++++++++++++++++++++++++--- leafmap/leafmap.py | 2 + 3 files changed, 105 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 90820bcf8d..25a015189b 100644 --- a/.gitignore +++ b/.gitignore @@ -105,4 +105,5 @@ ENV/ .mypy_cache/ # IDE settings -.vscode/ \ No newline at end of file +.vscode/ +.idea/ diff --git a/leafmap/heremap.py b/leafmap/heremap.py index 8a25b7e15f..16190e1358 100644 --- a/leafmap/heremap.py +++ b/leafmap/heremap.py @@ -1,6 +1,7 @@ """ This module defines here-map-widget-for-jupyter as backend for leafmap library. -For more details about Here Map Widget for Jupyter please check: https://github.com/heremaps/here-map-widget-for-jupyter +For more details about Here Map Widget for Jupyter +please check: https://github.com/heremaps/here-map-widget-for-jupyter """ import os import json @@ -9,6 +10,7 @@ import here_map_widget import ipywidgets as widgets from .basemaps import here_basemaps +from .common import shp_to_geojson, gdf_to_geojson from here_map_widget import ( FullscreenControl, @@ -50,6 +52,9 @@ def __init__(self, api_key, **kwargs): else: self.layout.height = kwargs["height"] + if "width" in kwargs: + self.layout.width = kwargs["width"] + if kwargs.get("layers_control"): self.add_control(LayersControl(alignment="RIGHT_TOP")) @@ -109,7 +114,8 @@ def add_tile_layer( """Adds a TileLayer to the map. Args: - url (str, optional): The URL of the tile layer. Defaults to 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'. + url (str, optional): The URL of the tile layer. + Defaults to 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'. name (str, optional): The layer name to use for the layer. Defaults to 'Untitled'. attribution (str, optional): The attribution to use. Defaults to ''. opacity (float, optional): The opacity of the layer. Defaults to 1. @@ -141,13 +147,19 @@ def add_geojson( """Adds a GeoJSON file to the map. Args: - in_geojson (str | dict): The file path or http URL to the input GeoJSON or a dictionary containing the geojson. + in_geojson (str | dict): The file path or http URL to the input GeoJSON or a + dictionary containing the geojson. layer_name (str, optional): The layer name to be used.. Defaults to "Untitled". style (dict, optional): A dictionary specifying the style to be used. Defaults to {}. hover_style (dict, optional): Hover style dictionary. Defaults to {}. - style_callback (function, optional): Styling function that is called for each feature, and should return the feature style. This styling function takes the feature as argument. Defaults to None. - fill_colors (list, optional): The random colors to use for filling polygons. Defaults to ["black"]. - info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover". + style_callback (function, optional): Styling function that is called for each feature, + and should return the feature style. This styling function takes the feature + as argument. Defaults to None. + fill_colors (list, optional): The random colors to use for filling polygons. + Defaults to ["black"]. + info_mode (str, optional): Displays the attributes by either on_hover or on_click. + Any value other than "on_hover" or "on_click" will be treated as None. + Defaults to "on_hover". Raises: FileNotFoundError: The provided GeoJSON file could not be found. """ @@ -255,3 +267,86 @@ def _update_html(feature, **_): geojson.on_click(_update_html) self.add_layer(geojson) + + def add_shp( + self, + in_shp, + layer_name="Untitled", + style=None, + hover_style=None, + style_callback=None, + fill_colors=None, + info_mode="on_hover", + ): + """Adds a shapefile to the map. + + Args: + in_shp (str): The input file path to the shapefile. + layer_name (str, optional): The layer name to be used.. Defaults to "Untitled". + style (dict, optional): A dictionary specifying the style to be used. Defaults to {}. + hover_style (dict, optional): Hover style dictionary. Defaults to {}. + style_callback (function, optional): Styling function that is called for each feature, + and should return the feature style. This styling function takes the feature as + argument. Defaults to None. + fill_colors (list, optional): The random colors to use for filling polygons. + Defaults to ["black"]. + info_mode (str, optional): Displays the attributes by either on_hover or on_click. + Any value other than "on_hover" or "on_click" will be treated as None. + Defaults to "on_hover". + + Raises: + FileNotFoundError: The provided shapefile could not be found. + """ + in_shp = os.path.abspath(in_shp) + if not os.path.exists(in_shp): + raise FileNotFoundError("The provided shapefile could not be found.") + + geojson = shp_to_geojson(in_shp) + self.add_geojson( + geojson, + layer_name, + style, + hover_style, + style_callback, + fill_colors, + info_mode, + ) + + def add_gdf( + self, + gdf, + layer_name="Untitled", + style=None, + hover_style=None, + style_callback=None, + fill_colors=None, + info_mode="on_hover", + zoom_to_layer=True, + ): + """Adds a GeoJSON file to the map. + + Args: + gdf (GeoDataFrame): A GeoPandas GeoDataFrame. + layer_name (str, optional): The layer name to be used.. Defaults to "Untitled". + style (dict, optional): A dictionary specifying the style to be used. Defaults to {}. + hover_style (dict, optional): Hover style dictionary. Defaults to {}. + style_callback (function, optional): Styling function that is called for each feature, + and should return the feature style. This styling function takes + the feature as argument. Defaults to None. + fill_colors (list, optional): The random colors to use for filling polygons. + Defaults to ["black"]. + info_mode (str, optional): Displays the attributes by either on_hover or on_click. + Any value other than "on_hover" or "on_click" will be treated as None. + Defaults to "on_hover". + zoom_to_layer (bool, optional): Whether to zoom to the layer. + """ + data = gdf_to_geojson(gdf, epsg="4326") + self.add_geojson( + data, + layer_name, + style, + hover_style, + style_callback, + fill_colors, + info_mode, + ) diff --git a/leafmap/leafmap.py b/leafmap/leafmap.py index bea05e892d..eaa74af3a4 100644 --- a/leafmap/leafmap.py +++ b/leafmap/leafmap.py @@ -47,6 +47,8 @@ def __init__(self, **kwargs): self.layout.height = "600px" else: self.layout.height = kwargs["height"] + if "width" in kwargs: + self.layout.width = kwargs["width"] if "layers_control" not in kwargs: kwargs["layers_control"] = False From 249b46fc176a6fe1702163e95fa590f8df5e249c Mon Sep 17 00:00:00 2001 From: "Kharude, Sachin" Date: Wed, 16 Jun 2021 20:06:48 +0530 Subject: [PATCH 4/4] Added methods to add vector data on map Signed-off-by: Kharude, Sachin --- docs/index.md | 2 +- examples/notebooks/16_heremap.ipynb | 129 ++++++++++++++- leafmap/heremap.py | 242 +++++++++++++++++++++++++--- requirements.txt | 2 +- 4 files changed, 352 insertions(+), 23 deletions(-) diff --git a/docs/index.md b/docs/index.md index d63bfa0000..34aee02b0b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,7 +23,7 @@ ## Introduction -**leafmap** is a Python package for geospatial analysis and interactive mapping in a Jupyter environment. It is a spin-off project of the [geemap](https://geemap.org) Python package, which was designed specifically to work with [Google Earth Engine](https://earthengine.google.com) (GEE). However, not everyone in the geospatial community has a GEE account. **leafmap** is designed to fill this gap for non-GEE users. It enables users to perform advanced geospatial analysis and interactive mapping with minimal coding in a Jupyter environment (e.g., Google Colab, JupyterLab, Jupyter notebook). It is built upon a number of open-source packages, such as [folium](https://github.com/python-visualization/folium) and [ipyleaflet](https://github.com/jupyter-widgets/ipyleaflet) (for creating interactive maps), [WhiteboxTools](https://github.com/jblindsay/whitebox-tools) and [whiteboxgui](https://github.com/giswqs/whiteboxgui) (for analyzing geospatial data), and [ipywidgets](https://github.com/jupyter-widgets/ipywidgets) (for designing interactive graphical user interface). +**leafmap** is a Python package for geospatial analysis and interactive mapping in a Jupyter environment. It is a spin-off project of the [geemap](https://geemap.org) Python package, which was designed specifically to work with [Google Earth Engine](https://earthengine.google.com) (GEE). However, not everyone in the geospatial community has a GEE account. **leafmap** is designed to fill this gap for non-GEE users. It enables users to perform advanced geospatial analysis and interactive mapping with minimal coding in a Jupyter environment (e.g., Google Colab, JupyterLab, Jupyter notebook). It is built upon a number of open-source packages, such as [folium](https://github.com/python-visualization/folium) and [ipyleaflet](https://github.com/jupyter-widgets/ipyleaflet) and [here-map-widget-for-jupyter](https://github.com/heremaps/here-map-widget-for-jupyter) (for creating interactive maps), [WhiteboxTools](https://github.com/jblindsay/whitebox-tools) and [whiteboxgui](https://github.com/giswqs/whiteboxgui) (for analyzing geospatial data), and [ipywidgets](https://github.com/jupyter-widgets/ipywidgets) (for designing interactive graphical user interface). The [WhiteboxTools](https://github.com/jblindsay/whitebox-tools) library currently contains **468** tools, which are each grouped based on their main function into one of the following categories. For a list of available tools with comprehensive documentation and usage details, please see the [WhiteboxTools User Manual](https://jblindsay.github.io/wbt_book/available_tools/index.html). diff --git a/examples/notebooks/16_heremap.ipynb b/examples/notebooks/16_heremap.ipynb index 3274d63448..ada9412897 100644 --- a/examples/notebooks/16_heremap.ipynb +++ b/examples/notebooks/16_heremap.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "a7424420", "metadata": {}, "source": [ "[![image](https://binder.pangeo.io/badge_logo.svg)](https://gishub.org/leafmap-pangeo)\n", @@ -12,6 +13,7 @@ { "cell_type": "code", "execution_count": null, + "id": "eeac34ce", "metadata": {}, "outputs": [], "source": [ @@ -20,6 +22,7 @@ }, { "cell_type": "markdown", + "id": "8c209034", "metadata": {}, "source": [ "## Prerequisites\n", @@ -36,6 +39,7 @@ { "cell_type": "code", "execution_count": null, + "id": "59c8949c", "metadata": {}, "outputs": [], "source": [ @@ -46,6 +50,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4847d0d4", "metadata": {}, "outputs": [], "source": [ @@ -56,6 +61,7 @@ }, { "cell_type": "markdown", + "id": "f005b1ad", "metadata": {}, "source": [ "## HERE default basemap" @@ -64,6 +70,7 @@ { "cell_type": "code", "execution_count": null, + "id": "46779f36", "metadata": {}, "outputs": [], "source": [ @@ -74,6 +81,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4c766874", "metadata": {}, "outputs": [], "source": [ @@ -84,6 +92,7 @@ { "cell_type": "code", "execution_count": null, + "id": "de61460b", "metadata": {}, "outputs": [], "source": [ @@ -94,6 +103,7 @@ { "cell_type": "code", "execution_count": null, + "id": "0db899d8", "metadata": {}, "outputs": [], "source": [ @@ -103,6 +113,7 @@ }, { "cell_type": "markdown", + "id": "7eedd319", "metadata": {}, "source": [ "## Basemaps" @@ -111,6 +122,7 @@ { "cell_type": "code", "execution_count": null, + "id": "194c557c", "metadata": {}, "outputs": [], "source": [ @@ -118,9 +130,28 @@ "m" ] }, + { + "cell_type": "markdown", + "id": "e98ba5e9", + "metadata": {}, + "source": [ + "### zoom to bounds" + ] + }, { "cell_type": "code", "execution_count": null, + "id": "2f45058d", + "metadata": {}, + "outputs": [], + "source": [ + "m.zoom_to_bounds((-9.0882278, -55.3228175, 168.2249543, 72.2460938)) # South West North East" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b89127d1", "metadata": {}, "outputs": [], "source": [ @@ -130,6 +161,7 @@ { "cell_type": "code", "execution_count": null, + "id": "4eac1fe9", "metadata": {}, "outputs": [], "source": [ @@ -140,6 +172,7 @@ }, { "cell_type": "markdown", + "id": "3a3790fe", "metadata": {}, "source": [ "## Add vector data\n", @@ -149,6 +182,7 @@ { "cell_type": "code", "execution_count": null, + "id": "e90cc80a", "metadata": {}, "outputs": [], "source": [ @@ -163,6 +197,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3aae4b8b", "metadata": {}, "outputs": [], "source": [ @@ -178,6 +213,7 @@ { "cell_type": "code", "execution_count": null, + "id": "92da3625", "metadata": {}, "outputs": [], "source": [ @@ -194,6 +230,97 @@ "m.add_geojson(url, layer_name=\"Countries\", style=style, hover_style=hover_style)\n", "m" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1d2c714", + "metadata": {}, + "outputs": [], + "source": [ + "in_shp = '../data/countries.shp'\n", + "in_geojson = '../data/us-states.json'\n", + "in_kml = '../data/us-states.kml'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f933d905", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(api_key=api_key, center=[0, 0], zoom=2)\n", + "m.add_shp(in_shp, layer_name=\"Shapefile\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "971acbff", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(api_key=api_key, center=[40.273502, -86.126976], zoom=4)\n", + "m.add_kml(in_kml, layer_name=\"KML\")\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3fad34a2", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(api_key=api_key, center=[0, 0], zoom=2)\n", + "url = \"https://raw.githubusercontent.com/giswqs/leafmap/master/examples/data/countries.geojson\"\n", + "m.add_vector(url, layer_name=\"Countries\")\n", + "m" + ] + }, + { + "cell_type": "markdown", + "id": "e6f21a6c", + "metadata": {}, + "source": [ + "### Point style for GeoJSON" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ffb5367", + "metadata": {}, + "outputs": [], + "source": [ + "m = leafmap.Map(api_key=api_key, center=[0, 0], zoom=2)\n", + "\n", + "url = \"http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_month.geojson\"\n", + "point_style = {\"strokeColor\": 'white', \"lineWidth\": 1, \"fillColor\": \"red\", \"fillOpacity\": 0.7, \"radius\": 5}\n", + "m.add_geojson(url, layer_name=\"Countries\", point_style=point_style, default_popup=True)\n", + "m" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "144aa0d8", + "metadata": {}, + "outputs": [], + "source": [ + "import geopandas\n", + "import json\n", + "import os\n", + "\n", + "countries = geopandas.read_file(geopandas.datasets.get_path(\"naturalearth_cities\"))\n", + "point_style = {\"strokeColor\": 'white', \"lineWidth\": 1, \"fillColor\": \"blue\", \"fillOpacity\": 0.7, \"radius\": 5}\n", + "\n", + "m = leafmap.Map(api_key=api_key, center=[0, 0], zoom=3)\n", + "m.add_gdf(countries, zoom_to_layer=False, point_style=point_style, default_popup=True)\n", + "m" + ] } ], "metadata": { @@ -213,7 +340,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.5" + "version": "3.9.5" }, "toc": { "base_numbering": 1, diff --git a/leafmap/heremap.py b/leafmap/heremap.py index 16190e1358..0cdd7e3032 100644 --- a/leafmap/heremap.py +++ b/leafmap/heremap.py @@ -10,7 +10,7 @@ import here_map_widget import ipywidgets as widgets from .basemaps import here_basemaps -from .common import shp_to_geojson, gdf_to_geojson +from .common import shp_to_geojson, gdf_to_geojson, vector_to_geojson from here_map_widget import ( FullscreenControl, @@ -87,6 +87,24 @@ def set_center(self, lon, lat, zoom=None): if zoom is not None: self.zoom = zoom + def zoom_to_bounds(self, bounds): + """Zooms to a bounding box in the form of [south, west, north, east]. + + Args: + bounds (list | tuple): A list/tuple containing south, west, north, east values for the + bounds. + """ + self.bounds = tuple(bounds) + + 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 add_basemap(self, basemap="HYBRID"): """Adds a basemap to the map. @@ -109,7 +127,7 @@ def add_tile_layer( name="Untitled", attribution="", opacity=1.0, - **kwargs + **kwargs, ): """Adds a TileLayer to the map. @@ -143,6 +161,8 @@ def add_geojson( style_callback=None, fill_colors=None, info_mode="on_hover", + point_style=None, + default_popup=False, ): """Adds a GeoJSON file to the map. @@ -160,6 +180,10 @@ def add_geojson( info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover". + point_style (dict, optional): style dictionary for Points in GeoJSON. If not provided + default Markers will be shown. + default_popup: If set to True this will disable info_mode and default popup will be + shown on clicking the feature. Raises: FileNotFoundError: The provided GeoJSON file could not be found. """ @@ -220,7 +244,7 @@ def add_geojson( output_widget = widgets.VBox([widgets.HBox([toolbar_button, close_button]), html]) info_control = WidgetControl(widget=output_widget, position="bottomright") - if info_mode in ["on_hover", "on_click"]: + if not default_popup and info_mode in ["on_hover", "on_click"]: self.add_control(info_control) def _toolbar_btn_click(change): @@ -259,12 +283,15 @@ def _update_html(feature, **_): hover_style=hover_style if hover_style else {}, style_callback=style_callback, name=layer_name, + point_style=point_style if point_style else {}, + show_bubble=default_popup, ) - if info_mode == "on_hover": - geojson.on_hover(_update_html) - elif info_mode == "on_click": - geojson.on_click(_update_html) + if not default_popup: + if info_mode == "on_hover": + geojson.on_hover(_update_html) + elif info_mode == "on_click": + geojson.on_click(_update_html) self.add_layer(geojson) @@ -277,6 +304,8 @@ def add_shp( style_callback=None, fill_colors=None, info_mode="on_hover", + point_style=None, + default_popup=False, ): """Adds a shapefile to the map. @@ -293,6 +322,10 @@ def add_shp( info_mode (str, optional): Displays the attributes by either on_hover or on_click. Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover". + point_style (dict, optional): style dictionary for Points in GeoJSON. If not provided + default Markers will be shown. + default_popup: If set to True this will disable info_mode and default popup will be + shown on clicking the feature. Raises: FileNotFoundError: The provided shapefile could not be found. @@ -303,13 +336,15 @@ def add_shp( geojson = shp_to_geojson(in_shp) self.add_geojson( - geojson, - layer_name, - style, - hover_style, - style_callback, - fill_colors, - info_mode, + in_geojson=geojson, + layer_name=layer_name, + style=style, + hover_style=hover_style, + style_callback=style_callback, + fill_colors=fill_colors, + info_mode=info_mode, + point_style=point_style, + default_popup=default_popup, ) def add_gdf( @@ -322,6 +357,8 @@ def add_gdf( fill_colors=None, info_mode="on_hover", zoom_to_layer=True, + point_style=None, + default_popup=False, ): """Adds a GeoJSON file to the map. @@ -339,14 +376,179 @@ def add_gdf( Any value other than "on_hover" or "on_click" will be treated as None. Defaults to "on_hover". zoom_to_layer (bool, optional): Whether to zoom to the layer. + point_style (dict, optional): style dictionary for Points in GeoJSON. If not provided + default Markers will be shown. + default_popup: If set to True this will disable info_mode and default popup will be + shown on clicking the feature. """ data = gdf_to_geojson(gdf, epsg="4326") self.add_geojson( - data, + in_geojson=data, + layer_name=layer_name, + style=style, + hover_style=hover_style, + style_callback=style_callback, + fill_colors=fill_colors, + info_mode=info_mode, + point_style=point_style, + default_popup=default_popup, + ) + 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"]) + print((south, west, north, east)) + self.bounds = (south, west, north, east) + + def add_kml( + self, + in_kml, + layer_name="Untitled", + style=None, + hover_style=None, + style_callback=None, + fill_colors=None, + info_mode="on_hover", + point_style=None, + default_popup=False, + ): + """Adds a GeoJSON file to the map. + + Args: + in_kml (str): The input file path to the KML. + layer_name (str, optional): The layer name to be used.. Defaults to "Untitled". + style (dict, optional): A dictionary specifying the style to be used. Defaults to {}. + hover_style (dict, optional): Hover style dictionary. Defaults to {}. + style_callback (function, optional): Styling function that is called for each feature, + and should return the feature style. This styling function takes the feature + as argument. Defaults to None. + fill_colors (list, optional): The random colors to use for filling polygons. + Defaults to ["black"]. + info_mode (str, optional): Displays the attributes by either on_hover or on_click. + Any value other than "on_hover" or "on_click" will be treated as None. + Defaults to "on_hover". + point_style (dict, optional): style dictionary for Points in GeoJSON. If not provided + default Markers will be shown. + default_popup: If set to True this will disable info_mode and default popup will be + shown on clicking the feature. + + Raises: + FileNotFoundError: The provided KML file could not be found. + """ + + in_kml = os.path.abspath(in_kml) + if not os.path.exists(in_kml): + raise FileNotFoundError("The provided KML file could not be found.") + self.add_vector( + in_kml, layer_name, - style, - hover_style, - style_callback, - fill_colors, - info_mode, + style=style, + hover_style=hover_style, + style_callback=style_callback, + fill_colors=fill_colors, + info_mode=info_mode, + point_style=point_style, + default_popup=default_popup, ) + + def add_vector( + self, + filename, + layer_name="Untitled", + bbox=None, + mask=None, + rows=None, + style=None, + hover_style=None, + style_callback=None, + fill_colors=None, + info_mode="on_hover", + point_style=None, + default_popup=False, + **kwargs, + ): + """Adds any geopandas-supported vector dataset to the map. + + Args: + filename (str): Either the absolute or relative path to the file or URL to be opened, + or any object with a read() method (such as an open file or StringIO). + layer_name (str, optional): The layer name to use. Defaults to "Untitled". + bbox (tuple | GeoDataFrame or GeoSeries | shapely Geometry, optional): Filter features + by given bounding box, GeoSeries, GeoDataFrame or a shapely geometry. + CRS mis-matches are resolved if given a GeoSeries or GeoDataFrame. + Cannot be used with mask. Defaults to None. + mask (dict | GeoDataFrame or GeoSeries | shapely Geometry, optional): Filter for + features that intersect with the given dict-like geojson geometry, GeoSeries, + GeoDataFrame or shapely geometry. CRS mis-matches are resolved if given a GeoSeries or + GeoDataFrame. Cannot be used with bbox. Defaults to None. + rows (int or slice, optional): Load in specific rows by passing an integer + (first n rows) or a slice() object.. Defaults to None. + style (dict, optional): A dictionary specifying the style to be used. Defaults to {}. + hover_style (dict, optional): Hover style dictionary. Defaults to {}. + style_callback (function, optional): Styling function that is called for each feature, + and should return the feature style. This styling function takes the feature as + argument. Defaults to None. + fill_colors (list, optional): The random colors to use for filling polygons. + Defaults to ["black"]. + info_mode (str, optional): Displays the attributes by either on_hover or on_click. + Any value other than "on_hover" or "on_click" will be treated as None. + Defaults to "on_hover". + point_style (dict, optional): style dictionary for Points in GeoJSON. If not provided + default Markers will be shown. + default_popup: If set to True this will disable info_mode and default popup will be + shown on clicking the feature. + + """ + if not filename.startswith("http"): + filename = os.path.abspath(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, + point_style, + default_popup, + ) + elif ext in [".json", ".geojson"]: + self.add_geojson( + in_geojson=filename, + layer_name=layer_name, + style=style, + hover_style=hover_style, + style_callback=style_callback, + fill_colors=fill_colors, + info_mode=info_mode, + point_style=point_style, + default_popup=default_popup, + ) + else: + geojson = vector_to_geojson( + filename, + bbox=bbox, + mask=mask, + rows=rows, + epsg="4326", + **kwargs, + ) + + self.add_geojson( + in_geojson=geojson, + layer_name=layer_name, + style=style, + hover_style=hover_style, + style_callback=style_callback, + fill_colors=fill_colors, + info_mode=info_mode, + point_style=point_style, + default_popup=default_popup, + ) diff --git a/requirements.txt b/requirements.txt index e1c9ec7e99..424ad5b805 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,4 @@ pyshp>=2.1.3 python-box whitebox whiteboxgui -here-map-widget-for-jupyter>=1.1.0 +here-map-widget-for-jupyter>=1.1.1