Skip to content

Commit

Permalink
Added plotly toolbar #160
Browse files Browse the repository at this point in the history
  • Loading branch information
giswqs committed Dec 29, 2021
1 parent afd1bce commit f0d0edb
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 1 deletion.
20 changes: 20 additions & 0 deletions leafmap/common.py
Expand Up @@ -4068,3 +4068,23 @@ def bbox_to_gdf(bbox, crs="EPSG:4326"):
gdf = GeoDataFrame(d, crs="EPSG:4326")
gdf.to_crs(crs=crs, inplace=True)
return gdf


def gdf_to_df(gdf, drop_geom=True):
"""Converts a GeoDataFrame to a pandas DataFrame.
Args:
gdf (gpd.GeoDataFrame): A GeoDataFrame.
drop_geom (bool, optional): Whether to drop the geometry column. Defaults to True.
Returns:
pd.DataFrame: A pandas DataFrame containing the GeoDataFrame.
"""
import pandas as pd

if drop_geom:
df = pd.DataFrame(gdf.drop(columns=["geometry"]))
else:
df = pd.DataFrame(gdf)

return df
63 changes: 62 additions & 1 deletion leafmap/plotlymap.py
@@ -1,7 +1,9 @@
import os
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import ipywidgets as widgets
from .basemaps import xyz_to_plotly
from .common import *
from .osm import *
Expand Down Expand Up @@ -37,6 +39,28 @@ def __init__(
}
)

def show(
self,
toolbar=True,
map_min_width="91%",
map_max_width="98%",
refresh=False,
**kwargs,
):
from .toolbar import plotly_toolbar

if not toolbar:
super().show(**kwargs)
else:
output = widgets.Output(layout=widgets.Layout(width=map_max_width))
with output:
display(self)
toolbar = plotly_toolbar(
self, output, map_min_width, map_max_width, refresh
)
canvas = widgets.HBox([output, toolbar])
display(canvas)

def clear_controls(self):
"""Removes all controls from the map."""
config = {
Expand All @@ -46,7 +70,7 @@ def clear_controls(self):
"showLink": False,
"displaylogo": False,
}
self.show(config=config)
self.show(toolbar=False, config=config)

def add_controls(self, controls):
"""Adds controls to the map.
Expand Down Expand Up @@ -400,6 +424,43 @@ def add_heatmap_demo(self, **kwargs):
self.add_basemap("Stamen.Terrain")
self.add_trace(heatmap)

def add_gdf(self, gdf, name=None, locations=None, color=None, **kwargs):
"""Adds a GeoDataFrame to the map.
Args:
gdf (GeoDataFrame): A GeoDataFrame.
locations (str, optional): The column name of locations. Defaults to None.
color (str, optional): The column name of color. Defaults to None.
"""

check_package("geopandas")
import geopandas as gpd

gdf.to_crs(epsg=4326, inplace=True)
if isinstance(locations, str):
gdf.set_index(locations, inplace=True)
locations = gdf.index
elif locations is None:
locations = gdf.index

if isinstance(color, str):
if color not in gdf.columns:
raise ValueError(
f"color must be a column name in the GeoDataFrame. Can be one of {','.join(gdf.columns)} "
)

fig = px.choropleth_mapbox(
gdf,
geojson=gdf.geometry,
locations=locations,
color=color,
# center={"lat": 45.5517, "lon": -73.7073},
# zoom=10,
# mapbox_style="open-street-map",
zoom=1,
)
self.add_traces(fig.data)


def fix_widget_error():
"""
Expand Down
165 changes: 165 additions & 0 deletions leafmap/toolbar.py
Expand Up @@ -2129,3 +2129,168 @@ def handle_interaction(**kwargs):

else:
return toolbar_widget


def plotly_toolbar(
m=None, output=None, map_min_width="91%", map_max_width="98%", refresh=False
):
"""Creates the main toolbar and adds it to the map.
Args:
m (leafmap.Map): The leafmap Map object.
"""

if not refresh:
width = int(map_min_width.replace("%", ""))
if width > 85:
map_min_width = "85%"

tools = {
"map": {
"name": "basemap",
"tooltip": "Change basemap",
},
"globe": {
"name": "split_map",
"tooltip": "Split-panel map",
},
# "adjust": {
# "name": "planet",
# "tooltip": "Planet imagery",
# },
# "folder-open": {
# "name": "open_data",
# "tooltip": "Open local vector/raster data",
# },
# "gears": {
# "name": "whitebox",
# "tooltip": "WhiteboxTools for local geoprocessing",
# },
# "fast-forward": {
# "name": "timeslider",
# "tooltip": "Activate the time slider",
# },
# "eraser": {
# "name": "eraser",
# "tooltip": "Remove all drawn features",
# },
# "camera": {
# "name": "save_map",
# "tooltip": "Save map as HTML or image",
# },
# "address-book": {
# "name": "census",
# "tooltip": "Get US Census data",
# },
# "info": {
# "name": "inspector",
# "tooltip": "Get COG/STAC pixel value",
# },
# "search": {
# "name": "search_xyz",
# "tooltip": "Search XYZ tile services",
# },
# "download": {
# "name": "download_osm",
# "tooltip": "Download OSM data",
# },
# "smile-o": {
# "name": "placeholder",
# "tooltip": "This is a placeholder",
# },
# "spinner": {
# "name": "placeholder2",
# "tooltip": "This is a placeholder",
# },
"question": {
"name": "help",
"tooltip": "Get help",
},
}

icons = list(tools.keys())
tooltips = [item["tooltip"] for item in list(tools.values())]

icon_width = "32px"
icon_height = "32px"
n_cols = 3
n_rows = math.ceil(len(icons) / n_cols)

toolbar_grid = widgets.GridBox(
children=[
widgets.ToggleButton(
layout=widgets.Layout(
width="auto", height="auto", padding="0px 0px 0px 4px"
),
button_style="primary",
icon=icons[i],
tooltip=tooltips[i],
)
for i in range(len(icons))
],
layout=widgets.Layout(
width="115px",
grid_template_columns=(icon_width + " ") * n_cols,
grid_template_rows=(icon_height + " ") * n_rows,
grid_gap="1px 1px",
padding="5px",
),
)

toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Toolbar",
icon="wrench",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)

layers_button = widgets.ToggleButton(
value=False,
tooltip="Layers",
icon="server",
layout=widgets.Layout(height="28px", width="72px"),
)

toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [layers_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [toolbar_grid]

toolbar_event = ipyevents.Event(
source=toolbar_widget, watched_events=["mouseenter", "mouseleave"]
)

def handle_toolbar_event(event):

if event["type"] == "mouseenter":
toolbar_widget.children = [toolbar_header, toolbar_footer]
elif event["type"] == "mouseleave":
if not toolbar_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.value = False
layers_button.value = False

toolbar_event.on_dom_event(handle_toolbar_event)

def toolbar_btn_click(change):
if change["new"]:
output.layout.width = map_min_width
if refresh:
with output:
output.clear_output()
display(m)
layers_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
output.layout.width = map_max_width
if not layers_button.value:
toolbar_widget.children = [toolbar_button]
if refresh:
with output:
output.clear_output()
display(m)

toolbar_button.observe(toolbar_btn_click, "value")
return toolbar_widget

0 comments on commit f0d0edb

Please sign in to comment.