Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add layer loading spinner and remove layer button #1983

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
107 changes: 103 additions & 4 deletions geemap/map_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import ee
import ipytree
import ipywidgets
import ipyevents

from . import common

Expand Down Expand Up @@ -765,7 +766,7 @@ def close_button_hidden(self, value):
def refresh_layers(self):
"""Recreates all the layer widgets."""
toggle_all_layout = ipywidgets.Layout(
height="18px", width="30ex", padding="0px 8px 25px 8px"
height="18px", width="30ex", padding="0px 4px 25px 4px"
)
toggle_all_checkbox = ipywidgets.Checkbox(
value=False,
Expand Down Expand Up @@ -809,7 +810,7 @@ def _render_layer_row(self, layer):
max=1,
step=0.01,
readout=False,
layout=ipywidgets.Layout(width="80px"),
layout=ipywidgets.Layout(width="70px", padding="0px 3px 0px 0px"),
)
opacity_slider.observe(
lambda change: self._on_layer_opacity_changed(change, layer), "value"
Expand All @@ -822,11 +823,109 @@ def _render_layer_row(self, layer):
)
settings_button.on_click(self._on_layer_settings_click)

spinner = ipywidgets.Button(
giswqs marked this conversation as resolved.
Show resolved Hide resolved
icon="times",
layout=ipywidgets.Layout(width="25px", height="25px", padding="0px"),
tooltip="Loaded",
)

def loading_change(change):
if change["new"]:
spinner.tooltip = "Loading ..."
spinner.icon = "spinner spin lg"
else:
spinner.tooltip = "Loaded"
spinner.icon = "times"

layer.observe(loading_change, "loading")

spinner_event = ipyevents.Event(
source=spinner, watched_events=["mouseenter", "mouseleave"]
)

def handle_spinner_event(event):
if event["type"] == "mouseenter":
spinner.icon = "times"
elif event["type"] == "mouseleave":
if layer.loading:
spinner.icon = "spinner spin lg"
else:
spinner.icon = "times"

spinner_event.on_dom_event(handle_spinner_event)

def remove_layer_click(_):
self._on_layer_remove_click(layer)

spinner.on_click(remove_layer_click)

return ipywidgets.HBox(
[visibility_checkbox, settings_button, opacity_slider],
layout=ipywidgets.Layout(padding="0px 8px 0px 8px"),
[
visibility_checkbox,
opacity_slider,
settings_button,
spinner,
],
layout=ipywidgets.Layout(padding="0px 4px 0px 4px"),
)

def _find_layer_row_index(self, layer):
for index, child in enumerate(self._toolbar_footer.children[1:]):
if child.children[0].description == layer.name:
return index + 1
return -1

def _remove_confirm_widget(self):
for index, child in enumerate(self._toolbar_footer.children[1:]):
if child.children[0].value == "Remove layer?":
self._toolbar_footer.children = (
self._toolbar_footer.children[: index + 1]
+ self._toolbar_footer.children[index + 2 :]
)
break

def _on_layer_remove_click(self, layer):

self._remove_confirm_widget()

label = ipywidgets.Label(
f"Remove layer?",
layout=ipywidgets.Layout(padding="0px 4px 0px 4px"),
)
yes_button = ipywidgets.Button(
description="Yes",
button_style="primary",
)
yes_button.layout.width = "86px"
no_button = ipywidgets.Button(
description="No",
button_style="primary",
)
no_button.layout.width = "86px"

confirm_widget = ipywidgets.HBox(
[label, yes_button, no_button], layout=ipywidgets.Layout(width="284px")
)

layer_row_index = self._find_layer_row_index(layer)

self._toolbar_footer.children = (
list(self._toolbar_footer.children[: layer_row_index + 1])
+ [confirm_widget]
+ list(self._toolbar_footer.children[layer_row_index + 1 :])
)

def on_yes_button_click(_):
self._host_map.remove_layer(layer)
self._remove_confirm_widget()

yes_button.on_click(on_yes_button_click)

def on_no_button_click(_):
self._remove_confirm_widget()

no_button.on_click(on_no_button_click)

def _compute_layer_opacity(self, layer):
if layer in self._host_map.geojson_layers:
opacity = layer.style.get("opacity", 1.0)
Expand Down
6 changes: 6 additions & 0 deletions tests/fake_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ def __init__(self, name="test-layer", visible=True, opacity=1.0):
self.visible = visible
self.opacity = opacity

def observe(self, func, names):
pass


class FakeTileLayer:
def __init__(self, name="test-layer", visible=True, opacity=1.0):
Expand All @@ -128,3 +131,6 @@ def __init__(self, name="test-layer", visible=True, style=None):
self.name = name
self.visible = visible
self.style = style or {}

def observe(self, func, names):
pass