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

Merged
merged 12 commits into from
Jun 12, 2024
106 changes: 102 additions & 4 deletions geemap/map_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from IPython.core.display import HTML, display

import ee
import ipyevents
import ipytree
import ipywidgets

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,10 +823,107 @@ 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(
"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:
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
Loading