toolbar module¶
Module for dealing with the toolbar.
census_widget(m)
¶
Widget for adding US Census data.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leaflet Map object. Defaults to None. |
required |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def census_widget(m):
"""Widget for adding US Census data.
Args:
m (leafmap.Map, optional): The leaflet Map object. Defaults to None.
Returns:
ipywidgets: The tool GUI widget.
"""
# from owslib.wms import WebMapService
census_dict = get_census_dict()
m.add_census_data("Census 2020", "States")
widget_width = "250px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
style = {"description_width": "initial"}
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Toolbar",
icon="filter",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
wms = widgets.Dropdown(
options=census_dict.keys(),
value="Census 2020",
description="WMS:",
layout=widgets.Layout(width=widget_width, padding=padding),
style=style,
)
layer = widgets.Dropdown(
options=census_dict["Census 2020"]["layers"],
value="States",
description="Layer:",
layout=widgets.Layout(width=widget_width, padding=padding),
style=style,
)
checkbox = widgets.Checkbox(
description="Replace existing census data layer",
value=True,
indent=False,
layout=widgets.Layout(padding=padding, width=widget_width),
)
# output = widgets.Output(layout=widgets.Layout(width=widget_width, padding=padding))
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [close_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
wms,
layer,
checkbox,
# output,
]
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
close_button.value = False
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
def wms_change(change):
layer.options = census_dict[change["new"]]["layers"]
layer.value = layer.options[0]
wms.observe(wms_change, "value")
def layer_change(change):
if change["new"] != "":
if checkbox.value:
m.layers = m.layers[:-1]
m.add_census_data(wms.value, layer.value)
# with output:
# w = WebMapService(census_dict[wms.value]["url"])
# output.outputs = ()
# print(w[layer.value].abstract)
layer.observe(layer_change, "value")
toolbar_button.value = True
if m is not None:
toolbar_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position="topright"
)
if toolbar_control not in m.controls:
m.add(toolbar_control)
m.tool_control = toolbar_control
else:
return toolbar_widget
change_basemap(m, position='topright')
¶
Widget for changing basemaps.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
object |
leafmap.Map. |
required |
Source code in leafmap/toolbar.py
def change_basemap(m, position: Optional[str] = "topright"):
"""Widget for changing basemaps.
Args:
m (object): leafmap.Map.
"""
from .basemaps import get_xyz_dict
from .leafmap import basemaps, get_basemap
xyz_dict = get_xyz_dict()
value = "OpenStreetMap"
dropdown = widgets.Dropdown(
options=list(basemaps.keys()),
value=value,
layout=widgets.Layout(width="200px"),
)
close_btn = widgets.Button(
icon="times",
tooltip="Close the basemap widget",
button_style="primary",
layout=widgets.Layout(width="32px"),
)
basemap_widget = widgets.HBox([dropdown, close_btn])
def on_click(change):
if change["new"]:
basemap_name = dropdown.value
if basemap_name not in m.get_layer_names():
m.add_basemap(basemap_name)
if basemap_name in xyz_dict:
if "bounds" in xyz_dict[basemap_name]:
bounds = xyz_dict[basemap_name]["bounds"]
bounds = [
bounds[0][1],
bounds[0][0],
bounds[1][1],
bounds[1][0],
]
m.zoom_to_bounds(bounds)
dropdown.observe(on_click, "value")
def close_click(change):
m.toolbar_reset()
if m.basemap_ctrl is not None and m.basemap_ctrl in m.controls:
m.remove_control(m.basemap_ctrl)
basemap_widget.close()
close_btn.on_click(close_click)
basemap_control = ipyleaflet.WidgetControl(widget=basemap_widget, position=position)
m.add(basemap_control)
m.basemap_ctrl = basemap_control
download_osm(m)
¶
Widget for downloading OSM data.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leaflet Map object. Defaults to None. |
required |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def download_osm(m):
"""Widget for downloading OSM data.
Args:
m (leafmap.Map, optional): The leaflet Map object. Defaults to None.
Returns:
ipywidgets: The tool GUI widget.
"""
widget_width = "250px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
style = {"description_width": "initial"}
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Toolbar",
icon="gear",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
checkbox = widgets.Checkbox(
description="Checkbox",
indent=False,
layout=widgets.Layout(padding=padding, width=widget_width),
)
dropdown = widgets.Dropdown(
options=["Option 1", "Option 2", "Option 3"],
value=None,
description="Dropdown:",
layout=widgets.Layout(width=widget_width, padding=padding),
style=style,
)
int_slider = widgets.IntSlider(
min=1,
max=100,
description="Int Slider: ",
readout=False,
continuous_update=True,
layout=widgets.Layout(width="220px", padding=padding),
style=style,
)
int_slider_label = widgets.Label()
# widgets.jslink((int_slider, "value"), (int_slider_label, "value"))
def int_slider_changed(change):
if change["new"]:
int_slider_label.value = str(int_slider.value)
int_slider.observe(int_slider_changed, "value")
float_slider = widgets.FloatSlider(
min=1,
max=100,
description="Float Slider: ",
readout=False,
continuous_update=True,
layout=widgets.Layout(width="220px", padding=padding),
style=style,
)
float_slider_label = widgets.Label()
# widgets.jslink((float_slider, "value"), (float_slider_label, "value"))
def float_slider_changed(change):
if change["new"]:
float_slider_label.value = str(float_slider.value)
float_slider.observe(float_slider_changed, "value")
color = widgets.ColorPicker(
concise=False,
description="Color:",
value="white",
style=style,
layout=widgets.Layout(width=widget_width, padding=padding),
)
text = widgets.Text(
value="",
description="Textbox:",
placeholder="Placeholder",
style=style,
layout=widgets.Layout(width=widget_width, padding=padding),
)
textarea = widgets.Textarea(
placeholder="Placeholder",
layout=widgets.Layout(width=widget_width),
)
buttons = widgets.ToggleButtons(
value=None,
options=["Apply", "Reset", "Close"],
tooltips=["Apply", "Reset", "Close"],
button_style="primary",
)
buttons.style.button_width = "80px"
buttons.style.button_padding = "0px"
output = widgets.Output(layout=widgets.Layout(width=widget_width, padding=padding))
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [close_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
checkbox,
widgets.HBox([int_slider, int_slider_label]),
widgets.HBox([float_slider, float_slider_label]),
dropdown,
text,
color,
textarea,
buttons,
output,
]
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
close_button.value = False
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
def button_clicked(change):
if change["new"] == "Apply":
with output:
output.outputs = ()
print("Running ...")
elif change["new"] == "Reset":
textarea.value = ""
output.outputs = ()
elif change["new"] == "Close":
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
buttons.value = None
buttons.observe(button_clicked, "value")
toolbar_button.value = True
if m is not None:
toolbar_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position="topright"
)
if toolbar_control not in m.controls:
m.add(toolbar_control)
m.tool_control = toolbar_control
else:
return toolbar_widget
edit_draw_gui(m)
¶
Generates a tool GUI for editing vector data attribute table.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leaflet Map object. Defaults to None. |
required |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def edit_draw_gui(m):
"""Generates a tool GUI for editing vector data attribute table.
Args:
m (leafmap.Map, optional): The leaflet Map object. Defaults to None.
Returns:
ipywidgets: The tool GUI widget.
"""
import ipysheet
import pandas as pd
widget_width = "250px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
style = {"description_width": "initial"}
m.edit_mode = True
n_props = len(m.get_draw_props())
if n_props == 0:
n_props = 1
if "MAX_PROPS" in os.environ:
max_props = int(os.environ["MAX_PROPS"])
else:
max_props = n_props + 10
sheet = ipysheet.from_dataframe(m.get_draw_props(n_props, return_df=True))
m.edit_sheet = sheet
output = widgets.Output(layout=widgets.Layout(width=widget_width, padding=padding))
m.edit_output = output
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Edit attribute table",
icon="pencil-square-o",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
open_button = widgets.ToggleButton(
value=False,
tooltip="Open vector data",
icon="folder-open",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
save_button = widgets.ToggleButton(
value=False,
tooltip="Save to file",
icon="floppy-o",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
refresh_button = widgets.ToggleButton(
value=False,
tooltip="Get attribute",
icon="refresh",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
m.edit_refresh = refresh_button
int_slider = widgets.IntSlider(
min=n_props,
max=max_props,
description="Rows:",
readout=False,
continuous_update=True,
layout=widgets.Layout(width="85px", padding=padding),
style=style,
)
int_slider_label = widgets.Label()
buttons = widgets.ToggleButtons(
value=None,
options=["Apply", "Reset", "Close"],
tooltips=["Apply", "Reset", "Close"],
button_style="primary",
)
buttons.style.button_width = "64px"
with output:
output.outputs = ()
display(m.edit_sheet)
def int_slider_changed(change):
if change["new"]:
int_slider_label.value = str(int_slider.value)
m.edit_sheet.rows = int_slider.value
m.num_attributes = int_slider.value
with output:
output.outputs = ()
m.edit_sheet = ipysheet.from_dataframe(
m.get_draw_props(n=int_slider.value, return_df=True)
)
display(m.edit_sheet)
int_slider.observe(int_slider_changed, "value")
m.num_attributes = int_slider.value
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [
close_button,
toolbar_button,
open_button,
save_button,
refresh_button,
int_slider,
int_slider_label,
]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
output,
buttons,
]
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
close_button.value = False
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
m.edit_mode = False
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
def open_chooser_callback(chooser):
with output:
import geopandas as gpd
gdf = gpd.read_file(chooser.selected)
geojson = gdf_to_geojson(gdf, epsg=4326, tuple_to_list=True)
m.draw_control.data = m.draw_control.data + (geojson["features"])
m.draw_features = m.draw_features + (geojson["features"])
open_button.value = False
m.edit_props = gdf.drop(["geometry"], axis=1).columns.tolist()
int_slider.value = len(m.edit_props)
if m.open_control in m.controls:
m.remove_control(m.open_control)
delattr(m, "open_control")
def open_btn_click(change):
if change["new"]:
save_button.value = False
open_chooser = FileChooser(
os.getcwd(),
sandbox_path=m.sandbox_path,
layout=widgets.Layout(width="454px"),
)
open_chooser.filter_pattern = ["*.shp", "*.geojson", "*.gpkg"]
open_chooser.use_dir_icons = True
open_chooser.register_callback(open_chooser_callback)
open_control = ipyleaflet.WidgetControl(
widget=open_chooser, position="topright"
)
m.add(open_control)
m.open_control = open_control
open_button.observe(open_btn_click, "value")
def chooser_callback(chooser):
m.save_draw_features(chooser.selected, indent=None)
if m.file_control in m.controls:
m.remove_control(m.file_control)
delattr(m, "file_control")
with output:
print(f"Saved to {chooser.selected}")
def save_btn_click(change):
if change["new"]:
save_button.value = False
file_chooser = FileChooser(
os.getcwd(),
sandbox_path=m.sandbox_path,
layout=widgets.Layout(width="454px"),
)
file_chooser.filter_pattern = ["*.shp", "*.geojson", "*.gpkg"]
file_chooser.default_filename = "data.geojson"
file_chooser.use_dir_icons = True
file_chooser.register_callback(chooser_callback)
file_control = ipyleaflet.WidgetControl(
widget=file_chooser, position="topright"
)
m.add(file_control)
m.file_control = file_control
save_button.observe(save_btn_click, "value")
def refresh_btn_click(change):
if change["new"]:
refresh_button.value = False
if m.draw_control.last_action == "edited":
with output:
geometries = [
feature["geometry"] for feature in m.draw_control.data
]
if len(m.draw_features) > 0:
if (
m.draw_features[-1]["geometry"]
== m.draw_control.last_draw["geometry"]
):
m.draw_features.pop()
for feature in m.draw_features:
if feature["geometry"] not in geometries:
feature["geometry"] = m.draw_control.last_draw["geometry"]
values = []
props = ipysheet.to_dataframe(m.edit_sheet)["Key"].tolist()
for prop in props:
if prop in feature["properties"]:
values.append(feature["properties"][prop])
else:
values.append("")
df = pd.DataFrame({"Key": props, "Value": values})
df.index += 1
m.edit_sheet = ipysheet.from_dataframe(df)
output.outputs = ()
display(m.edit_sheet)
refresh_button.observe(refresh_btn_click, "value")
def button_clicked(change):
if change["new"] == "Apply":
with output:
output.outputs = ()
display(m.edit_sheet)
if len(m.draw_control.data) == 0:
print("Please draw a feature first.")
else:
if m.draw_control.last_action == "edited":
m.update_draw_features()
m.update_draw_props(ipysheet.to_dataframe(m.edit_sheet))
elif change["new"] == "Reset":
m.edit_sheet = ipysheet.from_dataframe(
m.get_draw_props(int_slider.value, return_df=True)
)
with output:
output.outputs = ()
display(m.edit_sheet)
elif change["new"] == "Close":
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
m.edit_mode = False
toolbar_widget.close()
buttons.value = None
buttons.observe(button_clicked, "value")
toolbar_button.value = True
if m is not None:
toolbar_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position="topright"
)
if toolbar_control not in m.controls:
m.add(toolbar_control)
m.tool_control = toolbar_control
else:
return toolbar_widget
inspector_gui(m, position='topright', opened=True)
¶
Generates a tool GUI template using ipywidgets.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leaflet Map object. Defaults to None. |
required |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def inspector_gui(
m,
position: Optional[str] = "topright",
opened: Optional[bool] = True,
):
"""Generates a tool GUI template using ipywidgets.
Args:
m (leafmap.Map, optional): The leaflet Map object. Defaults to None.
Returns:
ipywidgets: The tool GUI widget.
"""
import pandas as pd
widget_width = "250px"
padding = "0px 5px 0px 5px" # upper, right, bottom, left
style = {"description_width": "initial"}
if m is not None:
marker_cluster = ipyleaflet.MarkerCluster(name="Inspector Markers")
setattr(m, "pixel_values", [])
setattr(m, "marker_cluster", marker_cluster)
if not hasattr(m, "interact_mode"):
setattr(m, "interact_mode", False)
if not hasattr(m, "inspector_output"):
inspector_output = widgets.Output(
layout=widgets.Layout(width=widget_width, padding="0px 5px 5px 5px")
)
setattr(m, "inspector_output", inspector_output)
output = m.inspector_output
output.outputs = ()
if not hasattr(m, "inspector_add_marker"):
inspector_add_marker = widgets.Checkbox(
description="Add Marker at clicked location",
value=True,
indent=False,
layout=widgets.Layout(padding=padding, width=widget_width),
)
setattr(m, "inspector_add_marker", inspector_add_marker)
add_marker = m.inspector_add_marker
if not hasattr(m, "inspector_bands_chk"):
inspector_bands_chk = widgets.Checkbox(
description="Get pixel value for visible bands only",
indent=False,
layout=widgets.Layout(padding=padding, width=widget_width),
)
setattr(m, "inspector_bands_chk", inspector_bands_chk)
bands_chk = m.inspector_bands_chk
if not hasattr(m, "inspector_class_label"):
inspector_label = widgets.Text(
value="",
description="Class label:",
placeholder="Add a label to the marker",
style=style,
layout=widgets.Layout(width=widget_width, padding=padding),
)
setattr(m, "inspector_class_label", inspector_label)
label = m.inspector_class_label
options = []
if hasattr(m, "cog_layer_dict"):
options = list(m.cog_layer_dict.keys())
options.sort()
if len(options) == 0:
default_option = None
else:
default_option = options[0]
if not hasattr(m, "inspector_dropdown"):
inspector_dropdown = widgets.Dropdown(
options=options,
value=default_option,
description="Select a layer:",
layout=widgets.Layout(width=widget_width, padding=padding),
style=style,
)
setattr(m, "inspector_dropdown", inspector_dropdown)
dropdown = m.inspector_dropdown
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Toolbar",
icon="info",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
buttons = widgets.ToggleButtons(
value=None,
options=["Download", "Reset", "Close"],
tooltips=["Download", "Reset", "Close"],
button_style="primary",
)
buttons.style.button_width = "80px"
if len(options) == 0:
with output:
print("No COG/STAC layers available")
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [close_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
add_marker,
label,
dropdown,
bands_chk,
buttons,
output,
]
toolbar_event = ipyevents.Event(
source=toolbar_widget, watched_events=["mouseenter", "mouseleave"]
)
def chk_change(change):
if hasattr(m, "pixel_values"):
m.pixel_values = []
if hasattr(m, "marker_cluster"):
m.marker_cluster.markers = []
output.outputs = ()
bands_chk.observe(chk_change, "value")
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
close_button.value = False
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
if m is not None:
if hasattr(m, "inspector_mode"):
delattr(m, "inspector_mode")
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
m.default_style = {"cursor": "default"}
m.marker_cluster.markers = []
m.pixel_values = []
marker_cluster_layer = m.find_layer("Inspector Markers")
if marker_cluster_layer is not None:
m.remove_layer(marker_cluster_layer)
if hasattr(m, "pixel_values"):
delattr(m, "pixel_values")
if hasattr(m, "marker_cluster"):
delattr(m, "marker_cluster")
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
def button_clicked(change):
if change["new"] == "Download":
with output:
output.outputs = ()
if len(m.pixel_values) == 0:
print(
"No pixel values available. Click on the map to start collection data."
)
else:
print("Downloading pixel values...")
df = pd.DataFrame(m.pixel_values)
temp_csv = temp_file_path("csv")
df.to_csv(temp_csv, index=False)
link = create_download_link(temp_csv)
with output:
output.outputs = ()
display(link)
elif change["new"] == "Reset":
label.value = ""
output.outputs = ()
if hasattr(m, "pixel_values"):
m.pixel_values = []
if hasattr(m, "marker_cluster"):
m.marker_cluster.markers = []
elif change["new"] == "Close":
if m is not None:
if hasattr(m, "inspector_mode"):
delattr(m, "inspector_mode")
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
m.default_style = {"cursor": "default"}
m.marker_cluster.markers = []
marker_cluster_layer = m.find_layer("Inspector Markers")
if marker_cluster_layer is not None:
m.remove_layer(marker_cluster_layer)
m.pixel_values = []
if hasattr(m, "pixel_values"):
delattr(m, "pixel_values")
if hasattr(m, "marker_cluster"):
delattr(m, "marker_cluster")
toolbar_widget.close()
buttons.value = None
buttons.observe(button_clicked, "value")
toolbar_button.value = opened
def handle_interaction(**kwargs):
latlon = kwargs.get("coordinates")
lat = round(latlon[0], 4)
lon = round(latlon[1], 4)
if (
kwargs.get("type") == "click"
and hasattr(m, "inspector_mode")
and m.inspector_mode
):
m.default_style = {"cursor": "wait"}
with output:
output.outputs = ()
print("Getting pixel value ...")
layer_dict = m.cog_layer_dict[dropdown.value]
if layer_dict["type"] == "STAC":
if bands_chk.value:
assets = layer_dict["assets"]
else:
assets = None
result = stac_pixel_value(
lon,
lat,
layer_dict["url"],
layer_dict["collection"],
layer_dict["item"],
assets,
layer_dict["titiler_endpoint"],
verbose=False,
)
if result is not None:
with output:
output.clear_output()
print(f"lat, lon: {lat:.4f}, {lon:.4f}\n")
for key in result:
print(f"{key}: {result[key]}")
result["latitude"] = lat
result["longitude"] = lon
result["label"] = label.value
m.pixel_values.append(result)
if add_marker.value:
markers = list(m.marker_cluster.markers)
markers.append(ipyleaflet.Marker(location=latlon))
m.marker_cluster.markers = markers
else:
with output:
output.clear_output()
print("No pixel value available")
bounds = m.cog_layer_dict[m.inspector_dropdown.value]["bounds"]
m.zoom_to_bounds(bounds)
elif layer_dict["type"] == "COG":
if m.inspector_bands_chk.value:
indexes = layer_dict["indexes"]
else:
indexes = list(range(1, len(layer_dict["band_names"]) + 1))
result = cog_pixel_value(
lon, lat, layer_dict["url"], indexes, verbose=False
)
if result is not None:
with output:
output.clear_output()
print(f"lat, lon: {lat:.4f}, {lon:.4f}\n")
for key in result:
print(f"{key}: {result[key]}")
result["latitude"] = lat
result["longitude"] = lon
result["label"] = label.value
m.pixel_values.append(result)
if add_marker.value:
markers = list(m.marker_cluster.markers)
markers.append(ipyleaflet.Marker(location=latlon))
m.marker_cluster.markers = markers
else:
with output:
output.outputs = ()
print("No pixel value available")
bounds = m.cog_layer_dict[m.inspector_dropdown.value]["bounds"]
m.zoom_to_bounds(bounds)
elif layer_dict["type"] == "LOCAL":
try:
data = local_tile_pixel_value(
lon, lat, layer_dict["tile_client"], verbose=False
)
result = {}
band_names = data.band_names
values = data.array.data.tolist()
for i, band in enumerate(band_names):
result[band] = values[i]
if m.inspector_bands_chk.value:
vis_bands = m.cog_layer_dict[m.inspector_dropdown.value][
"vis_bands"
]
new_result = {}
for key in result:
if key in vis_bands:
new_result[key] = result[key]
result = new_result
with output:
output.clear_output()
print(f"lat, lon: {lat:.4f}, {lon:.4f}\n")
for key in result:
print(f"{key}: {result[key]}")
result["latitude"] = lat
result["longitude"] = lon
result["label"] = label.value
m.pixel_values.append(result)
if add_marker.value:
markers = list(m.marker_cluster.markers)
markers.append(ipyleaflet.Marker(location=latlon))
m.marker_cluster.markers = markers
except Exception as e:
with output:
output.clear_output()
print(e)
# bounds = m.cog_layer_dict[m.inspector_dropdown.value]["bounds"]
# m.zoom_to_bounds(bounds)
m.default_style = {"cursor": "crosshair"}
if m is not None:
if not hasattr(m, "marker_cluster"):
setattr(m, "marker_cluster", marker_cluster)
m.add(marker_cluster)
if not m.interact_mode:
m.on_interaction(handle_interaction)
m.interact_mode = True
if m is not None:
toolbar_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position=position
)
if toolbar_control not in m.controls:
m.add(toolbar_control)
m.tool_control = toolbar_control
if not hasattr(m, "inspector_mode"):
if hasattr(m, "cog_layer_dict"):
setattr(m, "inspector_mode", True)
else:
setattr(m, "inspector_mode", False)
else:
return toolbar_widget
layer_manager_gui(m, position='topright', opened=True, return_widget=False)
¶
Creates a layer manager widget.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
geemap.Map |
The geemap.Map object. |
required |
position |
str |
The position of the widget. Defaults to "topright". |
'topright' |
return_widget |
bool |
Whether to return the widget. Defaults to False. |
False |
Source code in leafmap/toolbar.py
def layer_manager_gui(
m,
position: Optional[str] = "topright",
opened: Optional[bool] = True,
return_widget: Optional[bool] = False,
):
"""Creates a layer manager widget.
Args:
m (geemap.Map): The geemap.Map object.
position (str, optional): The position of the widget. Defaults to "topright".
return_widget (bool, optional): Whether to return the widget. Defaults to False.
"""
layers_button = widgets.ToggleButton(
value=False,
tooltip="Layer Manager",
icon="server",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
toolbar_header = widgets.HBox()
toolbar_header.children = [layers_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = []
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_header]
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not close_button.value:
toolbar_widget.children = [layers_button]
layers_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
layers_button.value = False
m.toolbar_reset()
if m.layer_manager is not None and m.layer_manager in m.controls:
m.remove_control(m.layer_manager)
m.layer_manager = None
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
def layers_btn_click(change):
if change["new"]:
layers_hbox = []
all_layers_chk = widgets.Checkbox(
value=False,
description="All layers on/off",
indent=False,
layout=widgets.Layout(height="18px", padding="0px 8px 25px 8px"),
)
all_layers_chk.layout.width = "30ex"
layers_hbox.append(all_layers_chk)
def all_layers_chk_changed(change):
if change["new"]:
for layer in m.layers:
if hasattr(layer, "visible"):
layer.visible = True
else:
for layer in m.layers:
if hasattr(layer, "visible"):
layer.visible = False
all_layers_chk.observe(all_layers_chk_changed, "value")
layers = [lyr for lyr in m.layers if lyr.name]
# if the layers contain unsupported layers (e.g., GeoJSON, GeoData), adds the ipyleaflet built-in LayerControl
if len(layers) < (len(m.layers) - 1):
if m.layer_control is None:
layer_control = ipyleaflet.LayersControl(position="topright")
m.layer_control = layer_control
if m.layer_control not in m.controls:
m.add(m.layer_control)
# for non-TileLayer, use layer.style={'opacity':0, 'fillOpacity': 0} to turn layer off.
for layer in layers:
visible = True
if hasattr(layer, "visible"):
visible = layer.visible
layer_chk = widgets.Checkbox(
value=visible,
description=layer.name,
indent=False,
layout=widgets.Layout(height="18px"),
)
layer_chk.layout.width = "25ex"
if layer in m.geojson_layers:
try:
opacity = max(
layer.style["opacity"], layer.style["fillOpacity"]
)
except KeyError:
opacity = 1.0
elif hasattr(layer, "opacity"):
opacity = layer.opacity
else:
opacity = 1.0
layer_opacity = widgets.FloatSlider(
value=opacity,
min=0,
max=1,
step=0.01,
readout=False,
layout=widgets.Layout(width="80px"),
)
layer_settings = widgets.Button(
icon="gear",
tooltip=layer.name,
layout=widgets.Layout(
width="25px", height="25px", padding="0px 0px 0px 0px"
),
)
def layer_settings_click(b):
if b.tooltip in m.cog_layer_dict:
m._add_layer_editor(
position="topright",
layer_dict=m.cog_layer_dict[b.tooltip],
)
layer_settings.on_click(layer_settings_click)
def layer_opacity_changed(change):
if change["new"]:
layer.style = {
"opacity": change["new"],
"fillOpacity": change["new"],
}
if hasattr(layer, "visible"):
widgets.jslink((layer_chk, "value"), (layer, "visible"))
if layer in m.geojson_layers:
layer_opacity.observe(layer_opacity_changed, "value")
elif hasattr(layer, "opacity"):
widgets.jsdlink((layer_opacity, "value"), (layer, "opacity"))
hbox = widgets.HBox(
[layer_chk, layer_settings, layer_opacity],
layout=widgets.Layout(padding="0px 8px 0px 8px"),
)
layers_hbox.append(hbox)
m.layer_widget = layers_hbox
toolbar_header.children = [close_button, layers_button]
toolbar_footer.children = layers_hbox
else:
toolbar_header.children = [layers_button]
layers_button.observe(layers_btn_click, "value")
layers_button.value = opened
if not hasattr(m, "_layer_manager_widget"):
m._layer_manager_widget = toolbar_footer
if return_widget:
return m.layer_widget
else:
layer_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position=position
)
if layer_control not in m.controls:
m.add_control(layer_control)
m.layer_manager = layer_control
main_toolbar(m)
¶
Creates the main toolbar and adds it to the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leafmap Map object. |
required |
Source code in leafmap/toolbar.py
def main_toolbar(m):
"""Creates the main toolbar and adds it to the map.
Args:
m (leafmap.Map): The leafmap Map object.
"""
all_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",
},
"filter": {
"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",
},
"picture-o": {
"name": "raster",
"tooltip": "Open COG/STAC dataset",
},
"search-plus": {
"name": "search_geojson",
"tooltip": "Search features in GeoJSON layer",
},
"table": {
"name": "attribute_table",
"tooltip": "Open attribute table",
},
"pencil-square-o": {
"name": "edit_vector",
"tooltip": "Create vector data",
},
"stack-exchange": {
"name": "stac",
"tooltip": "Discover STAC Catalog",
},
"plane": {
"name": "oam",
"tooltip": "Search OpenAerialMap",
},
# "spinner": {
# "name": "placeholder2",
# "tooltip": "This is a placeholder",
# },
# "question": {
# "name": "help",
# "tooltip": "Get help",
# },
}
# if m.sandbox_path is None and (os.environ.get("USE_VOILA") is not None):
# voila_tools = ["camera", "folder-open", "gears"]
# for item in voila_tools:
# if item in tools.keys():
# del tools[item]
tools = {}
for tool in all_tools:
if os.environ.get(all_tools[tool]["name"].upper(), "").upper() != "FALSE":
tools[tool] = all_tools[tool]
icons = list(tools.keys())
tooltips = [item["tooltip"] for item in list(tools.values())]
toolnames = [item["name"].upper() for item in list(all_tools.values())]
toolnames.sort()
setattr(m, "_ENV_VARS", toolnames)
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="109px",
grid_template_columns=(icon_width + " ") * n_cols,
grid_template_rows=(icon_height + " ") * n_rows,
grid_gap="1px 1px",
padding="5px",
),
)
m.toolbar = toolbar_grid
def tool_callback(change):
if change["new"]:
current_tool = change["owner"]
for tool in toolbar_grid.children:
if tool is not current_tool:
tool.value = False
tool = change["owner"]
tool_name = tools[tool.icon]["name"]
if tool_name == "basemap":
change_basemap(m)
if tool_name == "split_map":
split_basemaps(m)
if tool_name == "planet":
split_basemaps(m, layers_dict=planet_tiles())
elif tool_name == "open_data":
open_data_widget(m)
elif tool_name == "eraser":
if m.draw_control is not None:
m.draw_control.clear()
m.user_roi = None
m.user_rois = None
m.draw_features = []
elif tool_name == "whitebox":
import whiteboxgui.whiteboxgui as wbt
tools_dict = wbt.get_wbt_dict()
wbt_toolbox = wbt.build_toolbox(
tools_dict,
max_width="800px",
max_height="500px",
sandbox_path=m.sandbox_path,
)
wbt_control = ipyleaflet.WidgetControl(
widget=wbt_toolbox, position="bottomright"
)
m.whitebox = wbt_control
m.add(wbt_control)
elif tool_name == "timeslider":
m.add_time_slider()
elif tool_name == "save_map":
save_map((m))
elif tool_name == "census":
census_widget(m)
elif tool_name == "inspector":
inspector_gui(m)
elif tool_name == "search_xyz":
search_basemaps(m)
elif tool_name == "download_osm":
download_osm(m)
elif tool_name == "raster":
open_raster_gui(m)
elif tool_name == "search_geojson":
search_geojson_gui(m)
elif tool_name == "attribute_table":
select_table_gui(m)
elif tool_name == "edit_vector":
edit_draw_gui(m)
elif tool_name == "stac":
stac_gui(m)
elif tool_name == "oam":
oam_search_gui(m)
elif tool_name == "help":
import webbrowser
webbrowser.open_new_tab("https://leafmap.org")
current_tool.value = False
else:
# tool = change["owner"]
# tool_name = tools[tool.icon]["name"]
pass
m.toolbar_reset()
for tool in toolbar_grid.children:
tool.observe(tool_callback, "value")
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Toolbar",
icon="wrench",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
m.toolbar_button = toolbar_button
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"]:
layers_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not layers_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.observe(toolbar_btn_click, "value")
def layers_btn_click(change):
if change["new"]:
toolbar_footer.children = layer_manager_gui(m, return_widget=True)
toolbar_button.value = False
else:
toolbar_footer.children = [toolbar_grid]
layers_button.observe(layers_btn_click, "value")
toolbar_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position="topright"
)
m.add(toolbar_control)
nasa_data_gui(m, position='topright', opened=True, default_dataset='GEDI_L4A_AGB_Density_V2_1_2056', **kwargs)
¶
Search NASA Earth data interactive
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leaflet Map object. Defaults to None. |
required |
position |
str |
The position of the widget. Defaults to "topright". |
'topright' |
opened |
bool |
Whether to open the widget. Defaults to True. |
True |
default_dataset |
str |
The default dataset. Defaults to "GEDI_L4A_AGB_Density_V2_1_2056". |
'GEDI_L4A_AGB_Density_V2_1_2056' |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def nasa_data_gui(
m,
position: Optional[str] = "topright",
opened: Optional[bool] = True,
default_dataset: Optional[str] = "GEDI_L4A_AGB_Density_V2_1_2056",
**kwargs,
):
"""Search NASA Earth data interactive
Args:
m (leafmap.Map, optional): The leaflet Map object. Defaults to None.
position (str, optional): The position of the widget. Defaults to "topright".
opened (bool, optional): Whether to open the widget. Defaults to True.
default_dataset (str, optional): The default dataset. Defaults to "GEDI_L4A_AGB_Density_V2_1_2056".
Returns:
ipywidgets: The tool GUI widget.
"""
import pandas as pd
from datetime import datetime
widget_width = "400px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
style = {"description_width": "initial"}
if not hasattr(m, "_NASA_DATA"):
url = "https://github.com/opengeos/NASA-Earth-Data/raw/main/nasa_earth_data.tsv"
df = pd.read_csv(url, sep="\t")
setattr(m, "_NASA_DATA", df)
names = df["ShortName"].tolist()
setattr(m, "_NASA_DATA_NAMES", names)
default_title = m._NASA_DATA[m._NASA_DATA["ShortName"] == default_dataset][
"EntryTitle"
].values[0]
output = widgets.Output(
layout=widgets.Layout(width=widget_width, padding=padding, overflow="auto")
)
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Search NASA Earth data",
icon="search",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
keyword = widgets.Text(
value="",
description="Keyword:",
style=style,
layout=widgets.Layout(width=widget_width, padding=padding),
)
short_name = widgets.Dropdown(
options=m._NASA_DATA_NAMES,
value=default_dataset,
description="Short Name:",
style=style,
layout=widgets.Layout(width=widget_width, padding=padding),
)
title = widgets.Text(
value=default_title,
description="Title:",
style=style,
disabled=True,
layout=widgets.Layout(width=widget_width, padding=padding),
)
max_items = widgets.IntText(
value=50,
description="Max items:",
style=style,
layout=widgets.Layout(width="125px", padding=padding),
)
bbox = widgets.Text(
value="",
description="Bounding box:",
placeholder="xmin, ymin, xmax, ymax",
style=style,
layout=widgets.Layout(width="271px", padding=padding),
)
start_date = widgets.DatePicker(
description="Start date:",
disabled=False,
style=style,
layout=widgets.Layout(width="198px", padding=padding),
)
end_date = widgets.DatePicker(
description="End date:",
disabled=False,
style=style,
layout=widgets.Layout(width="198px", padding=padding),
)
dataset = widgets.Dropdown(
value=None,
description="Dataset:",
style=style,
layout=widgets.Layout(width=widget_width, padding=padding),
)
buttons = widgets.ToggleButtons(
value=None,
options=["Search", "Display", "Reset", "Close"],
tooltips=["Get Items", "Display Image", "Reset", "Close"],
button_style="primary",
)
buttons.style.button_width = "65px"
def change_keyword(change):
short_name.options = nasa_datasets(
keyword.value, df=m._NASA_DATA, return_short_name=True
)
keyword.on_submit(change_keyword)
def change_dataset(change):
title.value = m._NASA_DATA[m._NASA_DATA["ShortName"] == short_name.value][
"EntryTitle"
].values[0]
dataset.value = None
dataset.options = []
short_name.observe(change_dataset, "value")
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [close_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
keyword,
short_name,
title,
widgets.HBox([max_items, bbox]),
widgets.HBox([start_date, end_date]),
dataset,
buttons,
output,
]
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
close_button.value = False
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
def button_clicked(change):
if change["new"] == "Search":
with output:
output.clear_output()
with output:
print("Searching...")
if bbox.value.strip() == "":
if m.user_roi_bounds() is not None:
bounds = tuple(m.user_roi_bounds())
else:
bounds = (
m.bounds[0][1],
m.bounds[0][0],
m.bounds[1][1],
m.bounds[1][0],
)
else:
bounds = tuple(map(float, bbox.value.split(",")))
if len(bounds) != 4:
print("Please provide a valid bounding box.")
bounds = None
if start_date.value is not None and end_date.value is not None:
date_range = (str(start_date.value), str(end_date.value))
elif start_date.value is not None:
date_range = (
str(start_date.value),
datetime.today().strftime("%Y-%m-%d"),
)
else:
date_range = None
output.clear_output(wait=True)
try:
results, gdf = nasa_data_search(
count=max_items.value,
short_name=short_name.value,
bbox=bounds,
temporal=date_range,
return_gdf=True,
)
if len(results) > 0:
if "Footprints" in m.get_layer_names():
m.remove(m.find_layer("Footprints"))
if (
hasattr(m, "_NASA_DATA_CTRL")
and m._NASA_DATA_CTRL in m.controls
):
m.remove(m._NASA_DATA_CTRL)
style = {
# "stroke": True,
"color": "#3388ff",
"weight": 2,
"opacity": 1,
"fill": True,
"fillColor": "#3388ff",
"fillOpacity": 0.1,
}
hover_style = {
"weight": style["weight"] + 2,
"fillOpacity": 0,
"color": "yellow",
}
m.add_gdf(
gdf,
layer_name="Footprints",
info_mode="on_click",
zoom_to_layer=False,
style=style,
hover_style=hover_style,
)
setattr(m, "_NASA_DATA_CTRL", m.controls[-1])
dataset.options = gdf["native-id"].values.tolist()
dataset.value = dataset.options[0]
setattr(m, "_NASA_DATA_GDF", gdf)
setattr(m, "_NASA_DATA_RESULTS", results)
output.clear_output()
except Exception as e:
print(e)
elif change["new"] == "Display":
output.clear_output()
with output:
print("To be implemented...")
elif change["new"] == "Reset":
keyword.value = ""
short_name.options = m._NASA_DATA_NAMES
short_name.value = default_dataset
title.value = default_title
max_items.value = 50
bbox.value = ""
bbox.placeholder = "xmin, ymin, xmax, ymax"
start_date.value = None
end_date.value = None
dataset.options = []
dataset.value = None
output.clear_output()
if "Footprints" in m.get_layer_names():
m.remove(m.find_layer("Footprints"))
if hasattr(m, "_NASA_DATA_CTRL") and m._NASA_DATA_CTRL in m.controls:
m.remove(m._NASA_DATA_CTRL)
elif change["new"] == "Close":
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
buttons.value = None
buttons.observe(button_clicked, "value")
toolbar_button.value = opened
if m is not None:
toolbar_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position=position
)
if toolbar_control not in m.controls:
m.add(toolbar_control)
m.tool_control = toolbar_control
else:
return toolbar_widget
nasa_opera_gui(m, position='topright', opened=True, default_dataset='OPERA_L3_DSWX-HLS_V1', **kwargs)
¶
Search NASA Earth data interactive
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leaflet Map object. Defaults to None. |
required |
position |
str |
The position of the widget. Defaults to "topright". |
'topright' |
opened |
bool |
Whether to open the widget. Defaults to True. |
True |
default_dataset |
str |
The default dataset. Defaults to "OPERA_L3_DSWX-HLS_V1". |
'OPERA_L3_DSWX-HLS_V1' |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def nasa_opera_gui(
m,
position: Optional[str] = "topright",
opened: Optional[bool] = True,
default_dataset: Optional[str] = "OPERA_L3_DSWX-HLS_V1",
**kwargs,
):
"""Search NASA Earth data interactive
Args:
m (leafmap.Map, optional): The leaflet Map object. Defaults to None.
position (str, optional): The position of the widget. Defaults to "topright".
opened (bool, optional): Whether to open the widget. Defaults to True.
default_dataset (str, optional): The default dataset. Defaults to "OPERA_L3_DSWX-HLS_V1".
Returns:
ipywidgets: The tool GUI widget.
"""
import pandas as pd
from datetime import datetime
import boto3
import rasterio as rio
from rasterio.session import AWSSession
import xarray as xr
import matplotlib.pyplot as plt
widget_width = "400px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
style = {"description_width": "initial"}
colormaps = plt.colormaps()
cmap_options = [cmap for cmap in colormaps if (len(cmap) < 20 and cmap.islower())]
cmap_options.sort()
if not hasattr(m, "_NASA_DATA"):
data = {
"ShortName": [
"OPERA_L2_CSLC-S1-STATIC_V1",
"OPERA_L2_CSLC-S1_V1",
"OPERA_L2_RTC-S1-STATIC_V1",
"OPERA_L2_RTC-S1_V1",
"OPERA_L3_DIST-ALERT-HLS_V1",
"OPERA_L3_DIST-ANN-HLS_V1",
"OPERA_L3_DSWX-HLS_V1",
"OPERA_L3_DSWX-S1_V1",
],
"EntryTitle": [
"OPERA Coregistered Single-Look Complex from Sentinel-1 Static Layers validated product (Version 1)",
"OPERA Coregistered Single-Look Complex from Sentinel-1 validated product (Version 1)",
"OPERA Radiometric Terrain Corrected SAR Backscatter from Sentinel-1 Static Layers validated product (Version 1)",
"OPERA Radiometric Terrain Corrected SAR Backscatter from Sentinel-1 validated product (Version 1)",
"OPERA Land Surface Disturbance Alert from Harmonized Landsat Sentinel-2 product (Version 1)",
"OPERA Land Surface Disturbance Annual from Harmonized Landsat Sentinel-2 product (Version 1)",
"OPERA Dynamic Surface Water Extent from Harmonized Landsat Sentinel-2 product (Version 1)",
"OPERA Dynamic Surface Water Extent from Sentinel-1 (Version 1)",
],
}
df = pd.DataFrame(data)
setattr(m, "_NASA_DATA", df)
names = df["ShortName"].tolist()
setattr(m, "_NASA_DATA_NAMES", names)
# Generates the temporary
s3_cred_endpoint = "https://archive.podaac.earthdata.nasa.gov/s3credentials"
def get_temp_creds():
temp_creds_url = s3_cred_endpoint
return requests.get(temp_creds_url).json()
temp_creds_req = get_temp_creds()
session = boto3.Session(
aws_access_key_id=temp_creds_req["accessKeyId"],
aws_secret_access_key=temp_creds_req["secretAccessKey"],
aws_session_token=temp_creds_req["sessionToken"],
region_name="us-west-2",
)
rio_env = rio.Env(
AWSSession(session),
GDAL_DISABLE_READDIR_ON_OPEN="EMPTY_DIR",
CPL_VSIL_CURL_ALLOWED_EXTENSIONS="TIF, TIFF",
GDAL_HTTP_COOKIEFILE=os.path.expanduser("~/cookies.txt"),
GDAL_HTTP_COOKIEJAR=os.path.expanduser("~/cookies.txt"),
)
rio_env.__enter__()
default_title = m._NASA_DATA[m._NASA_DATA["ShortName"] == default_dataset][
"EntryTitle"
].values[0]
output = widgets.Output(
layout=widgets.Layout(width=widget_width, padding=padding, overflow="auto")
)
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Search NASA Earth data",
icon="search",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
short_name = widgets.Dropdown(
options=m._NASA_DATA_NAMES,
value=default_dataset,
description="Short Name:",
style=style,
layout=widgets.Layout(width=widget_width, padding=padding),
)
title = widgets.Text(
value=default_title,
description="Title:",
style=style,
disabled=True,
layout=widgets.Layout(width=widget_width, padding=padding),
)
max_items = widgets.IntText(
value=50,
description="Max items:",
style=style,
layout=widgets.Layout(width="125px", padding=padding),
)
bbox = widgets.Text(
value="",
description="Bounding box:",
placeholder="xmin, ymin, xmax, ymax",
style=style,
layout=widgets.Layout(width="271px", padding=padding),
)
start_date = widgets.DatePicker(
description="Start date:",
disabled=False,
style=style,
layout=widgets.Layout(width="198px", padding=padding),
)
end_date = widgets.DatePicker(
description="End date:",
disabled=False,
style=style,
layout=widgets.Layout(width="198px", padding=padding),
)
dataset = widgets.Dropdown(
value=None,
description="Dataset:",
style=style,
layout=widgets.Layout(width=widget_width, padding=padding),
)
layer = widgets.Dropdown(
value=None,
description="Layer:",
style=style,
layout=widgets.Layout(width="200px", padding=padding),
)
palette = widgets.Dropdown(
options=cmap_options,
value="tab10",
description="Colormap:",
style=style,
layout=widgets.Layout(width="200px", padding=padding),
)
buttons = widgets.ToggleButtons(
value=None,
options=["Search", "Display", "Reset", "Close"],
tooltips=["Get Items", "Display Image", "Reset", "Close"],
button_style="primary",
)
buttons.style.button_width = "65px"
def change_dataset(change):
title.value = m._NASA_DATA[m._NASA_DATA["ShortName"] == short_name.value][
"EntryTitle"
].values[0]
dataset.value = None
dataset.options = []
layer.value = None
layer.options = []
short_name.observe(change_dataset, "value")
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [close_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
short_name,
title,
widgets.HBox([max_items, bbox]),
widgets.HBox([start_date, end_date]),
dataset,
widgets.HBox([layer, palette]),
buttons,
output,
]
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
close_button.value = False
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
def button_clicked(change):
if change["new"] == "Search":
with output:
output.clear_output()
with output:
print("Searching...")
if bbox.value.strip() == "":
if m.user_roi_bounds() is not None:
bounds = tuple(m.user_roi_bounds())
else:
bounds = (
m.bounds[0][1],
m.bounds[0][0],
m.bounds[1][1],
m.bounds[1][0],
)
else:
bounds = tuple(map(float, bbox.value.split(",")))
if len(bounds) != 4:
print("Please provide a valid bounding box.")
bounds = None
if start_date.value is not None and end_date.value is not None:
date_range = (str(start_date.value), str(end_date.value))
elif start_date.value is not None:
date_range = (
str(start_date.value),
datetime.today().strftime("%Y-%m-%d"),
)
else:
date_range = None
output.clear_output(wait=True)
try:
results, gdf = nasa_data_search(
count=max_items.value,
short_name=short_name.value,
bbox=bounds,
temporal=date_range,
return_gdf=True,
)
if len(results) > 0:
if "Footprints" in m.get_layer_names():
m.remove(m.find_layer("Footprints"))
if (
hasattr(m, "_NASA_DATA_CTRL")
and m._NASA_DATA_CTRL in m.controls
):
m.remove(m._NASA_DATA_CTRL)
style = {
# "stroke": True,
"color": "#3388ff",
"weight": 2,
"opacity": 1,
"fill": True,
"fillColor": "#3388ff",
"fillOpacity": 0.1,
}
hover_style = {
"weight": style["weight"] + 2,
"fillOpacity": 0,
"color": "yellow",
}
m.add_gdf(
gdf,
layer_name="Footprints",
info_mode="on_click",
zoom_to_layer=False,
style=style,
hover_style=hover_style,
)
setattr(m, "_NASA_DATA_CTRL", m.controls[-1])
dataset.options = gdf["native-id"].values.tolist()
dataset.value = dataset.options[0]
setattr(m, "_NASA_DATA_GDF", gdf)
setattr(m, "_NASA_DATA_RESULTS", results)
if len(m._NASA_DATA_RESULTS) > 0:
links = m._NASA_DATA_RESULTS[0].data_links()
layer.options = [link.split("_")[-1] for link in links]
layer.value = layer.options[0]
else:
layer.options = []
layer.value = None
output.clear_output()
except Exception as e:
print(e)
elif change["new"] == "Display":
output.clear_output()
with output:
print("Loading...")
links = m._NASA_DATA_RESULTS[
dataset.options.index(dataset.value)
].data_links()
link = links[layer.index]
try:
if link.endswith(".tif"):
ds = xr.open_dataset(link, engine="rasterio")
setattr(m, "_NASA_DATA_DS", ds)
da = ds["band_data"]
nodata = os.environ.get("NODATA", 0)
da = da.fillna(nodata)
image = array_to_image(da)
setattr(m, "_NASA_DATA_IMAGE", image)
name_prefix = layer.value.split(".")[0]
items = dataset.value.split("_")
name_suffix = items[3] + "_" + items[4][:8] + "_" + items[6]
layer_name = f"{name_prefix}_{name_suffix}"
m.add_raster(
image,
zoom_to_layer=True,
colormap=palette.value,
nodata=nodata,
layer_name=layer_name,
)
output.clear_output()
else:
output.clear_output()
print("Only GeoTIFF files are supported.")
except Exception as e:
output.clear_output()
print(e)
elif change["new"] == "Reset":
short_name.options = m._NASA_DATA_NAMES
short_name.value = default_dataset
title.value = default_title
max_items.value = 50
bbox.value = ""
bbox.placeholder = "xmin, ymin, xmax, ymax"
start_date.value = None
end_date.value = None
dataset.options = []
dataset.value = None
layer.options = []
layer.value = None
palette.value = "tab10"
output.clear_output()
if "Footprints" in m.get_layer_names():
m.remove(m.find_layer("Footprints"))
if hasattr(m, "_NASA_DATA_CTRL") and m._NASA_DATA_CTRL in m.controls:
m.remove(m._NASA_DATA_CTRL)
elif change["new"] == "Close":
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
buttons.value = None
buttons.observe(button_clicked, "value")
toolbar_button.value = opened
if m is not None:
toolbar_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position=position
)
if toolbar_control not in m.controls:
m.add(toolbar_control)
m.tool_control = toolbar_control
else:
return toolbar_widget
oam_search_gui(m, position='topright', opened=True)
¶
Generates a tool GUI template using ipywidgets. Icons can be found at https://fontawesome.com/v4/icons
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leaflet Map object. Defaults to None. |
required |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def oam_search_gui(
m,
position: Optional[str] = "topright",
opened: Optional[bool] = True,
):
"""Generates a tool GUI template using ipywidgets. Icons can be found at https://fontawesome.com/v4/icons
Args:
m (leafmap.Map, optional): The leaflet Map object. Defaults to None.
Returns:
ipywidgets: The tool GUI widget.
"""
widget_width = "250px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
style = {"description_width": "initial"}
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Toolbar",
icon="plane",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
dropdown = widgets.Dropdown(
options=["Map bounds", "User drawn ROI", "Custom bbox"],
value="Map bounds",
description="bbox:",
layout=widgets.Layout(width=widget_width, padding=padding),
style=style,
)
start_date = widgets.DatePicker(
description="start date:",
disabled=False,
style=style,
layout=widgets.Layout(width=widget_width, padding=padding),
)
end_date = widgets.DatePicker(
description="end date:",
disabled=False,
style=style,
layout=widgets.Layout(width=widget_width, padding=padding),
)
checkbox = widgets.Checkbox(
description="Additional parameters",
indent=False,
layout=widgets.Layout(padding=padding, width=widget_width),
)
textarea = widgets.Textarea(
placeholder="Addition parameters as a dictionary, e.g., {'platform': 'UAV'}",
layout=widgets.Layout(width=widget_width),
)
params_hbox = widgets.HBox()
def checkbox_changed(change):
if checkbox.value:
params_hbox.children = [textarea]
else:
params_hbox.children = []
checkbox.observe(checkbox_changed, "value")
int_slider = widgets.IntSlider(
value=100,
min=1,
max=200,
description="Limit: ",
readout=False,
continuous_update=True,
layout=widgets.Layout(width="220px", padding=padding),
style=style,
)
int_slider_label = widgets.Label("100")
def int_slider_changed(change):
if change["new"]:
int_slider_label.value = str(int_slider.value)
int_slider.observe(int_slider_changed, "value")
buttons = widgets.ToggleButtons(
value=None,
options=["Search", "Reset", "Close"],
tooltips=["Search", "Reset", "Close"],
button_style="primary",
)
buttons.style.button_width = "80px"
output = widgets.Output(layout=widgets.Layout(width=widget_width, padding=padding))
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [close_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
dropdown,
start_date,
end_date,
widgets.HBox([int_slider, int_slider_label]),
checkbox,
params_hbox,
buttons,
output,
]
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
close_button.value = False
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
def button_clicked(change):
if change["new"] == "Search":
with output:
output.outputs = ()
if m is not None:
if dropdown.value == "Map bounds":
bbox = m.get_bbox()
elif dropdown.value == "User drawn ROI":
bbox = m.user_roi_bounds()
else:
bbox = None
if start_date.value is not None:
start = str(start_date.value)
else:
start = None
if end_date.value is not None:
end = str(end_date.value)
else:
end = None
if (
checkbox.value
and textarea.value.strip().startswith("{")
and textarea.value.strip().endswith("}")
):
params = eval(textarea.value)
else:
params = {}
limit = int_slider.value
m.oam_search(bbox, start, end, limit=limit, **params)
elif change["new"] == "Reset":
textarea.value = ""
dropdown.value = "Map bounds"
start_date.value = None
end_date.value = None
checkbox.value = False
int_slider.value = 100
output.outputs = ()
elif change["new"] == "Close":
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
buttons.value = None
buttons.observe(button_clicked, "value")
toolbar_button.value = opened
if m is not None:
toolbar_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position=position
)
if toolbar_control not in m.controls:
m.add(toolbar_control)
m.tool_control = toolbar_control
else:
return toolbar_widget
open_data_widget(m)
¶
A widget for opening local vector/raster data.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
object |
leafmap.Map |
required |
Source code in leafmap/toolbar.py
def open_data_widget(m):
"""A widget for opening local vector/raster data.
Args:
m (object): leafmap.Map
"""
import warnings
from .colormaps import list_colormaps
warnings.filterwarnings("ignore")
padding = "0px 0px 0px 5px"
style = {"description_width": "initial"}
file_type = widgets.ToggleButtons(
options=["Shapefile", "GeoJSON", "CSV", "Vector", "Raster"],
tooltips=[
"Open a shapefile",
"Open a GeoJSON file",
"Open a vector dataset",
"Create points from CSV",
"Open a vector dataset",
"Open a raster dataset",
],
)
file_type.style.button_width = "88px"
filepath = widgets.Text(
value="",
description="File path or http URL:",
tooltip="Enter a file path or http URL to vector data",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
http_widget = widgets.HBox()
file_chooser = FileChooser(
os.getcwd(), sandbox_path=m.sandbox_path, layout=widgets.Layout(width="454px")
)
file_chooser.filter_pattern = "*.shp"
file_chooser.use_dir_icons = True
layer_name = widgets.Text(
value="Shapefile",
description="Enter a layer name:",
tooltip="Enter a layer name for the selected file",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
longitude = widgets.Dropdown(
options=[],
value=None,
description="Longitude:",
layout=widgets.Layout(width="149px", padding=padding),
style=style,
)
latitude = widgets.Dropdown(
options=[],
value=None,
description="Latitude:",
layout=widgets.Layout(width="149px", padding=padding),
style=style,
)
label = widgets.Dropdown(
options=[],
value=None,
description="Label:",
layout=widgets.Layout(width="149px", padding=padding),
style=style,
)
point_check = widgets.Checkbox(
description="Is it a point layer?",
indent=False,
layout=widgets.Layout(padding=padding, width="150px"),
style=style,
)
point_popup = widgets.SelectMultiple(
options=[
"None",
],
value=["None"],
description="Popup attributes:",
disabled=False,
style=style,
)
csv_widget = widgets.HBox()
point_widget = widgets.HBox()
def point_layer_check(change):
if point_check.value:
if filepath.value.strip() != "":
m.default_style = {"cursor": "wait"}
point_popup.options = vector_col_names(filepath.value)
point_popup.value = [point_popup.options[0]]
point_widget.children = [point_check, point_popup]
else:
point_widget.children = [point_check]
point_check.observe(point_layer_check)
ok_cancel = widgets.ToggleButtons(
value=None,
options=["Apply", "Reset", "Close"],
tooltips=["Apply", "Reset", "Close"],
button_style="primary",
)
# ok_cancel.style.button_width = "50px"
bands = widgets.Text(
value=None,
description="Band:",
tooltip="Enter a list of band indices",
style=style,
layout=widgets.Layout(width="150px", padding=padding),
)
vmin = widgets.Text(
value=None,
description="vmin:",
tooltip="Minimum value of the raster to visualize",
style=style,
layout=widgets.Layout(width="148px"),
)
vmax = widgets.Text(
value=None,
description="vmax:",
tooltip="Maximum value of the raster to visualize",
style=style,
layout=widgets.Layout(width="148px"),
)
nodata = widgets.Text(
value=None,
description="Nodata:",
tooltip="Nodata the raster to visualize",
style=style,
layout=widgets.Layout(width="150px", padding=padding),
)
palette = widgets.Dropdown(
options=[],
value=None,
description="palette:",
layout=widgets.Layout(width="300px"),
style=style,
)
raster_options = widgets.VBox()
def filepath_change(_):
if file_type.value == "Raster":
pass
# if (
# filepath.value.startswith("http")
# or filepath.value.endswith(".txt")
# or filepath.value.endswith(".csv")
# ):
# bands.disabled = True
# palette.disabled = False
# # x_dim.disabled = True
# # y_dim.disabled = True
# else:
# bands.disabled = False
# palette.disabled = False
# # x_dim.disabled = True
# # y_dim.disabled = True
filepath.observe(filepath_change, "value")
tool_output = widgets.Output(
layout=widgets.Layout(max_height="150px", max_width="500px", overflow="auto")
)
main_widget = widgets.VBox(
[
file_type,
file_chooser,
http_widget,
csv_widget,
layer_name,
point_widget,
raster_options,
ok_cancel,
tool_output,
]
)
tool_output_ctrl = ipyleaflet.WidgetControl(widget=main_widget, position="topright")
if m.tool_output_ctrl is not None and m.tool_output_ctrl in m.controls:
m.remove_control(m.tool_output_ctrl)
def bands_changed(change):
if change["new"] and "," in change["owner"].value:
palette.value = None
palette.disabled = True
else:
palette.disabled = False
bands.observe(bands_changed, "value")
def chooser_callback(chooser):
filepath.value = file_chooser.selected
if file_type.value == "CSV":
import pandas as pd
df = pd.read_csv(filepath.value)
col_names = df.columns.values.tolist()
longitude.options = col_names
latitude.options = col_names
label.options = col_names
if "longitude" in col_names:
longitude.value = "longitude"
if "latitude" in col_names:
latitude.value = "latitude"
if "name" in col_names:
label.value = "name"
file_chooser.register_callback(chooser_callback)
def file_type_changed(change):
ok_cancel.value = None
file_chooser.default_path = os.getcwd()
file_chooser.reset()
layer_name.value = file_type.value
csv_widget.children = []
filepath.value = ""
tool_output.outputs = ()
if change["new"] == "Shapefile":
file_chooser.filter_pattern = "*.shp"
raster_options.children = []
point_widget.children = []
point_check.value = False
http_widget.children = []
elif change["new"] == "GeoJSON":
file_chooser.filter_pattern = ["*.geojson", "*.json"]
raster_options.children = []
point_widget.children = []
point_check.value = False
http_widget.children = [filepath]
elif change["new"] == "Vector":
file_chooser.filter_pattern = "*.*"
raster_options.children = []
point_widget.children = [point_check]
point_check.value = False
http_widget.children = [filepath]
elif change["new"] == "CSV":
file_chooser.filter_pattern = ["*.csv", "*.CSV"]
csv_widget.children = [longitude, latitude, label]
raster_options.children = []
point_widget.children = []
point_check.value = False
http_widget.children = [filepath]
elif change["new"] == "Raster":
file_chooser.filter_pattern = ["*.tif", "*.img"]
palette.options = list_colormaps(add_extra=True)
palette.value = None
raster_options.children = [
widgets.HBox([bands, vmin, vmax]),
widgets.HBox([nodata, palette]),
]
point_widget.children = []
point_check.value = False
http_widget.children = [filepath]
def ok_cancel_clicked(change):
if change["new"] == "Apply":
m.default_style = {"cursor": "wait"}
file_path = filepath.value
with tool_output:
tool_output.outputs = ()
if file_path.strip() != "":
ext = os.path.splitext(file_path)[1]
if point_check.value:
popup = list(point_popup.value)
if len(popup) == 1:
popup = popup[0]
m.add_point_layer(
file_path,
popup=popup,
layer_name=layer_name.value,
)
elif ext.lower() == ".shp":
m.add_shp(file_path, style={}, layer_name=layer_name.value)
elif ext.lower() == ".geojson":
m.add_geojson(file_path, style={}, layer_name=layer_name.value)
elif ext.lower() == ".csv" and file_type.value == "CSV":
m.add_xy_data(
file_path,
x=longitude.value,
y=latitude.value,
label=label.value,
layer_name=layer_name.value,
)
elif (
ext.lower() in [".tif", "img"]
) and file_type.value == "Raster":
band = None
vis_min = None
vis_max = None
vis_nodata = None
try:
if len(bands.value) > 0:
band = int(bands.value)
if len(vmin.value) > 0:
vis_min = float(vmin.value)
if len(vmax.value) > 0:
vis_max = float(vmax.value)
if len(nodata.value) > 0:
vis_nodata = float(nodata.value)
except Exception as _:
pass
m.add_raster(
file_path,
layer_name=layer_name.value,
band=band,
palette=palette.value,
vmin=vis_min,
vmax=vis_max,
nodata=vis_nodata,
)
else:
print("Please select a file to open.")
m.toolbar_reset()
m.default_style = {"cursor": "default"}
elif change["new"] == "Reset":
file_chooser.reset()
tool_output.outputs = ()
filepath.value = ""
m.toolbar_reset()
elif change["new"] == "Close":
if m.tool_output_ctrl is not None and m.tool_output_ctrl in m.controls:
m.remove_control(m.tool_output_ctrl)
m.tool_output_ctrl = None
m.toolbar_reset()
ok_cancel.value = None
file_type.observe(file_type_changed, names="value")
ok_cancel.observe(ok_cancel_clicked, names="value")
# file_chooser.register_callback(chooser_callback)
m.add(tool_output_ctrl)
m.tool_output_ctrl = tool_output_ctrl
open_raster_gui(m)
¶
A widget for opening local/remote COG/STAC data.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
object |
leafmap.Map |
required |
Source code in leafmap/toolbar.py
def open_raster_gui(m):
"""A widget for opening local/remote COG/STAC data.
Args:
m (object): leafmap.Map
"""
padding = "0px 0px 0px 5px"
style = {"description_width": "initial"}
tool_output = widgets.Output(
layout=widgets.Layout(max_height="150px", max_width="500px", overflow="auto")
)
file_type = widgets.ToggleButtons(
options=["GeoTIFF", "COG", "STAC", "Microsoft"],
tooltips=[
"Open a local GeoTIFF file",
"Open a remote COG file",
"Open a remote STAC item",
"Create COG from Microsoft Planetary Computer",
],
)
file_type.style.button_width = "110px"
file_chooser = FileChooser(
os.getcwd(), sandbox_path=m.sandbox_path, layout=widgets.Layout(width="454px")
)
file_chooser.filter_pattern = ["*.tif", "*.tiff"]
file_chooser.use_dir_icons = True
source_widget = widgets.VBox([file_chooser])
http_url = widgets.Text(
value="",
description="HTTP URL:",
tooltip="Enter an http URL to COG file",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
collection = widgets.Dropdown(
options=["landsat-8-c2-l2 - Landsat 8 Collection 2 Level-2"],
value="landsat-8-c2-l2 - Landsat 8 Collection 2 Level-2",
description="PC Collection:",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
items = widgets.Text(
value="LC08_L2SP_047027_20201204_02_T1",
description="STAC Items:",
tooltip="STAC Item ID",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
assets = widgets.Text(
value="SR_B7,SR_B5,SR_B4",
description="STAC Assets:",
tooltip="STAC Asset ID",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
layer_name = widgets.Text(
value="GeoTIFF",
description="Enter a layer name:",
tooltip="Enter a layer name for the selected file",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
ok_cancel = widgets.ToggleButtons(
value=None,
options=["Apply", "Reset", "Close"],
tooltips=["Apply", "Reset", "Close"],
button_style="primary",
)
# ok_cancel.style.button_width = "50px"
bands = widgets.Text(
value=None,
description="Band:",
tooltip="Enter a list of band indices",
style=style,
layout=widgets.Layout(width="150px", padding=padding),
)
band_width = "149px"
red = widgets.Dropdown(
value=None,
options=[],
description="Red:",
tooltip="Select a band for the red channel",
style=style,
layout=widgets.Layout(width=band_width, padding=padding),
)
green = widgets.Dropdown(
value=None,
options=[],
description="Green:",
tooltip="Select a band for the green channel",
style=style,
layout=widgets.Layout(width="148px", padding=padding),
)
blue = widgets.Dropdown(
value=None,
options=[],
description="Blue:",
tooltip="Select a band for the blue channel",
style=style,
layout=widgets.Layout(width=band_width, padding=padding),
)
vmin = widgets.Text(
value=None,
description="vmin:",
tooltip="Minimum value of the raster to visualize",
style=style,
layout=widgets.Layout(width="148px", padding=padding),
)
vmax = widgets.Text(
value=None,
description="vmax:",
tooltip="Maximum value of the raster to visualize",
style=style,
layout=widgets.Layout(width="148px", padding=padding),
)
nodata = widgets.Text(
value=None,
description="Nodata:",
tooltip="Nodata the raster to visualize",
style=style,
layout=widgets.Layout(width="150px", padding=padding),
)
local_tile_palettes = list_palettes(add_extra=True)
cog_stac_palettes = list_palettes(lowercase=True)
palette_options = local_tile_palettes
palette = widgets.Dropdown(
options=palette_options,
value=None,
description="palette:",
layout=widgets.Layout(width="300px", padding=padding),
style=style,
)
checkbox = widgets.Checkbox(
value=False,
description="Additional params",
indent=False,
layout=widgets.Layout(width="154px", padding=padding),
style=style,
)
add_params_text1 = "Additional parameters in the format of a dictionary, for example, \n {'palette': ['#006633', '#E5FFCC', '#662A00', '#D8D8D8', '#F5F5F5']}"
add_params_text2 = "Additional parameters in the format of a dictionary, for example, \n {'expression': '(SR_B5-SR_B4)/(SR_B5+SR_B4)'}"
add_params = widgets.Textarea(
value="",
placeholder=add_params_text1,
layout=widgets.Layout(width="454px", padding=padding),
style=style,
)
params_widget = widgets.HBox()
raster_options = widgets.VBox()
raster_options.children = [
widgets.HBox([red, green, blue]),
widgets.HBox([vmin, vmax, nodata]),
widgets.HBox([palette, checkbox]),
params_widget,
]
def collection_changed(change):
if change["new"]:
if not hasattr(m, "pc_inventory"):
setattr(m, "pc_inventory", pc.get_pc_inventory())
col_name = change["new"].split(" - ")[0]
items.value = m.pc_inventory[col_name]["first_item"]
band_names = m.pc_inventory[col_name]["bands"]
red.options = band_names
green.options = band_names
blue.options = band_names
if change["new"] == "landsat-8-c2-l2 - Landsat 8 Collection 2 Level-2":
items.value = "LC08_L2SP_047027_20201204_02_T1"
assets.value = "SR_B7,SR_B5,SR_B4"
red.value = "SR_B7"
green.value = "SR_B5"
blue.value = "SR_B4"
elif change["new"] == "sentinel-2-l2a - Sentinel-2 Level-2A":
items.value = "S2B_MSIL2A_20190629T212529_R043_T06VVN_20201006T080531"
assets.value = "B08,B04,B03"
red.value = "B08"
green.value = "B04"
blue.value = "B03"
else:
if len(band_names) > 2:
assets.value = ",".join(band_names[:3])
red.value = band_names[0]
green.value = band_names[1]
blue.value = band_names[2]
else:
assets.value = band_names[0]
red.value = band_names[0]
green.value = band_names[0]
blue.value = band_names[0]
collection.observe(collection_changed, names="value")
def band_changed(change):
if change["name"]:
if not checkbox.value:
if file_type.value == "GeoTIFF":
if hasattr(m, "tile_client"):
min_max = local_tile_vmin_vmax(
m.tile_client, bands=[red.value, green.value, blue.value]
)
vmin.value = str(min_max[0])
vmax.value = str(min_max[1])
elif file_type.value == "Microsoft":
if len(set([red.value, green.value, blue.value])) == 1:
assets.value = f"{red.value}"
else:
assets.value = f"{red.value},{green.value},{blue.value}"
red.observe(band_changed, names="value")
green.observe(band_changed, names="value")
blue.observe(band_changed, names="value")
def checkbox_changed(change):
if change["new"]:
params_widget.children = [add_params]
else:
params_widget.children = []
checkbox.observe(checkbox_changed, names="value")
def url_change(change):
if change["new"] and change["new"].startswith("http"):
with tool_output:
try:
print("Retrieving band names...")
if file_type.value == "COG":
bandnames = cog_bands(change["new"])
elif file_type.value == "STAC":
bandnames = stac_bands(change["new"])
red.options = bandnames
green.options = bandnames
blue.options = bandnames
if len(bandnames) > 2:
red.value = bandnames[0]
green.value = bandnames[1]
blue.value = bandnames[2]
else:
red.value = bandnames[0]
green.value = bandnames[0]
blue.value = bandnames[0]
tool_output.outputs = ()
except Exception as e:
print(e)
print("Error loading URL.")
return
else:
red.options = []
green.options = []
blue.options = []
vmin.value = ""
vmax.value = ""
nodata.value = ""
palette.value = None
http_url.observe(url_change, names="value")
main_widget = widgets.VBox(
[
file_type,
source_widget,
layer_name,
raster_options,
ok_cancel,
tool_output,
]
)
tool_output_ctrl = ipyleaflet.WidgetControl(widget=main_widget, position="topright")
if m.tool_output_ctrl is not None and m.tool_output_ctrl in m.controls:
m.remove_control(m.tool_output_ctrl)
def bands_changed(change):
if change["new"] and "," in change["owner"].value:
palette.value = None
palette.disabled = True
else:
palette.disabled = False
bands.observe(bands_changed, "value")
def chooser_callback(chooser):
try:
source = file_chooser.selected
tile_layer, tile_client = get_local_tile_layer(source, return_client=True)
if not hasattr(m, "tile_client"):
setattr(m, "tile_client", tile_client)
bandnames = local_tile_bands(tile_client)
red.options = bandnames
green.options = bandnames
blue.options = bandnames
if len(bandnames) > 2:
red.value = bandnames[0]
green.value = bandnames[1]
blue.value = bandnames[2]
min_max = local_tile_vmin_vmax(
tile_client, bands=[red.value, green.value, blue.value]
)
vmin.value = str(min_max[0])
vmax.value = str(min_max[1])
else:
red.value = bandnames[0]
green.value = bandnames[0]
blue.value = bandnames[0]
min_max = local_tile_vmin_vmax(tile_client)
vmin.value = str(min_max[0])
vmax.value = str(min_max[1])
except Exception as e:
with tool_output:
print(e)
file_chooser.register_callback(chooser_callback)
def file_type_changed(change):
ok_cancel.value = None
file_chooser.default_path = os.getcwd()
file_chooser.reset()
layer_name.value = file_type.value
http_url.value = ""
tool_output.outputs = ()
red.value = None
green.value = None
blue.value = None
vmin.value = ""
vmax.value = ""
nodata.value = ""
palette.value = None
if change["new"] == "GeoTIFF":
source_widget.children = [file_chooser]
file_chooser.filter_pattern = ["*.tif", "*.tiff"]
palette.options = local_tile_palettes
palette.value = None
add_params.placeholder = add_params_text1
raster_options.children = [
widgets.HBox([red, green, blue]),
widgets.HBox([vmin, vmax, nodata]),
widgets.HBox([palette, checkbox]),
params_widget,
]
elif change["new"] == "COG":
http_url.value = "https://github.com/opengeos/data/releases/download/raster/Libya-2023-09-13.tif"
source_widget.children = [http_url]
palette.options = cog_stac_palettes
palette.value = None
add_params.placeholder = add_params_text2
elif change["new"] == "STAC":
http_url.value = "https://canada-spot-ortho.s3.amazonaws.com/canada_spot_orthoimages/canada_spot5_orthoimages/S5_2007/S5_11055_6057_20070622/S5_11055_6057_20070622.json"
source_widget.children = [http_url]
palette.options = cog_stac_palettes
palette.value = None
red.value = "B3"
green.value = "B2"
blue.value = "B1"
add_params.placeholder = add_params_text2
elif change["new"] == "Microsoft":
source_widget.children = [collection, items, assets]
palette.options = cog_stac_palettes
palette.value = None
add_params.placeholder = add_params_text2
collection.options = pc.get_pc_collection_list()
collection.value = "landsat-8-c2-l2 - Landsat 8 Collection 2 Level-2"
if not hasattr(m, "pc_inventory"):
setattr(m, "pc_inventory", pc.get_pc_inventory())
items.value = "LC08_L2SP_047027_20201204_02_T1"
assets.value = "SR_B7,SR_B5,SR_B4"
def ok_cancel_clicked(change):
if change["new"] == "Apply":
m.default_style = {"cursor": "wait"}
# file_path = http_url.value
with tool_output:
tool_output.outputs = ()
print("Loading data...")
if file_type.value == "GeoTIFF" and file_chooser.selected:
band = None
vis_min = None
vis_max = None
vis_nodata = None
vis_palette = None
try:
if len(red.options) > 2:
band = [red.value, green.value, blue.value]
if len(set(band)) > 1:
palette.value = None
else:
band = [red.value]
else:
band = [red.value]
if len(vmin.value) > 0:
vis_min = float(vmin.value)
if len(vmax.value) > 0:
vis_max = float(vmax.value)
if len(nodata.value) > 0:
vis_nodata = float(nodata.value)
if (
checkbox.value
and add_params.value.strip().startswith("{")
and add_params.value.strip().endswith("}")
):
vis_params = eval(add_params.value)
if "palette" in vis_params:
vis_palette = vis_params["palette"]
else:
vis_palette = get_palette_colors(
palette.value, hashtag=True
)
elif palette.value is not None:
vis_palette = get_palette_colors(
palette.value, hashtag=True
)
except Exception as e:
pass
m.add_raster(
file_chooser.selected,
layer_name=layer_name.value,
band=band,
palette=vis_palette,
vmin=vis_min,
vmax=vis_max,
nodata=vis_nodata,
)
tool_output.outputs = ()
elif file_type.value in ["COG", "STAC"] and http_url.value:
try:
tool_output.outputs = ()
print("Loading data...")
if (
checkbox.value
and add_params.value.strip().startswith("{")
and add_params.value.strip().endswith("}")
):
vis_params = eval(add_params.value)
else:
vis_params = {}
if (
palette.value
and len(set([red.value, green.value, blue.value])) == 1
):
vis_params["colormap_name"] = palette.value
elif (
palette.value
and len(set([red.value, green.value, blue.value])) > 1
):
palette.value = None
print("Palette can only be set for single band images.")
if vmin.value and vmax.value:
vis_params["rescale"] = f"{vmin.value},{vmax.value}"
if nodata.value:
vis_params["nodata"] = nodata.value
if file_type.value == "COG":
m.add_cog_layer(
http_url.value,
name=layer_name.value,
bands=[red.value, green.value, blue.value],
**vis_params,
)
elif file_type.value == "STAC":
m.add_stac_layer(
http_url.value,
bands=[red.value, green.value, blue.value],
name=layer_name.value,
**vis_params,
)
tool_output.outputs = ()
except Exception as e:
print(e)
print("Error loading data.")
return
elif file_type.value == "Microsoft":
try:
tool_output.outputs = ()
print("Loading data...")
if (
checkbox.value
and add_params.value.strip().startswith("{")
and add_params.value.strip().endswith("}")
):
vis_params = eval(add_params.value)
else:
vis_params = {}
if (
palette.value
and len(set([red.value, green.value, blue.value])) == 1
) or (palette.value and "expression" in vis_params):
vis_params["colormap_name"] = palette.value
elif (
palette.value
and len(set([red.value, green.value, blue.value])) > 1
and "expression" not in vis_params
):
palette.value = None
print("Palette can only be set for single band images.")
if vmin.value and vmax.value:
vis_params["rescale"] = f"{vmin.value},{vmax.value}"
if nodata.value:
vis_params["nodata"] = nodata.value
col = collection.value.split(" - ")[0]
m.add_stac_layer(
collection=col,
item=items.value,
assets=assets.value,
name=layer_name.value,
**vis_params,
)
tool_output.outputs = ()
except Exception as e:
print(e)
print("Error loading data.")
return
else:
tool_output.outputs = ()
print("Please select a file and enter an http URL.")
m.toolbar_reset()
m.default_style = {"cursor": "default"}
elif change["new"] == "Reset":
file_chooser.reset()
tool_output.outputs = ()
http_url.value = ""
add_params.value = ""
checkbox.value = False
palette.value = None
red.value = None
green.value = None
blue.value = None
vmin.value = ""
vmax.value = ""
nodata.value = ""
collection.value = None
items.value = ""
assets.value = ""
m.toolbar_reset()
elif change["new"] == "Close":
if m.tool_output_ctrl is not None and m.tool_output_ctrl in m.controls:
m.remove_control(m.tool_output_ctrl)
m.tool_output_ctrl = None
m.toolbar_reset()
ok_cancel.value = None
file_type.observe(file_type_changed, names="value")
ok_cancel.observe(ok_cancel_clicked, names="value")
m.add(tool_output_ctrl)
m.tool_output_ctrl = tool_output_ctrl
plotly_basemap_gui(canvas, map_min_width='78%', map_max_width='98%')
¶
Widget for changing basemaps.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
object |
leafmap.Map. |
required |
Source code in leafmap/toolbar.py
def plotly_basemap_gui(canvas, map_min_width="78%", map_max_width="98%"):
"""Widget for changing basemaps.
Args:
m (object): leafmap.Map.
"""
from .plotlymap import basemaps
m = canvas.map
layer_count = len(m.layout.mapbox.layers)
container_widget = canvas.container_widget
map_widget = canvas.map_widget
map_widget.layout.width = map_min_width
value = "Esri.WorldTopoMap"
m.add_basemap(value)
dropdown = widgets.Dropdown(
options=list(basemaps.keys()),
value=value,
layout=widgets.Layout(width="200px"),
)
close_btn = widgets.Button(
icon="times",
tooltip="Close the basemap widget",
button_style="primary",
layout=widgets.Layout(width="32px"),
)
basemap_widget = widgets.HBox([dropdown, close_btn])
container_widget.children = [basemap_widget]
def on_click(change):
basemap_name = change["new"]
m.layout.mapbox.layers = m.layout.mapbox.layers[:layer_count]
m.add_basemap(basemap_name)
dropdown.observe(on_click, "value")
def close_click(change):
container_widget.children = []
basemap_widget.close()
map_widget.layout.width = map_max_width
canvas.toolbar_reset()
canvas.toolbar_button.value = False
close_btn.on_click(close_click)
plotly_search_basemaps(canvas)
¶
The widget for search XYZ tile services.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
plotlymap.Map |
The Plotly Map object. Defaults to None. |
required |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def plotly_search_basemaps(canvas):
"""The widget for search XYZ tile services.
Args:
m (plotlymap.Map, optional): The Plotly Map object. Defaults to None.
Returns:
ipywidgets: The tool GUI widget.
"""
import xyzservices.providers as xyz
from xyzservices import TileProvider
m = canvas.map
container_widget = canvas.container_widget
map_widget = canvas.map_widget
map_widget.layout.width = "75%"
# map_widget.layout.width = map_min_width
widget_width = "250px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
style = {"description_width": "initial"}
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Toolbar",
icon="search",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
checkbox = widgets.Checkbox(
description="Search Quick Map Services (QMS)",
indent=False,
layout=widgets.Layout(padding=padding, width=widget_width),
)
providers = widgets.Dropdown(
options=[],
value=None,
description="XYZ Tile:",
layout=widgets.Layout(width=widget_width, padding=padding),
style=style,
)
keyword = widgets.Text(
value="",
description="Search keyword:",
placeholder="OpenStreetMap",
style=style,
layout=widgets.Layout(width=widget_width, padding=padding),
)
def search_callback(change):
providers.options = []
if keyword.value != "":
tiles = search_xyz_services(keyword=keyword.value)
if checkbox.value:
tiles = tiles + search_qms(keyword=keyword.value)
providers.options = tiles
keyword.on_submit(search_callback)
buttons = widgets.ToggleButtons(
value=None,
options=["Search", "Reset", "Close"],
tooltips=["Search", "Reset", "Close"],
button_style="primary",
)
buttons.style.button_width = "80px"
output = widgets.Output(layout=widgets.Layout(width=widget_width, padding=padding))
def providers_change(change):
if change["new"] != "":
provider = change["new"]
if provider is not None:
if provider.startswith("qms"):
with output:
output.outputs = ()
print("Adding data. Please wait...")
name = provider[4:]
qms_provider = TileProvider.from_qms(name)
url = qms_provider.build_url()
attribution = qms_provider.attribution
m.add_tile_layer(url, name, attribution)
output.outputs = ()
elif provider.startswith("xyz"):
name = provider[4:]
xyz_provider = xyz.flatten()[name]
url = xyz_provider.build_url()
attribution = xyz_provider.attribution
if xyz_provider.requires_token():
with output:
output.outputs = ()
print(f"{provider} requires an API Key.")
m.add_tile_layer(url, name, attribution)
providers.observe(providers_change, "value")
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [close_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
checkbox,
keyword,
providers,
buttons,
output,
]
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
close_button.value = False
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
canvas.toolbar_reset()
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
def button_clicked(change):
if change["new"] == "Search":
providers.options = []
output.outputs = ()
if keyword.value != "":
tiles = search_xyz_services(keyword=keyword.value)
if checkbox.value:
tiles = tiles + search_qms(keyword=keyword.value)
providers.options = tiles
else:
with output:
print("Please enter a search keyword.")
elif change["new"] == "Reset":
keyword.value = ""
providers.options = []
output.outputs = ()
elif change["new"] == "Close":
canvas.toolbar_reset()
toolbar_widget.close()
buttons.value = None
buttons.observe(button_clicked, "value")
toolbar_button.value = True
container_widget.children = [toolbar_widget]
plotly_toolbar(canvas)
¶
Creates the main toolbar and adds it to the map.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
plotlymap.Map |
The plotly Map object. |
required |
Source code in leafmap/toolbar.py
def plotly_toolbar(
canvas,
):
"""Creates the main toolbar and adds it to the map.
Args:
m (plotlymap.Map): The plotly Map object.
"""
m = canvas.map
map_min_width = canvas.map_min_width
map_max_width = canvas.map_max_width
map_refresh = canvas.map_refresh
map_widget = canvas.map_widget
if not map_refresh:
width = int(map_min_width.replace("%", ""))
if width > 90:
map_min_width = "90%"
tools = {
"map": {
"name": "basemap",
"tooltip": "Change basemap",
},
"search": {
"name": "search_xyz",
"tooltip": "Search XYZ tile services",
},
"gears": {
"name": "whitebox",
"tooltip": "WhiteboxTools for local geoprocessing",
},
"folder-open": {
"name": "vector",
"tooltip": "Open local vector/raster data",
},
"picture-o": {
"name": "raster",
"tooltip": "Open COG/STAC dataset",
},
"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",
),
)
canvas.toolbar = toolbar_grid
def tool_callback(change):
if change["new"]:
current_tool = change["owner"]
for tool in toolbar_grid.children:
if tool is not current_tool:
tool.value = False
tool = change["owner"]
tool_name = tools[tool.icon]["name"]
canvas.container_widget.children = []
if tool_name == "basemap":
plotly_basemap_gui(canvas)
elif tool_name == "search_xyz":
plotly_search_basemaps(canvas)
elif tool_name == "whitebox":
plotly_whitebox_gui(canvas)
elif tool_name == "vector":
plotly_tool_template(canvas)
elif tool_name == "raster":
plotly_tool_template(canvas)
elif tool_name == "help":
import webbrowser
webbrowser.open_new_tab("https://leafmap.org")
tool.value = False
else:
canvas.container_widget.children = []
map_widget.layout.width = map_max_width
for tool in toolbar_grid.children:
tool.observe(tool_callback, "value")
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Toolbar",
icon="wrench",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
canvas.toolbar_button = toolbar_button
layers_button = widgets.ToggleButton(
value=False,
tooltip="Layers",
icon="server",
layout=widgets.Layout(height="28px", width="72px"),
)
canvas.layers_button = layers_button
toolbar_widget = widgets.VBox(layout=widgets.Layout(overflow="hidden"))
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox(layout=widgets.Layout(overflow="hidden"))
toolbar_header.children = [layers_button, toolbar_button]
toolbar_footer = widgets.VBox(layout=widgets.Layout(overflow="hidden"))
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]
# map_widget.layout.width = "85%"
elif event["type"] == "mouseleave":
if not toolbar_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.value = False
layers_button.value = False
# map_widget.layout.width = map_max_width
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
map_widget.layout.width = map_min_width
if map_refresh:
with map_widget:
map_widget.outputs = ()
display(m)
layers_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
canvas.toolbar_reset()
map_widget.layout.width = map_max_width
if not layers_button.value:
toolbar_widget.children = [toolbar_button]
if map_refresh:
with map_widget:
map_widget.outputs = ()
display(m)
toolbar_button.observe(toolbar_btn_click, "value")
def layers_btn_click(change):
if change["new"]:
layer_names = list(m.get_layers().keys())
layers_hbox = []
all_layers_chk = widgets.Checkbox(
value=True,
description="All layers on/off",
indent=False,
layout=widgets.Layout(height="18px", padding="0px 8px 25px 8px"),
)
all_layers_chk.layout.width = "30ex"
layers_hbox.append(all_layers_chk)
layer_chk_dict = {}
for name in layer_names:
if name in m.get_tile_layers():
index = m.find_layer_index(name)
layer = m.layout.mapbox.layers[index]
elif name in m.get_data_layers():
index = m.find_layer_index(name)
layer = m.data[index]
layer_chk = widgets.Checkbox(
value=layer.visible,
description=name,
indent=False,
layout=widgets.Layout(height="18px"),
)
layer_chk.layout.width = "25ex"
layer_chk_dict[name] = layer_chk
if hasattr(layer, "opacity"):
opacity = layer.opacity
elif hasattr(layer, "marker"):
opacity = layer.marker.opacity
else:
opacity = 1.0
layer_opacity = widgets.FloatSlider(
value=opacity,
description_tooltip=name,
min=0,
max=1,
step=0.01,
readout=False,
layout=widgets.Layout(width="80px"),
)
layer_settings = widgets.ToggleButton(
icon="gear",
tooltip=name,
layout=widgets.Layout(
width="25px", height="25px", padding="0px 0px 0px 5px"
),
)
def layer_chk_change(change):
if change["new"]:
m.set_layer_visibility(change["owner"].description, True)
else:
m.set_layer_visibility(change["owner"].description, False)
layer_chk.observe(layer_chk_change, "value")
def layer_opacity_change(change):
if change["new"]:
m.set_layer_opacity(
change["owner"].description_tooltip, change["new"]
)
layer_opacity.observe(layer_opacity_change, "value")
hbox = widgets.HBox(
[layer_chk, layer_settings, layer_opacity],
layout=widgets.Layout(padding="0px 8px 0px 8px"),
)
layers_hbox.append(hbox)
def all_layers_chk_changed(change):
if change["new"]:
for name in layer_names:
m.set_layer_visibility(name, True)
layer_chk_dict[name].value = True
else:
for name in layer_names:
m.set_layer_visibility(name, False)
layer_chk_dict[name].value = False
all_layers_chk.observe(all_layers_chk_changed, "value")
toolbar_footer.children = layers_hbox
toolbar_button.value = False
else:
toolbar_footer.children = [toolbar_grid]
layers_button.observe(layers_btn_click, "value")
return toolbar_widget
plotly_whitebox_gui(canvas)
¶
Display a GUI for the whitebox tool.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
canvas |
plotlymap.Canvas |
Map canvas. |
required |
Source code in leafmap/toolbar.py
def plotly_whitebox_gui(canvas):
"""Display a GUI for the whitebox tool.
Args:
canvas (plotlymap.Canvas): Map canvas.
"""
import whiteboxgui.whiteboxgui as wbt
container_widget = canvas.container_widget
map_widget = canvas.map_widget
map_width = "25%"
map_widget.layout.width = map_width
widget_width = "250px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
# style = {"description_width": "initial"}
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Toolbar",
icon="gears",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
output = widgets.Output(layout=widgets.Layout(width=widget_width, padding=padding))
tools_dict = wbt.get_wbt_dict()
wbt_toolbox = wbt.build_toolbox(
tools_dict,
max_width="800px",
max_height="500px",
sandbox_path=os.getcwd(),
)
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [close_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
wbt_toolbox,
output,
]
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]
map_widget.layout.width = map_width
elif event["type"] == "mouseleave":
if not toolbar_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.value = False
close_button.value = False
map_widget.layout.width = canvas.map_max_width
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
map_widget.layout.width = map_width
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
map_widget.layout.width = canvas.map_max_width
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
canvas.toolbar_reset()
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
toolbar_button.value = True
container_widget.children = [toolbar_widget]
save_map(m)
¶
Saves the map as HTML, JPG, or PNG.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leafmap Map object. |
required |
Source code in leafmap/toolbar.py
def save_map(m):
"""Saves the map as HTML, JPG, or PNG.
Args:
m (leafmap.Map): The leafmap Map object.
"""
import time
tool_output = widgets.Output()
m.tool_output = tool_output
tool_output.outputs = ()
save_map_widget = widgets.VBox()
save_type = widgets.ToggleButtons(
options=["HTML", "PNG", "JPG"],
tooltips=[
"Save the map as an HTML file",
"Take a screenshot and save as a PNG file",
"Take a screenshot and save as a JPG file",
],
)
file_chooser = FileChooser(
os.getcwd(), sandbox_path=m.sandbox_path, layout=widgets.Layout(width="454px")
)
file_chooser.default_filename = "my_map.html"
file_chooser.use_dir_icons = True
ok_cancel = widgets.ToggleButtons(
value=None,
options=["OK", "Cancel", "Close"],
tooltips=["OK", "Cancel", "Close"],
button_style="primary",
)
def save_type_changed(change):
ok_cancel.value = None
# file_chooser.reset()
file_chooser.default_path = os.getcwd()
if change["new"] == "HTML":
file_chooser.default_filename = "my_map.html"
elif change["new"] == "PNG":
file_chooser.default_filename = "my_map.png"
elif change["new"] == "JPG":
file_chooser.default_filename = "my_map.jpg"
save_map_widget.children = [save_type, file_chooser]
def chooser_callback(chooser):
save_map_widget.children = [save_type, file_chooser, ok_cancel]
def ok_cancel_clicked(change):
if change["new"] == "OK":
file_path = file_chooser.selected
ext = os.path.splitext(file_path)[1]
if save_type.value == "HTML" and ext.upper() == ".HTML":
tool_output.outputs = ()
m.to_html(file_path)
elif save_type.value == "PNG" and ext.upper() == ".PNG":
tool_output.outputs = ()
m.toolbar_button.value = False
if m.save_map_control is not None:
m.remove_control(m.save_map_control)
m.save_map_control = None
time.sleep(2)
screen_capture(outfile=file_path)
elif save_type.value == "JPG" and ext.upper() == ".JPG":
tool_output.outputs = ()
m.toolbar_button.value = False
if m.save_map_control is not None:
m.remove_control(m.save_map_control)
m.save_map_control = None
time.sleep(2)
screen_capture(outfile=file_path)
else:
label = widgets.Label(
value="The selected file extension does not match the selected exporting type."
)
save_map_widget.children = [save_type, file_chooser, label]
elif change["new"] == "Cancel":
tool_output.outputs = ()
file_chooser.reset()
elif change["new"] == "Close":
if m.save_map_control is not None:
m.remove_control(m.save_map_control)
m.save_map_control = None
ok_cancel.value = None
m.toolbar_reset()
save_type.observe(save_type_changed, names="value")
ok_cancel.observe(ok_cancel_clicked, names="value")
file_chooser.register_callback(chooser_callback)
save_map_widget.children = [save_type, file_chooser]
save_map_control = ipyleaflet.WidgetControl(
widget=save_map_widget, position="topright"
)
m.add(save_map_control)
m.save_map_control = save_map_control
search_basemaps(m)
¶
The widget for search XYZ tile services.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leaflet Map object. Defaults to None. |
required |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def search_basemaps(m):
"""The widget for search XYZ tile services.
Args:
m (leafmap.Map, optional): The leaflet Map object. Defaults to None.
Returns:
ipywidgets: The tool GUI widget.
"""
import xyzservices.providers as xyz
from xyzservices import TileProvider
layers = m.layers
widget_width = "250px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
style = {"description_width": "initial"}
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Toolbar",
icon="search",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
checkbox = widgets.Checkbox(
description="Search Quick Map Services (QMS)",
indent=False,
layout=widgets.Layout(padding=padding, width=widget_width),
)
providers = widgets.Dropdown(
options=[],
value=None,
description="XYZ Tile:",
layout=widgets.Layout(width=widget_width, padding=padding),
style=style,
)
keyword = widgets.Text(
value="",
description="Search keyword:",
placeholder="OpenStreetMap",
style=style,
layout=widgets.Layout(width=widget_width, padding=padding),
)
def search_callback(change):
providers.options = []
if keyword.value != "":
tiles = search_xyz_services(keyword=keyword.value)
if checkbox.value:
tiles = tiles + search_qms(keyword=keyword.value)
providers.options = tiles
keyword.on_submit(search_callback)
buttons = widgets.ToggleButtons(
value=None,
options=["Search", "Reset", "Close"],
tooltips=["Search", "Reset", "Close"],
button_style="primary",
)
buttons.style.button_width = "80px"
output = widgets.Output(layout=widgets.Layout(width=widget_width, padding=padding))
def providers_change(change):
if change["new"] != "":
provider = change["new"]
if provider is not None:
if provider.startswith("qms"):
with output:
output.outputs = ()
print("Adding data. Please wait...")
name = provider[4:]
qms_provider = TileProvider.from_qms(name)
url = qms_provider.build_url()
attribution = qms_provider.attribution
m.layers = layers
m.add_tile_layer(url, name, attribution)
output.outputs = ()
elif provider.startswith("xyz"):
name = provider[4:]
xyz_provider = xyz.flatten()[name]
url = xyz_provider.build_url()
attribution = xyz_provider.attribution
m.layers = layers
if xyz_provider.requires_token():
with output:
output.outputs = ()
print(f"{provider} requires an API Key.")
m.add_tile_layer(url, name, attribution)
providers.observe(providers_change, "value")
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [close_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
checkbox,
keyword,
providers,
buttons,
output,
]
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
close_button.value = False
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
def button_clicked(change):
if change["new"] == "Search":
providers.options = []
if keyword.value != "":
tiles = search_xyz_services(keyword=keyword.value)
if checkbox.value:
tiles = tiles + search_qms(keyword=keyword.value)
providers.options = tiles
with output:
output.outputs = ()
# print("Running ...")
elif change["new"] == "Reset":
keyword.value = ""
providers.options = []
output.outputs = ()
elif change["new"] == "Close":
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
buttons.value = None
buttons.observe(button_clicked, "value")
toolbar_button.value = True
if m is not None:
toolbar_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position="topright"
)
if toolbar_control not in m.controls:
m.add(toolbar_control)
m.tool_control = toolbar_control
else:
return toolbar_widget
search_geojson_gui(m)
¶
Generates a tool GUI template using ipywidgets.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leaflet Map object. Defaults to None. |
required |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def search_geojson_gui(m):
"""Generates a tool GUI template using ipywidgets.
Args:
m (leafmap.Map, optional): The leaflet Map object. Defaults to None.
Returns:
ipywidgets: The tool GUI widget.
"""
widget_width = "250px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
style = {"description_width": "initial"}
if len(m.geojson_layers) > 0:
geojson_layer_group = ipyleaflet.LayerGroup()
for geojson_layer in m.geojson_layers:
geojson_layer_group.add(geojson_layer)
if not hasattr(m, "geojson_layer_group"):
setattr(m, "geojson_layer_group", geojson_layer_group)
else:
m.geojson_layer_group = geojson_layer_group
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Toolbar",
icon="search-plus",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
layer_options = []
if len(m.geojson_layers) > 0:
layer_options = [layer.name for layer in m.geojson_layers]
layers = widgets.Dropdown(
options=layer_options,
value=None,
description="Layer:",
layout=widgets.Layout(width=widget_width, padding=padding),
style=style,
)
attributes = widgets.Dropdown(
options=[],
value=None,
description="Attribute:",
layout=widgets.Layout(width=widget_width, padding=padding),
style=style,
)
buttons = widgets.ToggleButtons(
value=None,
options=["Apply", "Reset", "Close"],
tooltips=["Apply", "Reset", "Close"],
button_style="primary",
)
buttons.style.button_width = "80px"
output = widgets.Output(layout=widgets.Layout(width=widget_width, padding=padding))
if len(m.geojson_layers) == 0:
with output:
print("Please add vector data layers to the map before using this tool.")
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [close_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
layers,
attributes,
buttons,
output,
]
toolbar_event = ipyevents.Event(
source=toolbar_widget, watched_events=["mouseenter", "mouseleave"]
)
def layer_change(change):
if change["new"]:
for layer in m.geojson_layers:
if layer.name == change["new"]:
df = geojson_to_df(layer.data)
attributes.options = list(df.columns)
layers.observe(layer_change, names="value")
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
close_button.value = False
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
if len(m.geojson_layers) > 0 and m.search_control is not None:
m.search_control.marker.visible = False
m.remove_control(m.search_control)
m.search_control = None
m.geojson_layer_group.clear_layers()
delattr(m, "geojson_layer_group")
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
def button_clicked(change):
if change["new"] == "Apply":
if len(m.geojson_layers) > 0 and attributes.value is not None:
if m.search_control is None:
geojson_control = ipyleaflet.SearchControl(
position="topleft",
layer=m.geojson_layer_group,
property_name=attributes.value,
marker=ipyleaflet.Marker(
icon=ipyleaflet.AwesomeIcon(
name="check", marker_color="green", icon_color="darkred"
)
),
)
m.add(geojson_control)
m.search_control = geojson_control
else:
m.search_control.property_name = attributes.value
with output:
output.outputs = ()
elif change["new"] == "Reset":
output.outputs = ()
layers.value = None
attributes.value = None
elif change["new"] == "Close":
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
if len(m.geojson_layers) > 0 and m.search_control is not None:
m.search_control.marker.visible = False
m.remove_control(m.search_control)
m.search_control = None
if hasattr(m, "geojson_layer_group"):
m.geojson_layer_group.clear_layers()
delattr(m, "geojson_layer_group")
toolbar_widget.close()
buttons.value = None
buttons.observe(button_clicked, "value")
toolbar_button.value = True
if m is not None:
toolbar_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position="topright"
)
if toolbar_control not in m.controls:
m.add(toolbar_control)
m.tool_control = toolbar_control
else:
return toolbar_widget
select_table_gui(m=None)
¶
GUI for selecting layers to display attribute table.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leaflet Map object. Defaults to None. |
None |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def select_table_gui(m=None):
"""GUI for selecting layers to display attribute table.
Args:
m (leafmap.Map, optional): The leaflet Map object. Defaults to None.
Returns:
ipywidgets: The tool GUI widget.
"""
import ipysheet
widget_width = "250px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
style = {"description_width": "initial"}
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Toolbar",
icon="table",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
layer_options = []
if len(m.geojson_layers) > 0:
layer_options = [layer.name for layer in m.geojson_layers]
layers = widgets.Dropdown(
options=layer_options,
value=None,
description="Layer:",
layout=widgets.Layout(width=widget_width, padding=padding),
style=style,
)
buttons = widgets.ToggleButtons(
value=None,
options=["Apply", "Reset", "Close"],
tooltips=["Apply", "Reset", "Close"],
button_style="primary",
)
buttons.style.button_width = "80px"
output = widgets.Output(layout=widgets.Layout(width=widget_width, padding=padding))
if len(m.geojson_layers) == 0:
with output:
print("Please add vector data layers to the map before using this tool.")
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [close_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
layers,
buttons,
output,
]
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
close_button.value = False
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
def button_clicked(change):
if change["new"] == "Apply":
if len(m.geojson_layers) > 0 and layers.value is not None:
if hasattr(m, "table_control"):
m.remove_control(m.table_control)
lyr_index = layers.options.index(layers.value)
data = m.geojson_layers[lyr_index].data
df = geojson_to_df(data)
show_table_gui(m, df)
elif change["new"] == "Reset":
output.outputs = ()
layers.value = None
elif change["new"] == "Close":
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
buttons.value = None
buttons.observe(button_clicked, "value")
toolbar_button.value = True
if m is not None:
toolbar_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position="topright"
)
if toolbar_control not in m.controls:
m.add(toolbar_control)
m.tool_control = toolbar_control
else:
return toolbar_widget
show_table_gui(m, df)
¶
Open the attribute table GUI.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leaflet Map object |
required |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def show_table_gui(m, df):
"""Open the attribute table GUI.
Args:
m (leafmap.Map, optional): The leaflet Map object
Returns:
ipywidgets: The tool GUI widget.
"""
import ipysheet
widget_width = "560px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
# style = {"description_width": "initial"}
sheet = ipysheet.from_dataframe(df.head(10))
output = widgets.Output(
layout=widgets.Layout(
width=widget_width,
padding=padding,
)
)
checkbox = widgets.Checkbox(
description="Show all rows",
indent=False,
layout=widgets.Layout(padding=padding, width="115px"),
)
sheet.layout.width = output.layout.width
def checkbox_clicked(change):
output.outputs = ()
if change["new"]:
sheet = ipysheet.from_dataframe(df)
else:
sheet = ipysheet.from_dataframe(df.head(10))
sheet.layout.max_width = output.layout.width
output.layout.max_height = str(int(m.layout.height[:-2]) - 220) + "px"
sheet.layout.max_height = output.layout.height
if sheet.layout.height > output.layout.max_height:
sheet.layout.height = output.layout.max_height
with output:
display(sheet)
checkbox.observe(checkbox_clicked, "value")
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Minimize window",
icon="window-minimize",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
toolbar_widget = widgets.VBox()
m.table_widget = toolbar_widget
m.table_output = output
reset_btn = widgets.Button(
tooltip="Reset the plot",
icon="home",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 0px"),
)
def reset_btn_clicked(b):
output.layout.width = widget_width
output.layout.max_height = str(int(m.layout.height[:-2]) - 220) + "px"
reset_btn.on_click(reset_btn_clicked)
fullscreen_btn = widgets.Button(
tooltip="Fullscreen the attribute table",
icon="arrows-alt",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 0px"),
)
def fullscreen_btn_clicked(b):
output.layout.width = "1000px"
output.layout.max_height = str(int(m.layout.height[:-2]) - 220) + "px"
sheet.layout.width = output.layout.width
with output:
output.outputs = ()
display(sheet)
fullscreen_btn.on_click(fullscreen_btn_clicked)
width_btn = widgets.Button(
tooltip="Change table width",
icon="arrows-h",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 0px"),
)
height_btn = widgets.Button(
tooltip="Change table height",
icon="arrows-v",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 0px"),
)
width_slider = widgets.IntSlider(
value=560,
min=550,
max=1500,
step=10,
description="",
readout=False,
continuous_update=False,
layout=widgets.Layout(width="100px", padding=padding),
style={"description_width": "initial"},
)
width_slider_label = widgets.Label(
value="560", layout=widgets.Layout(padding="0px 10px 0px 0px")
)
# widgets.jslink((width_slider, "value"), (width_slider_label, "value"))
def width_changed(change):
if change["new"]:
width_slider_label.value = str(width_slider.value)
output.layout.width = str(width_slider.value) + "px"
if checkbox.value:
sheet = ipysheet.from_dataframe(df)
else:
sheet = ipysheet.from_dataframe(df.head(10))
sheet.layout.width = output.layout.width
with output:
output.outputs = ()
display(sheet)
width_slider.observe(width_changed, "value")
height_slider = widgets.IntSlider(
value=250,
min=200,
max=1000,
step=10,
description="",
readout=False,
continuous_update=False,
layout=widgets.Layout(width="100px", padding=padding),
style={"description_width": "initial"},
)
height_slider_label = widgets.Label(value="250")
# widgets.jslink((height_slider, "value"), (height_slider_label, "value"))
def height_changed(change):
if change["new"]:
height_slider_label.value = str(height_slider.value)
output.layout.max_height = str(height_slider.value) + "px"
if checkbox.value:
sheet = ipysheet.from_dataframe(df)
else:
sheet = ipysheet.from_dataframe(df.head(10))
sheet.layout.height = output.layout.max_height
with output:
output.outputs = ()
display(sheet)
height_slider.observe(height_changed, "value")
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [
close_button,
toolbar_button,
reset_btn,
fullscreen_btn,
width_btn,
width_slider,
width_slider_label,
height_btn,
height_slider,
height_slider_label,
checkbox,
]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
output,
]
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]
toolbar_button.icon = "window-minimize"
elif event["type"] == "mouseleave":
if not toolbar_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.value = False
close_button.value = False
toolbar_button.icon = "window-maximize"
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
toolbar_button.icon = "window-minimize"
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.icon = "window-maximize"
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
if m is not None:
m.toolbar_reset()
if m.table_control is not None and m.table_control in m.controls:
m.remove_control(m.table_control)
m.table_control = None
delattr(m, "table_control")
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
with output:
display(sheet)
toolbar_button.value = True
if m is not None:
table_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position="topright"
)
if table_control not in m.controls:
m.add(table_control)
m.table_control = table_control
else:
return toolbar_widget
split_basemaps(m, layers_dict=None, left_name=None, right_name=None, width='120px', **kwargs)
¶
Create a split-panel map for visualizing two maps.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
ipyleaflet.Map |
An ipyleaflet map object. |
required |
layers_dict |
dict |
A dictionary of TileLayers. Defaults to None. |
None |
left_name |
str |
The default value of the left dropdown list. Defaults to None. |
None |
right_name |
str |
The default value of the right dropdown list. Defaults to None. |
None |
width |
str |
The width of the dropdown list. Defaults to "120px". |
'120px' |
Source code in leafmap/toolbar.py
def split_basemaps(
m,
layers_dict: Optional[Dict] = None,
left_name=None,
right_name=None,
width="120px",
**kwargs,
):
"""Create a split-panel map for visualizing two maps.
Args:
m (ipyleaflet.Map): An ipyleaflet map object.
layers_dict (dict, optional): A dictionary of TileLayers. Defaults to None.
left_name (str, optional): The default value of the left dropdown list. Defaults to None.
right_name (str, optional): The default value of the right dropdown list. Defaults to None.
width (str, optional): The width of the dropdown list. Defaults to "120px".
"""
from .basemaps import wms_tiles
from .leafmap import basemaps, get_basemap
controls = m.controls
layers = m.layers
m.clear_controls()
add_zoom = True
add_fullscreen = True
if layers_dict is None:
keys = []
tmp_keys = list(basemaps.keys())
for key in tmp_keys:
if key not in wms_tiles:
keys.append(key)
else:
keys = list(layers_dict.keys())
if layers_dict is None:
if left_name is None:
left_name = "ROADMAP"
if right_name is None:
right_name = "HYBRID"
else:
if left_name is None:
left_name = keys[0]
if right_name is None:
right_name = keys[-1]
if layers_dict is None:
if isinstance(left_name, str):
left_layer = get_basemap(left_name)
if isinstance(right_name, str):
right_layer = get_basemap(right_name)
else:
left_layer = layers_dict[left_name]
right_layer = layers_dict[right_name]
control = ipyleaflet.SplitMapControl(left_layer=left_layer, right_layer=right_layer)
m.add(control)
m.dragging = False
left_dropdown = widgets.Dropdown(
options=keys, value=left_name, layout=widgets.Layout(width=width)
)
left_control = ipyleaflet.WidgetControl(widget=left_dropdown, position="topleft")
m.add(left_control)
right_dropdown = widgets.Dropdown(
options=keys, value=right_name, layout=widgets.Layout(width=width)
)
right_control = ipyleaflet.WidgetControl(widget=right_dropdown, position="topright")
m.add(right_control)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
# button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
def close_btn_click(change):
if change["new"]:
m.controls = controls
m.clear_layers()
m.layers = layers
close_button.observe(close_btn_click, "value")
close_control = ipyleaflet.WidgetControl(
widget=close_button, position="bottomright"
)
m.add(close_control)
if add_zoom:
m.add(ipyleaflet.ZoomControl())
if add_fullscreen:
m.add(ipyleaflet.FullScreenControl())
m.add(ipyleaflet.ScaleControl(position="bottomleft"))
split_control = None
for ctrl in m.controls:
if isinstance(ctrl, ipyleaflet.SplitMapControl):
split_control = ctrl
break
def left_change(change):
if layers_dict is None:
split_control.left_layer.url = get_basemap(left_dropdown.value).url
else:
split_control.left_layer.url = layers_dict[left_dropdown.value].url
left_dropdown.observe(left_change, "value")
def right_change(change):
if layers_dict is None:
split_control.right_layer.url = get_basemap(right_dropdown.value).url
else:
split_control.right_layer.url = layers_dict[right_dropdown.value].url
right_dropdown.observe(right_change, "value")
stac_custom_gui(m, button_width='85px', **kwargs)
¶
Generates a tool GUI template using ipywidgets.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leaflet Map object. Defaults to None. |
required |
button_width |
str |
The button width. Defaults to "85px". |
'85px' |
**kwargs |
Additional keyword arguments that will be passed to the pystac Client.open() function. |
{} |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def stac_custom_gui(m, button_width: Optional[str] = "85px", **kwargs):
"""Generates a tool GUI template using ipywidgets.
Args:
m (leafmap.Map, optional): The leaflet Map object. Defaults to None.
button_width (str, optional): The button width. Defaults to "85px".
**kwargs: Additional keyword arguments that will be passed to the pystac Client.open() function.
Returns:
ipywidgets: The tool GUI widget.
"""
if hasattr(m, "_STAC_CATALOGS") and isinstance(m._STAC_CATALOGS, dict):
catalogs = m._STAC_CATALOGS
else:
raise ValueError("Please set the STAC catalogs first.")
if not hasattr(m, "_STAC_COLLECTIONS"):
m._STAC_COLLECTIONS = {}
if len(catalogs) == 0:
raise ValueError("Please set the STAC catalogs first.")
catalog_ids = list(catalogs.keys())
widget_width = "450px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
style = {"description_width": "initial"}
MAX_ITEMS = 20
if "MAX_ITEMS" in os.environ:
MAX_ITEMS = int(os.environ["MAX_ITEMS"])
stac_data = []
output = widgets.Output(
layout=widgets.Layout(width=widget_width, padding=padding, overflow="auto")
)
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Discver STAC Catalog",
icon="stack-exchange",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
catalog = widgets.Dropdown(
options=catalog_ids,
value=catalog_ids[0],
description="Catalog:",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
endpoint = widgets.Text(
value=catalogs[catalog.value],
description="URL:",
tooltip="STAC Catalog URL",
placeholder="Enter a STAC URL, e.g., https://earth-search.aws.element84.com/v1",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
collection = widgets.Dropdown(
options=[],
value=None,
description="Collection:",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
start_date = widgets.DatePicker(
description="Start date:",
disabled=False,
style=style,
layout=widgets.Layout(width="225px", padding=padding),
)
end_date = widgets.DatePicker(
description="End date:",
disabled=False,
style=style,
layout=widgets.Layout(width="225px", padding=padding),
)
band_names = []
item = widgets.Dropdown(
options=[],
description="Item:",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
vis_option = widgets.Dropdown(
options=["1 band (Grayscale)", "3 bands (RGB)"],
value="3 bands (RGB)",
description="Visualization:",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
layer_name = widgets.Text(
value="STAC Layer",
description="Layer name:",
tooltip="Enter a layer name for the selected file",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
band_width = "149px"
red = widgets.Dropdown(
options=band_names,
value=None,
description="Red:",
tooltip="Select a band for the red channel",
style=style,
layout=widgets.Layout(width=band_width, padding=padding),
)
green = widgets.Dropdown(
options=band_names,
value=None,
description="Green:",
tooltip="Select a band for the green channel",
style=style,
layout=widgets.Layout(width="148px", padding=padding),
)
blue = widgets.Dropdown(
options=band_names,
value=None,
description="Blue:",
tooltip="Select a band for the blue channel",
style=style,
layout=widgets.Layout(width=band_width, padding=padding),
)
gray_band = widgets.Dropdown(
options=band_names,
value=None,
description="Band:",
tooltip="Select a band for visualization",
style=style,
layout=widgets.Layout(width="227px", padding=padding),
)
palette_options = list_palettes(lowercase=True)
palette = widgets.Dropdown(
options=palette_options,
value=None,
description="Palette:",
layout=widgets.Layout(width="223px", padding=padding),
style=style,
)
band_box = widgets.HBox([red, green, blue])
def vis_option_changed(change):
if change["new"] == "1 band (Grayscale)":
band_box.children = [gray_band, palette]
else:
band_box.children = [red, green, blue]
vis_option.observe(vis_option_changed, names="value")
max_items = widgets.Text(
value="20",
description="Max items:",
placeholder="Maximum number of items to return from the STAC API",
tooltip="Maximum number of items to return from the STAC API",
style=style,
layout=widgets.Layout(width="130px", padding=padding),
)
vmin = widgets.Text(
value=None,
description="vmin:",
tooltip="Minimum value of the raster to visualize",
style=style,
layout=widgets.Layout(width="100px", padding=padding),
)
vmax = widgets.Text(
value=None,
description="vmax:",
tooltip="Maximum value of the raster to visualize",
style=style,
layout=widgets.Layout(width="100px", padding=padding),
)
nodata = widgets.Text(
value=None,
description="Nodata:",
tooltip="Nodata the raster to visualize",
style=style,
layout=widgets.Layout(width="113px", padding=padding),
)
more_label = widgets.Label(
"More options:",
layout=widgets.Layout(width="100px", padding=padding),
style=style,
)
add_footprints = widgets.Checkbox(
value=True,
description="Add image footprints",
indent=False,
layout=widgets.Layout(width="170px", padding=padding),
style=style,
)
checkbox = widgets.Checkbox(
value=False,
description="Additional params",
indent=False,
layout=widgets.Layout(width="154px", padding=padding),
style=style,
)
query_params_text = "Additional parameters to query the STAC API, for example: {'query': {'eo:cloud_cover':{'lt':10}}}"
query_params = widgets.Textarea(
value="",
placeholder=query_params_text,
layout=widgets.Layout(width="454px", padding=padding),
style=style,
)
add_params_text = "Additional parameters to visualize imagery, for example: {'palette': ['#006633', '#E5FFCC', '#662A00', '#D8D8D8', '#F5F5F5'], 'expression': '(SR_B5-SR_B4)/(SR_B5+SR_B4)'}"
add_params = widgets.Textarea(
value="",
placeholder=add_params_text,
layout=widgets.Layout(width="454px", padding=padding),
style=style,
)
def reset_options():
"""Reset the options to their default values."""
catalog.value = catalog_ids[0]
collection.options = []
collection.value = None
endpoint.value = catalogs[catalog.value]
start_date.value = None
end_date.value = None
item.options = []
item.value = None
layer_name.value = ""
max_items.value = "20"
vmin.value = ""
vmax.value = ""
nodata.value = ""
palette.value = None
add_params.value = ""
reset_bands()
vis_option.value = "3 bands (RGB)"
output.outputs = ()
params_widget = widgets.VBox()
more_options = widgets.VBox()
more_options.children = [
widgets.HBox([more_label, add_footprints, checkbox]),
params_widget,
]
buttons = widgets.ToggleButtons(
value=None,
options=["Collections", "Items", "Display", "Reset", "Close"],
tooltips=["Get Collections", "Get Items", "Display Image", "Reset", "Close"],
button_style="primary",
)
buttons.style.button_width = button_width
dataset_widget = widgets.VBox()
dataset_widget.children = [endpoint, collection]
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [close_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
catalog,
dataset_widget,
widgets.HBox([start_date, end_date]),
item,
vis_option,
band_box,
more_options,
buttons,
output,
]
toolbar_event = ipyevents.Event(
source=toolbar_widget, watched_events=["mouseenter", "mouseleave"]
)
def update_bands():
excluded = [
"index",
"metadata",
"mtl.json",
"mtl.txt",
"mtl.xml",
"qa",
"qa-browse",
"QA",
"rendered_preview",
"tilejson",
"tir-browse",
"vnir-browse",
"xml",
"documentation",
]
if len(stac_data) > 0:
bnames = stac_data[0][item.value]["bands"]
else:
bnames = []
bnames = [b for b in bnames if b not in excluded]
red.options = bnames
green.options = bnames
blue.options = bnames
if len(bnames) >= 3:
vis_option.value = "3 bands (RGB)"
palette.value = None
gray_band.options = bnames
default_bands = set_default_bands(bnames)
try:
red.value = default_bands[0]
green.value = default_bands[1]
blue.value = default_bands[2]
except Exception as e:
red.value = None
green.value = None
blue.value = None
elif len(bnames) >= 1:
vis_option.value = "1 band (Grayscale)"
gray_band.options = bnames
gray_band.value = bnames[0]
red.value = bnames[0]
green.value = bnames[0]
blue.value = bnames[0]
def reset_bands():
red.options = []
green.options = []
blue.options = []
gray_band.options = []
red.value = None
green.value = None
blue.value = None
gray_band.value = None
def catalog_changed(change):
if change["new"]:
endpoint.value = catalogs[catalog.value]
collection.options = []
collection.value = None
item.options = []
item.value = None
if catalog.value in m._STAC_COLLECTIONS:
collection.options = m._STAC_COLLECTIONS[catalog.value]
collection.value = m._STAC_COLLECTIONS[catalog.value][0]
reset_bands()
catalog.observe(catalog_changed, names="value")
def endpoint_changed(change):
with output:
output.outputs = ()
output.clear_output()
print("Retrieving collections...")
try:
if endpoint.value is not None:
collection.options = []
collection.value = None
collections = stac_collections(
endpoint.value, return_ids=True, get_root=False, **kwargs
)
if collections:
collections.sort()
collection.options = collections
collection.value = collections[0]
output.clear_output()
output.outputs = ()
else:
print("No collections found.")
else:
print("No URL provided.")
reset_bands()
except Exception as e:
print(e)
endpoint.on_submit(endpoint_changed)
def collection_changed(change):
if change["new"]:
reset_bands()
item.options = []
item.value = None
collection.observe(collection_changed, names="value")
def item_changed(change):
if change["new"]:
layer_name.value = item.value
item.observe(item_changed, names="value")
def checkbox_changed(change):
if change["new"]:
params_widget.children = [
layer_name,
widgets.HBox([max_items, vmin, vmax, nodata]),
query_params,
add_params,
]
else:
params_widget.children = []
checkbox.observe(checkbox_changed, names="value")
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
close_button.value = False
toolbar_event.on_dom_event(handle_toolbar_event)
def toolbar_btn_click(change):
if change["new"]:
close_button.value = False
toolbar_widget.children = [toolbar_header, toolbar_footer]
else:
if not close_button.value:
toolbar_widget.children = [toolbar_button]
toolbar_button.observe(toolbar_btn_click, "value")
def close_btn_click(change):
if change["new"]:
toolbar_button.value = False
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
close_button.observe(close_btn_click, "value")
def button_clicked(change):
if change["new"] == "Collections":
collection.options = []
with output:
output.outputs = ()
print("Retrieving collections...")
try:
if endpoint.value is not None:
if (
endpoint.value == catalogs[catalog.value]
and catalog.value in m._STAC_COLLECTIONS
):
collections = m._STAC_COLLECTIONS[catalog.value]
else:
collections = stac_collections(
endpoint.value, return_ids=True, **kwargs
)
if collections:
collections.sort()
collection.options = collections
collection.value = collections[0]
m._STAC_COLLECTIONS[catalog.value] = collections
output.clear_output()
output.outputs = ()
else:
print("No collections found.")
else:
print("No URL provided.")
except Exception as e:
print(e)
elif change["new"] == "Items":
with output:
output.outputs = ()
if endpoint.value is not None:
if start_date.value is not None and end_date.value is not None:
datetime = str(start_date.value) + "/" + str(end_date.value)
elif start_date.value is not None:
datetime = str(start_date.value) + "/.."
elif end_date.value is not None:
datetime = "../" + str(end_date.value)
else:
datetime = None
if m is not None:
if m.user_roi is not None:
intersects = m.user_roi["geometry"]
else:
intersects = bbox_to_geojson(m.bounds)
else:
intersects = None
if (
checkbox.value
and query_params.value.strip().startswith("{")
and query_params.value.strip().endswith("}")
):
query = eval(query_params.value)
elif query_params.value.strip() == "":
query = {}
else:
print(
"Invalid query parameters. It must be a dictionary with keys such as 'query', 'sortby', 'filter', 'fields'"
)
query = {}
print("Retrieving items...")
try:
if collection.value is None:
output.outputs = ()
print("Please click on 'Collections' to retrieve collections.")
else:
item.options = []
item.value = None
reset_bands()
search = stac_search(
url=endpoint.value,
collections=[collection.value],
max_items=int(max_items.value),
intersects=intersects,
datetime=datetime,
**query,
**kwargs,
)
search_dict = stac_search_to_dict(search)
item.options = list(search_dict.keys())
item.value = list(search_dict.keys())[0]
setattr(m, "stac_search", search)
setattr(m, "stac_dict", search_dict)
setattr(m, "stac_items", stac_search_to_list(search))
if add_footprints.value and m is not None:
gdf = stac_search_to_gdf(search)
style = {
"stroke": True,
"color": "#3388ff",
"weight": 2,
"opacity": 1,
"fill": True,
"fillColor": "#000000",
"fillOpacity": 0,
}
hover_style = {"weight": 4}
m.add_gdf(
gdf,
style=style,
hover_style=hover_style,
layer_name="Footprints",
zoom_to_layer=False,
info_mode="on_click",
)
setattr(m, "stac_gdf", gdf)
stac_data.clear()
stac_data.append(search_dict)
update_bands()
output.clear_output()
output.outputs = ()
except NotImplementedError as e:
print(e)
except Exception as e:
print(e)
elif change["new"] == "Display":
with output:
output.outputs = ()
if item.value and m is not None:
print("Loading data...")
if (
checkbox.value
and add_params.value.strip().startswith("{")
and add_params.value.strip().endswith("}")
):
vis_params = eval(add_params.value)
else:
vis_params = {}
if (
vis_option.value == "1 band (Grayscale)"
and (palette.value is not None)
) or (palette.value and "expression" in vis_params):
vis_params["colormap_name"] = palette.value
elif (
palette.value
and len(set([red.value, green.value, blue.value])) > 1
and "expression" not in vis_params
):
palette.value = None
print("Palette can only be set for single band images.")
if vmax.value and not vmin.value:
vmin.value = "0"
if vmin.value and vmax.value:
vis_params["rescale"] = f"{vmin.value},{vmax.value}"
if nodata.value:
vis_params["nodata"] = nodata.value
if vis_option.value == "1 band (Grayscale)":
assets = gray_band.value
else:
assets = f"{red.value},{green.value},{blue.value}"
try:
if "Planetary Computer" in catalog.value:
output.outputs = ()
m.add_stac_layer(
collection=collection.value,
item=item.value,
assets=assets,
name=layer_name.value,
fit_bounds=False,
**vis_params,
)
setattr(
m,
"stac_item",
m.stac_dict[item.value],
)
m.stac_item["collection"] = collection.value
else:
output.outputs = ()
m.add_stac_layer(
url=stac_data[0][item.value]["href"],
assets=assets,
name=layer_name.value,
fit_bounds=False,
**vis_params,
)
setattr(m, "stac_item", m.stac_dict[item.value])
if "rescale" in m.layers[-1].url:
output.outputs = ()
output.clear_output()
except Exception as e:
print(e)
else:
print("Please click on 'Items' to retrieve items.")
buttons.value = None
elif change["new"] == "Reset":
reset_options()
elif change["new"] == "Close":
if m is not None:
m.toolbar_reset()
if m.tool_control is not None and m.tool_control in m.controls:
m.remove_control(m.tool_control)
m.tool_control = None
toolbar_widget.close()
buttons.value = None
buttons.observe(button_clicked, "value")
toolbar_button.value = True
if m is not None:
toolbar_control = ipyleaflet.WidgetControl(
widget=toolbar_widget, position="topright"
)
if toolbar_control not in m.controls:
m.add(toolbar_control)
m.tool_control = toolbar_control
else:
return toolbar_widget
stac_gui(m, position='topright', opened=True, **kwargs)
¶
Generates a tool GUI template using ipywidgets.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
m |
leafmap.Map |
The leaflet Map object. Defaults to None. |
required |
Returns:
Type | Description |
---|---|
ipywidgets |
The tool GUI widget. |
Source code in leafmap/toolbar.py
def stac_gui(
m,
position: Optional[str] = "topright",
opened: Optional[bool] = True,
**kwargs,
):
"""Generates a tool GUI template using ipywidgets.
Args:
m (leafmap.Map, optional): The leaflet Map object. Defaults to None.
Returns:
ipywidgets: The tool GUI widget.
"""
import pandas as pd
if os.environ.get("STAC_CATALOGS") is not None:
try:
source = eval(os.environ.get("STAC_CATALOGS"))
m.set_catalog_source(source)
except Exception as e:
print(e)
print("Invalid STAC_CATALOGS environment variable.")
if hasattr(m, "_STAC_CATALOGS"):
stac_custom_gui(m, **kwargs)
return
widget_width = "450px"
padding = "0px 0px 0px 5px" # upper, right, bottom, left
style = {"description_width": "initial"}
MAX_ITEMS = 20
if "MAX_ITEMS" in os.environ:
MAX_ITEMS = int(os.environ["MAX_ITEMS"])
catalog_path = download_data_catalogs()
aws_open_data_path = os.path.join(catalog_path, "aws_stac_catalogs.tsv")
gee_path = os.path.join(catalog_path, "gee_catalog.tsv")
pc_path = os.path.join(catalog_path, "pc_catalog.tsv")
nasa_path = os.path.join(catalog_path, "nasa_cmr_catalog.tsv")
stac_index_path = os.path.join(catalog_path, "stac_catalogs.tsv")
stac_data = []
stac_info = {
"AWS Open Data": {
"filename": aws_open_data_path,
"name": "Name",
"url": "Endpoint",
"description": "Description",
},
"Google Earth Engine": {
"filename": gee_path,
"name": "id",
"url": "url",
"description": "title",
},
"Microsoft Planetary Computer": {
"filename": pc_path,
"name": "title",
"url": "link",
"description": "description",
},
"NASA Common Metadata Repository": {
"filename": nasa_path,
"name": "id",
"url": "url",
"description": "title",
},
"STAC Index Catalogs": {
"filename": stac_index_path,
"name": "title",
"url": "url",
"description": "summary",
},
"Custom STAC API Endpoint": {
"filename": "",
"name": "",
"url": "",
"description": "",
},
}
connections = list(stac_info.keys())
output = widgets.Output(
layout=widgets.Layout(width=widget_width, padding=padding, overflow="auto")
)
toolbar_button = widgets.ToggleButton(
value=False,
tooltip="Discver STAC Catalog",
icon="stack-exchange",
layout=widgets.Layout(width="28px", height="28px", padding="0px 0px 0px 4px"),
)
close_button = widgets.ToggleButton(
value=False,
tooltip="Close the tool",
icon="times",
button_style="primary",
layout=widgets.Layout(height="28px", width="28px", padding="0px 0px 0px 4px"),
)
connection = widgets.Dropdown(
options=connections,
value="AWS Open Data",
description="Catalog:",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
df = pd.read_csv(stac_info[connection.value]["filename"], sep="\t")
datasets = df[stac_info[connection.value]["name"]].tolist()
dataset = widgets.Dropdown(
options=datasets,
value="Sentinel-2 Cloud-Optimized GeoTIFFs",
description="Dataset:",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
description = widgets.Text(
value="Sentinel-2 Level 2A scenes and metadata",
description="Description:",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
http_url = widgets.Text(
value="https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a",
description="URL:",
tooltip="STAC Catalog URL",
placeholder="Enter a STAC URL, e.g., https://earth-search.aws.element84.com/v1",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
custom_dataset = widgets.Dropdown(
options=[],
value=None,
description="Dataset:",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
start_date = widgets.DatePicker(
description="Start date:",
disabled=False,
style=style,
layout=widgets.Layout(width="225px", padding=padding),
)
end_date = widgets.DatePicker(
description="End date:",
disabled=False,
style=style,
layout=widgets.Layout(width="225px", padding=padding),
)
collection = widgets.Dropdown(
options=[],
value=None,
description="Collection:",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
band_names = ["red", "green", "blue"]
item = widgets.Dropdown(
options=[],
description="Item:",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
layer_name = widgets.Text(
value="STAC Layer",
description="Layer name:",
tooltip="Enter a layer name for the selected file",
style=style,
layout=widgets.Layout(width="454px", padding=padding),
)
band_width = "149px"
red = widgets.Dropdown(
options=band_names,
value="red",
description="Red:",
tooltip="Select a band for the red channel",
style=style,
layout=widgets.Layout(width=band_width, padding=padding),
)
green = widgets.Dropdown(
options=band_names,
value="green",
description="Green:",
tooltip="Select a band for the green channel",
style=style,
layout=widgets.Layout(width="148px", padding=padding),
)
blue = widgets.Dropdown(
options=band_names,
value="blue",
description="Blue:",
tooltip="Select a band for the blue channel",
style=style,
layout=widgets.Layout(width=band_width, padding=padding),
)
max_items = widgets.Text(
value="20",
description="Max items:",
placeholder="Maximum number of items to return from the STAC API",
tooltip="Maximum number of items to return from the STAC API",
style=style,
layout=widgets.Layout(width="130px", padding=padding),
)
vmin = widgets.Text(
value=None,
description="vmin:",
tooltip="Minimum value of the raster to visualize",
style=style,
layout=widgets.Layout(width="100px", padding=padding),
)
vmax = widgets.Text(
value=None,
description="vmax:",
tooltip="Maximum value of the raster to visualize",
style=style,
layout=widgets.Layout(width="100px", padding=padding),
)
nodata = widgets.Text(
value=None,
description="Nodata:",
tooltip="Nodata the raster to visualize",
style=style,
layout=widgets.Layout(width="113px", padding=padding),
)
palette_options = list_palettes(lowercase=True)
palette = widgets.Dropdown(
options=palette_options,
value=None,
description="palette:",
layout=widgets.Layout(width="180px", padding=padding),
style=style,
)
add_footprints = widgets.Checkbox(
value=True,
description="Add footprints",
indent=False,
layout=widgets.Layout(width="120px", padding=padding),
style=style,
)
checkbox = widgets.Checkbox(
value=False,
description="Additional params",
indent=False,
layout=widgets.Layout(width="154px", padding=padding),
style=style,
)
query_params_text = "Additional parameters to query the STAC API, for example: {'query': {'eo:cloud_cover':{'lt':10}}}"
query_params = widgets.Textarea(
value="",
placeholder=query_params_text,
layout=widgets.Layout(width="454px", padding=padding),
style=style,
)
add_params_text = "Additional parameters to visualize imagery, for example: {'palette': ['#006633', '#E5FFCC', '#662A00', '#D8D8D8', '#F5F5F5'], 'expression': '(SR_B5-SR_B4)/(SR_B5+SR_B4)'}"
add_params = widgets.Textarea(
value="",
placeholder=add_params_text,
layout=widgets.Layout(width="454px", padding=padding),
style=style,
)
def reset_options(reset_url=True):
"""Reset the options to their default values."""
connection.value = "AWS Open Data"
dataset.options = datasets
dataset.value = "Sentinel-2 Cloud-Optimized GeoTIFFs"
http_url.value = (
"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a"
)
start_date.value = None
end_date.value = None
item.options = []
item.value = None
layer_name.value = ""
red.options = ["red", "green", "blue"]
green.options = ["red", "green", "blue"]
blue.options = ["red", "green", "blue"]
red.value = "red"
green.value = "green"
blue.value = "blue"
max_items.value = "20"
vmin.value = ""
vmax.value = ""
nodata.value = ""
palette.value = None
add_params.value = ""
output.outputs = ()
params_widget = widgets.VBox()
raster_options = widgets.VBox()
raster_options.children = [
widgets.HBox([red, green, blue]),
widgets.HBox([palette, add_footprints, checkbox]),
params_widget,
]
buttons = widgets.ToggleButtons(
value=None,
options=["Search", "Display", "Reset", "Close"],
tooltips=["Get Items", "Display Image", "Reset", "Close"],
button_style="primary",
)
buttons.style.button_width = "65px"
dataset_widget = widgets.VBox()
dataset_widget.children = [
dataset,
description,
http_url,
]
toolbar_widget = widgets.VBox()
toolbar_widget.children = [toolbar_button]
toolbar_header = widgets.HBox()
toolbar_header.children = [close_button, toolbar_button]
toolbar_footer = widgets.VBox()
toolbar_footer.children = [
connection,
dataset_widget,
widgets.HBox([start_date, end_date]),
item,
raster_options,
buttons,
output,
]
toolbar_event = ipyevents.Event(
source=toolbar_widget, watched_events=["mouseenter", "mouseleave"]
)
def update_bands():
if len(stac_data) > 0:
bnames = stac_data[0][item.value]["bands"]
else:
bnames = []
red.options = bnames
green.options = bnames
blue.options = bnames
default_bands = set_default_bands(bnames)
try:
red.value = default_bands[0]
green.value = default_bands[1]
blue.value = default_bands[2]
except Exception as e:
red.value = None
green.value = None
blue.value = None
def connection_changed(change):
if change["new"]:
if connection.value != "Custom STAC API Endpoint":
df = pd.read_csv(stac_info[connection.value]["filename"], sep="\t")
datasets = df[stac_info[connection.value]["name"]].tolist()
dataset.options = datasets
dataset.value = datasets[0]
dataset_widget.children = [dataset, description, http_url]
else:
http_url.value = "https://earth-search.aws.element84.com/v1"
dataset_widget.children = [http_url, custom_dataset]
with output:
print("Enter a STAC API endpoint and press Enter to list datasets")
connection.observe(connection_changed, names="value")
def dataset_changed(change):
if change["new"]:
if connection.value != "Custom STAC API Endpoint":
df = pd.read_csv(stac_info[connection.value]["filename"], sep="\t")
df = df[df[stac_info[connection.value]["name"]] == dataset.value]
description.value = df[
stac_info[connection.value]["description"]
].tolist()[0]
http_url.value = df[stac_info[connection.value]["url"]].tolist()[0]
item.options = []
custom_dataset.options = []
stac_data.clear()
update_bands()
dataset.observe(dataset_changed, names="value")
def http_url_changed(change):
with output:
output.outputs = ()
print("Searching...")
try:
if connection.value == "Custom STAC API Endpoint":
custom_collections = stac_collections(
http_url.value, return_ids=True
)
if custom_collections:
custom_dataset.options = custom_collections
collection_id = http_url.value.split("/")[-1]
if collection_id in custom_collections:
custom_dataset.value = collection_id
else:
custom_dataset.options = []
else:
custom_cols = stac_collections(http_url.value, return_ids=True)
item.options = custom_cols
stac_data.clear()
update_bands()
output.outputs = ()
except Exception as e:
print(e)
http_url.on_submit(http_url_changed)
def item_changed(change):
if change["new"]:
layer_name.value = item.value
with output:
update_bands()
if dataset.value == "Sentinel-2 Cloud-Optimized GeoTIFFs":