Skip to content

toolbar module

Module for dealing with interactive GUIs.

Toolbar (AnyWidget)

A toolbar that can be added to the map.

Source code in geemap/toolbar.py
@map_widgets.Theme.apply
class Toolbar(anywidget.AnyWidget):
    """A toolbar that can be added to the map."""

    _esm = pathlib.Path(__file__).parent / "static" / "toolbar.js"

    # The accessory widget.
    accessory_widgets = map_widgets.TypedTuple(
        trait=traitlets.Instance(widgets.Widget),
        help="The accessory widget",
    ).tag(sync=True, **widgets.widget_serialization)

    # The list of main tools.
    main_tools = map_widgets.TypedTuple(
        trait=traitlets.Instance(widgets.Widget),
        help="List of main tools",
    ).tag(sync=True, **widgets.widget_serialization)

    # The list of extra tools.
    extra_tools = map_widgets.TypedTuple(
        trait=traitlets.Instance(widgets.Widget),
        help="List of extra tools",
    ).tag(sync=True, **widgets.widget_serialization)

    # Whether the toolbar is expanded.
    expanded = traitlets.Bool(False).tag(sync=True)

    # The currently selected tab.
    tab_index = traitlets.Int(0).tag(sync=True)

    _TOGGLE_EXPAND_ICON = "add"
    _TOGGLE_EXPAND_TOOLTIP = "Expand toolbar"
    _TOGGLE_COLLAPSE_ICON = "remove"
    _TOGGLE_COLLAPSE_TOOLTIP = "Collapse toolbar"

    def __init__(
        self,
        host_map: "geemap.Map",
        main_tools: List[ToolbarItem],
        extra_tools: List[ToolbarItem],
        accessory_widgets: List[widgets.DOMWidget],
    ):
        """Adds a toolbar with `main_tools` and `extra_tools` to the `host_map`."""
        super().__init__()
        if not main_tools:
            raise ValueError("A toolbar cannot be initialized without `main_tools`.")
        self.host_map = host_map
        self.toggle_widget = ToolbarItem(
            icon=self._TOGGLE_EXPAND_ICON,
            tooltip=self._TOGGLE_EXPAND_TOOLTIP,
            callback=self._toggle_callback,
            reset=True,
        )
        self.main_tools = main_tools + ([self.toggle_widget] if extra_tools else [])
        self.extra_tools = extra_tools
        for widget in self.main_tools + self.extra_tools:
            widget.callback_wrapper = lambda callback, value, tool: callback(
                self.host_map, value, tool
            )
        self.accessory_widgets = accessory_widgets

    def reset(self):
        """Resets the toolbar so that no widget is selected."""
        for widget in self.main_tools + self.extra_tools:
            widget.value = False

    def _toggle_callback(self, m, selected, item):
        del m, item  # unused
        if not selected:
            return
        if self.toggle_widget.icon == self._TOGGLE_EXPAND_ICON:
            self.expanded = True
            self.toggle_widget.tooltip_text = self._TOGGLE_COLLAPSE_TOOLTIP
            self.toggle_widget.icon = self._TOGGLE_COLLAPSE_ICON
        elif self.toggle_widget.icon == self._TOGGLE_COLLAPSE_ICON:
            self.expanded = False
            self.toggle_widget.tooltip_text = self._TOGGLE_EXPAND_TOOLTIP
            self.toggle_widget.icon = self._TOGGLE_EXPAND_ICON

__init__(self, host_map, main_tools, extra_tools, accessory_widgets) special

Adds a toolbar with main_tools and extra_tools to the host_map.

Source code in geemap/toolbar.py
def __init__(
    self,
    host_map: "geemap.Map",
    main_tools: List[ToolbarItem],
    extra_tools: List[ToolbarItem],
    accessory_widgets: List[widgets.DOMWidget],
):
    """Adds a toolbar with `main_tools` and `extra_tools` to the `host_map`."""
    super().__init__()
    if not main_tools:
        raise ValueError("A toolbar cannot be initialized without `main_tools`.")
    self.host_map = host_map
    self.toggle_widget = ToolbarItem(
        icon=self._TOGGLE_EXPAND_ICON,
        tooltip=self._TOGGLE_EXPAND_TOOLTIP,
        callback=self._toggle_callback,
        reset=True,
    )
    self.main_tools = main_tools + ([self.toggle_widget] if extra_tools else [])
    self.extra_tools = extra_tools
    for widget in self.main_tools + self.extra_tools:
        widget.callback_wrapper = lambda callback, value, tool: callback(
            self.host_map, value, tool
        )
    self.accessory_widgets = accessory_widgets

reset(self)

Resets the toolbar so that no widget is selected.

Source code in geemap/toolbar.py
def reset(self):
    """Resets the toolbar so that no widget is selected."""
    for widget in self.main_tools + self.extra_tools:
        widget.value = False

ToolbarItem (AnyWidget)

A toolbar item widget for geemap.

Source code in geemap/toolbar.py
@map_widgets.Theme.apply
class ToolbarItem(anywidget.AnyWidget):
    """A toolbar item widget for geemap."""

    _esm = pathlib.Path(__file__).parent / "static" / "toolbar_item.js"
    active = traitlets.Bool(False).tag(sync=True)
    icon = traitlets.Unicode("").tag(sync=True)
    # Note: "tooltip" is already defined on ipywidgets.Widget.
    tooltip_text = traitlets.Unicode("").tag(sync=True)

    def __init__(
        self,
        icon: str,
        tooltip: str,
        callback: Callable[[Any, bool, Any], None],
        control: Optional[widgets.Widget] = None,
        reset=False,
        active=False,
    ):
        """A togglable, toolbar item.

        Args:
            icon (str): The icon name to use, from https://fonts.google.com/icons.
            tooltip: The tooltip text to show a user on hover.
            callback: A callback function to execute when the item icon is clicked.
                Its signature should be `callback(map, selected, item)`, where
                `map` is the host map, `selected` is a boolean indicating if the
                user selected or unselected the tool, and `item` is this object.
            control: The control widget associated with this item. Used to
                cleanup state when toggled off.
            reset: Whether to reset the selection after the callback has finished.
            active: Whether the tool is currently active.
        """
        super().__init__()
        self.icon = icon
        self.tooltip_text = tooltip
        self.callback = callback
        self.callback_wrapper = lambda *args: None
        self.control = control
        self.reset = reset
        self.active = active

    def toggle_off(self):
        if self.active:
            self.active = False

    @traitlets.observe("active")
    def _observe_value(self, change: Dict[str, Any]) -> None:
        if (value := change.get("new")) is not None:
            self.callback_wrapper(self.callback, value, self)
        if self.active and self.reset:
            self.active = False

__init__(self, icon, tooltip, callback, control=None, reset=False, active=False) special

A togglable, toolbar item.

Parameters:

Name Type Description Default
icon str

The icon name to use, from https://fonts.google.com/icons.

required
tooltip str

The tooltip text to show a user on hover.

required
callback Callable[[Any, bool, Any], NoneType]

A callback function to execute when the item icon is clicked. Its signature should be callback(map, selected, item), where map is the host map, selected is a boolean indicating if the user selected or unselected the tool, and item is this object.

required
control Optional[ipywidgets.widgets.widget.Widget]

The control widget associated with this item. Used to cleanup state when toggled off.

None
reset

Whether to reset the selection after the callback has finished.

False
active

Whether the tool is currently active.

False
Source code in geemap/toolbar.py
def __init__(
    self,
    icon: str,
    tooltip: str,
    callback: Callable[[Any, bool, Any], None],
    control: Optional[widgets.Widget] = None,
    reset=False,
    active=False,
):
    """A togglable, toolbar item.

    Args:
        icon (str): The icon name to use, from https://fonts.google.com/icons.
        tooltip: The tooltip text to show a user on hover.
        callback: A callback function to execute when the item icon is clicked.
            Its signature should be `callback(map, selected, item)`, where
            `map` is the host map, `selected` is a boolean indicating if the
            user selected or unselected the tool, and `item` is this object.
        control: The control widget associated with this item. Used to
            cleanup state when toggled off.
        reset: Whether to reset the selection after the callback has finished.
        active: Whether the tool is currently active.
    """
    super().__init__()
    self.icon = icon
    self.tooltip_text = tooltip
    self.callback = callback
    self.callback_wrapper = lambda *args: None
    self.control = control
    self.reset = reset
    self.active = active

build_toolbox(tools_dict, max_width='1080px', max_height='600px')

Build the GEE toolbox.

Parameters:

Name Type Description Default
tools_dict dict

A dictionary containing information for all tools.

required
max_width str

The maximum width of the widget.

'1080px'
max_height str

The maximum height of the widget.

'600px'

Returns:

Type Description
object

An ipywidget representing the toolbox.

Source code in geemap/toolbar.py
def build_toolbox(tools_dict, max_width="1080px", max_height="600px"):
    """Build the GEE toolbox.

    Args:
        tools_dict (dict): A dictionary containing information for all tools.
        max_width (str, optional): The maximum width of the widget.
        max_height (str, optional): The maximum height of the widget.

    Returns:
        object: An ipywidget representing the toolbox.
    """
    left_widget = widgets.VBox(layout=widgets.Layout(min_width="175px"))
    center_widget = widgets.VBox(
        layout=widgets.Layout(min_width="200px", max_width="200px")
    )
    right_widget = widgets.Output(
        layout=widgets.Layout(width="630px", max_height=max_height)
    )
    full_widget = widgets.HBox(
        [left_widget, center_widget, right_widget],
        layout=widgets.Layout(max_width=max_width, max_height=max_height),
    )

    search_widget = widgets.Text(
        placeholder="Search tools ...", layout=widgets.Layout(width="170px")
    )
    label_widget = widgets.Label(layout=widgets.Layout(width="170px"))
    label_widget.value = f"{len(tools_dict)} Available Tools"
    close_btn = widgets.Button(
        description="Close Toolbox", icon="close", layout=widgets.Layout(width="170px")
    )

    categories = {}
    categories["All Tools"] = []
    for key in tools_dict.keys():
        category = tools_dict[key]["category"]
        if category not in categories.keys():
            categories[category] = []
        categories[category].append(tools_dict[key]["name"])
        categories["All Tools"].append(tools_dict[key]["name"])

    options = list(categories.keys())
    all_tools = categories["All Tools"]
    all_tools.sort()
    category_widget = widgets.Select(
        options=options, layout=widgets.Layout(width="170px", height="165px")
    )
    tools_widget = widgets.Select(
        options=[], layout=widgets.Layout(width="195px", height="400px")
    )

    def category_selected(change):
        if change["new"]:
            selected = change["owner"].value
            options = categories[selected]
            options.sort()
            tools_widget.options = options
            label_widget.value = f"{len(options)} Available Tools"

    category_widget.observe(category_selected, "value")

    def tool_selected(change):
        if change["new"]:
            selected = change["owner"].value
            tool_dict = tools_dict[selected]
            with right_widget:
                right_widget.outputs = ()
                display(tool_gui(tool_dict, max_height=max_height))

    tools_widget.observe(tool_selected, "value")

    def search_changed(change):
        if change["new"]:
            keyword = change["owner"].value
            if len(keyword) > 0:
                selected_tools = []
                for tool in all_tools:
                    if keyword.lower() in tool.lower():
                        selected_tools.append(tool)
                if len(selected_tools) > 0:
                    tools_widget.options = selected_tools
                label_widget.value = f"{len(selected_tools)} Available Tools"
        else:
            tools_widget.options = all_tools
            label_widget.value = f"{len(tools_dict)} Available Tools"

    search_widget.observe(search_changed, "value")

    def cleanup():
        full_widget.close()

    full_widget.cleanup = cleanup

    def close_btn_clicked(b):
        full_widget.cleanup()

    close_btn.on_click(close_btn_clicked)

    category_widget.value = list(categories.keys())[0]
    tools_widget.options = all_tools
    left_widget.children = [category_widget, search_widget, label_widget, close_btn]
    center_widget.children = [tools_widget]

    return full_widget

convert_js2py(m)

A widget for converting Earth Engine JavaScript to Python.

Parameters:

Name Type Description Default
m object

geemap.Map

required
Source code in geemap/toolbar.py
def convert_js2py(m):
    """A widget for converting Earth Engine JavaScript to Python.

    Args:
        m (object): geemap.Map
    """

    full_widget = widgets.VBox(layout=widgets.Layout(width="465px", height="350px"))

    text_widget = widgets.Textarea(
        placeholder="Paste your Earth Engine JavaScript into this textbox and click the Convert button below to convert the Javascript to Python",
        layout=widgets.Layout(width="455px", height="310px"),
    )

    buttons = widgets.ToggleButtons(
        value=None,
        options=["Convert", "Clear", "Close"],
        tooltips=["Convert", "Clear", "Close"],
        button_style="primary",
    )
    buttons.style.button_width = "128px"

    def cleanup():
        if m._convert_ctrl is not None and m._convert_ctrl in m.controls:
            m.remove_control(m._convert_ctrl)
        full_widget.close()

    def button_clicked(change):
        if change["new"] == "Convert":
            from .conversion import create_new_cell, js_snippet_to_py

            if len(text_widget.value) > 0:
                out_lines = js_snippet_to_py(
                    text_widget.value,
                    add_new_cell=False,
                    import_ee=False,
                    import_geemap=False,
                    show_map=False,
                    Map=m._var_name,
                )
                if len(out_lines) > 0 and len(out_lines[0].strip()) == 0:
                    out_lines = out_lines[1:]

                prefix = "# The code has been copied to the clipboard. \n# Press Ctrl+V to in a code cell to paste it.\n"
                text_widget.value = "".join([prefix] + out_lines)
                create_code_cell("".join(out_lines))

        elif change["new"] == "Clear":
            text_widget.value = ""
        elif change["new"] == "Close":
            m._convert_ctrl.cleanup()
        buttons.value = None

    buttons.observe(button_clicked, "value")

    full_widget.children = [text_widget, buttons]
    widget_control = ipyleaflet.WidgetControl(widget=full_widget, position="topright")
    widget_control.cleanup = cleanup
    m.add_control(widget_control)
    m._convert_ctrl = widget_control

ee_plot_gui(m, position='topright', **kwargs)

Widget for plotting Earth Engine data.

Parameters:

Name Type Description Default
m object

geemap.Map.

required
position str

Position of the widget. Defaults to "topright".

'topright'
Source code in geemap/toolbar.py
def ee_plot_gui(m, position="topright", **kwargs):
    """Widget for plotting Earth Engine data.

    Args:
        m (object): geemap.Map.
        position (str, optional): Position of the widget. Defaults to "topright".
    """
    close_btn = widgets.Button(
        icon="times",
        tooltip="Close the plot widget",
        button_style="primary",
        layout=widgets.Layout(width="32px"),
    )

    m._plot_checked = True
    dropdown = widgets.Dropdown(options=list(m.ee_raster_layers.keys()))
    dropdown.layout.width = "18ex"
    m._plot_dropdown_widget = dropdown

    widget = widgets.HBox([dropdown, close_btn])

    plot_dropdown_control = ipyleaflet.WidgetControl(widget=widget, position=position)
    m._plot_dropdown_control = plot_dropdown_control
    m.add(plot_dropdown_control)

    old_draw_control = m.get_draw_control()
    m.remove_draw_control()
    m.add_draw_control_lite()
    draw_control = m.get_draw_control()

    if not hasattr(m, "_chart_points"):
        m._chart_points = []
    if not hasattr(m, "_chart_values"):
        m._chart_values = []
    if not hasattr(m, "_chart_labels"):
        m._chart_labels = None

    def get_layer_name_and_ee_object():
        if not m._plot_checked or not len(m.ee_raster_layers) > 0:
            raise AssertionError(
                "Plot widget must be active and one raster layer available."
            )
        plot_layer_name = m._plot_dropdown_widget.value
        ee_object = m.ee_layers.get(plot_layer_name)["ee_object"]
        if isinstance(ee_object, ee.ImageCollection):
            ee_object = ee_object.mosaic()
        return plot_layer_name, ee_object

    def generate_chart(dict_values, chart_point):
        try:
            plot_layer_name, ee_object = get_layer_name_and_ee_object()
            m.default_style = {"cursor": "wait"}
            plot_options = {}
            if hasattr(m, "_plot_options"):
                plot_options = m._plot_options
            if "title" not in plot_options.keys():
                plot_options["title"] = plot_layer_name
            if ("add_marker_cluster" in plot_options.keys()) and plot_options[
                "add_marker_cluster"
            ]:
                if not hasattr(m, "_plot_markers"):
                    m._plot_markers = []
                markers = m._plot_markers
                marker_cluster = m._plot_marker_cluster
                markers.append(ipyleaflet.Marker(location=chart_point))
                marker_cluster.markers = markers
                m._plot_marker_cluster = marker_cluster

            band_names = ee_object.bandNames().getInfo()
            if any(len(name) > 3 for name in band_names):
                band_names = list(range(1, len(band_names) + 1))

            m._chart_labels = band_names
            m._chart_points.append(chart_point)

            band_values = list(dict_values.values())
            m._chart_values.append(band_values)
            m.plot(band_names, band_values, **plot_options)
            if plot_options["title"] == plot_layer_name:
                del plot_options["title"]
            m.default_style = {"cursor": "crosshair"}
        except Exception as e:
            if not hasattr(m, "_plot_widget"):
                m._plot_widget = None
            if m._plot_widget is not None:
                with m._plot_widget:
                    m._plot_widget.outputs = ()
                    print("No data for the clicked location.")
            else:
                print(e)
            m.default_style = {"cursor": "crosshair"}

    def handle_interaction(**kwargs):
        try:
            _, ee_object = get_layer_name_and_ee_object()
        except AssertionError:
            return

        latlon = kwargs.get("coordinates")
        if kwargs.get("type") == "click":
            xy = ee.Geometry.Point(latlon[::-1])
            plot_options = {}
            if hasattr(m, "_plot_options"):
                plot_options = m._plot_options
            sample_scale = m.getScale()
            if "sample_scale" in plot_options.keys() and (
                plot_options["sample_scale"] is not None
            ):
                sample_scale = plot_options["sample_scale"]
            try:
                dict_values_tmp = (
                    ee_object.sample(xy, scale=sample_scale)
                    .first()
                    .toDictionary()
                    .getInfo()
                )
                b_names = ee_object.bandNames().getInfo()
                dict_values = dict(zip(b_names, [dict_values_tmp[b] for b in b_names]))
                generate_chart(dict_values, latlon)
            except Exception as e:
                if hasattr(m, "_plot_widget") and m._plot_widget is not None:
                    m._plot_widget.clear_output()
                    with m._plot_widget:
                        print("No data for the clicked location.")
                else:
                    pass
                m.default_style = {"cursor": "crosshair"}

    m.on_interaction(handle_interaction)

    def handle_draw(_, geometry):
        try:
            _, ee_object = get_layer_name_and_ee_object()
        except AssertionError:
            return

        if m.roi_reducer_scale is None:
            scale = ee_object.select(0).projection().nominalScale()
        else:
            scale = m.roi_reducer_scale
        dict_values_tmp = ee_object.reduceRegion(
            reducer=m.roi_reducer,
            geometry=geometry,
            scale=scale,
            bestEffort=True,
        ).getInfo()
        b_names = ee_object.bandNames().getInfo()
        dict_values = dict(zip(b_names, [dict_values_tmp[b] for b in b_names]))
        chart_point = geometry.centroid(1).coordinates().getInfo()
        generate_chart(dict_values, chart_point)

    draw_control.on_geometry_create(handle_draw)

    def cleanup():
        m._plot_checked = False

        if (
            hasattr(m, "plot_control")
            and (m._plot_control is not None)
            and (m._plot_control in m.controls)
        ):
            m._plot_widget.outputs = ()
            m.remove_control(m._plot_control)

        if (
            m._plot_dropdown_control is not None
            and m._plot_dropdown_control in m.controls
        ):
            m.remove_control(m._plot_dropdown_control)

        widget.close()

        m.on_interaction(handle_interaction, remove=True)
        m._plot_widget = None
        m.default_style = {"cursor": "default"}
        if old_draw_control:
            old_draw_control.open()
            m.substitute(m.get_draw_control(), old_draw_control)
        else:
            m.remove_draw_control()

    m._plot_dropdown_control.cleanup = cleanup

    def cleanup():
        if not hasattr(m, "_plot_dropdown_widget"):
            m._plot_dropdown_widget = None
        if not hasattr(m, "_plot_dropdown_control"):
            m._plot_dropdown_control = None
        plot_dropdown_widget = m._plot_dropdown_widget
        plot_dropdown_control = m._plot_dropdown_control
        if plot_dropdown_control in m.controls:
            m.remove_control(plot_dropdown_control)
        del plot_dropdown_widget
        del plot_dropdown_control

        if not hasattr(m, "_plot_widget"):
            m._plot_widget = None
        if not hasattr(m, "_plot_control"):
            m._plot_control = None

        if m._plot_control in m.controls:
            plot_control = m._plot_control
            plot_widget = m._plot_widget
            m.remove_control(plot_control)
            m._plot_control = None
            m._plot_widget = None
            del plot_control
            del plot_widget
        if (
            hasattr(m, "_plot_marker_cluster")
            and m._plot_marker_cluster is not None
            and m._plot_marker_cluster in m.layers
        ):
            m.remove_layer(m._plot_marker_cluster)
        if hasattr(m, "_chart_points"):
            m._chart_points = []
        if hasattr(m, "_chart_values"):
            m._chart_values = []
        if hasattr(m, "_chart_labels"):
            m._chart_labels = None

    m._plot_dropdown_control.cleanup = cleanup

    def close_click(_):
        m._plot_dropdown_control.cleanup()

    close_btn.on_click(close_click)

inspector_gui(m=None)

Generates a tool GUI template using ipywidgets.

Parameters:

Name Type Description Default
m geemap.Map

The leaflet Map object. Defaults to None.

None

Returns:

Type Description
ipywidgets

The tool GUI widget.

Source code in geemap/toolbar.py
def inspector_gui(m=None):
    """Generates a tool GUI template using ipywidgets.

    Args:
        m (geemap.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",
                    max_width=widget_width,
                )
            )
            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-circle",
        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 cleanup():
        toolbar_button.value = False
        if m is not None:
            if hasattr(m, "inspector_mode"):
                delattr(m, "inspector_mode")
            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()

    def close_btn_click(change):
        if change["new"]:
            m.tool_control.cleanup()

    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":
            m.tool_control.cleanup()

        buttons.value = None

    buttons.observe(button_clicked, "value")

    toolbar_button.value = True

    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["items"],
                    assets,
                    layer_dict["titiler_endpoint"],
                    verbose=False,
                )
                if result is not None:
                    with output:
                        output.outputs = ()
                        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"] == "COG":
                result = cog_pixel_value(lon, lat, layer_dict["url"], verbose=False)
                if result is not None:
                    with output:
                        output.outputs = ()
                        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":
                result = local_tile_pixel_value(
                    lon, lat, layer_dict["tile_client"], verbose=False
                )
                if result is not None:
                    if m.inspector_bands_chk.value:
                        band = m.cog_layer_dict[m.inspector_dropdown.value]["band"]
                        band_names = m.cog_layer_dict[m.inspector_dropdown.value][
                            "band_names"
                        ]
                        if band is not None:
                            sel_bands = [band_names[b - 1] for b in band]
                            result = {k: v for k, v in result.items() if k in sel_bands}
                    with output:
                        output.outputs = ()
                        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)
            m.default_style = {"cursor": "crosshair"}

    if m is not None:
        if not hasattr(m, "marker_cluster"):
            setattr(m, "marker_cluster", marker_cluster)
        m.add_layer(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="topright"
        )
        toolbar_control.cleanup = cleanup

        if toolbar_control not in m.controls:
            m.add_control(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

open_data_widget(m)

A widget for opening local vector/raster data.

Parameters:

Name Type Description Default
m object

geemap.Map

required
Source code in geemap/toolbar.py
def open_data_widget(m):
    """A widget for opening local vector/raster data.

    Args:
        m (object): geemap.Map
    """

    padding = "0px 0px 0px 5px"
    style = {"description_width": "initial"}

    tool_output = widgets.Output()
    tool_output_ctrl = ipyleaflet.WidgetControl(widget=tool_output, position="topright")

    if (
        hasattr(m, "_tool_output_ctrl")
        and m._tool_output_ctrl is not None
        and m._tool_output_ctrl in m.controls
    ):
        m.remove_control(m._tool_output_ctrl)

    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

    style = {"description_width": "initial"}
    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="0px 0px 0px 5px"),
    )

    longitude = widgets.Dropdown(
        options=[],
        value=None,
        description="Longitude:",
        layout=widgets.Layout(width="149px", padding="0px 0px 0px 5px"),
        style={"description_width": "initial"},
    )

    latitude = widgets.Dropdown(
        options=[],
        value=None,
        description="Latitude:",
        layout=widgets.Layout(width="149px", padding="0px 0px 0px 5px"),
        style={"description_width": "initial"},
    )

    label = widgets.Dropdown(
        options=[],
        value=None,
        description="Label:",
        layout=widgets.Layout(width="149px", padding="0px 0px 0px 5px"),
        style={"description_width": "initial"},
    )

    csv_widget = widgets.HBox()

    convert_bool = widgets.Checkbox(
        description="Convert to ee.FeatureCollection?",
        indent=False,
        layout=widgets.Layout(padding="0px 0px 0px 5px"),
    )
    convert_hbox = widgets.HBox([convert_bool])

    ok_cancel = widgets.ToggleButtons(
        value=None,
        options=["Apply", "Reset", "Close"],
        tooltips=["Apply", "Reset", "Close"],
        button_style="primary",
    )
    # ok_cancel.style.button_width = "133px"

    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()

    main_widget = widgets.VBox(
        [
            file_type,
            file_chooser,
            http_widget,
            csv_widget,
            layer_name,
            convert_hbox,
            raster_options,
            ok_cancel,
        ]
    )

    tool_output.outputs = ()
    with tool_output:
        display(main_widget)

    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 = ""

        if change["new"] == "Shapefile":
            file_chooser.filter_pattern = "*.shp"
            raster_options.children = []
            convert_hbox.children = [convert_bool]
            http_widget.children = []
        elif change["new"] == "GeoJSON":
            file_chooser.filter_pattern = "*.geojson"
            raster_options.children = []
            convert_hbox.children = [convert_bool]
            http_widget.children = [filepath]
        elif change["new"] == "Vector":
            file_chooser.filter_pattern = "*.*"
            raster_options.children = []
            convert_hbox.children = [convert_bool]
            http_widget.children = [filepath]
        elif change["new"] == "CSV":
            file_chooser.filter_pattern = ["*.csv", "*.CSV"]
            csv_widget.children = [longitude, latitude, label]
            raster_options.children = []
            convert_hbox.children = [convert_bool]
            http_widget.children = [filepath]
        elif change["new"] == "Raster":
            if not hasattr(m, "_colormaps"):
                from .colormaps import list_colormaps

                m._colormaps = list_colormaps(add_extra=True)
            file_chooser.filter_pattern = ["*.tif", "*.img"]
            palette.options = m._colormaps
            palette.value = None
            raster_options.children = [
                widgets.HBox([bands, vmin, vmax]),
                widgets.HBox([nodata, palette]),
            ]
            convert_hbox.children = []
            http_widget.children = [filepath]

    def cleanup():
        if (
            hasattr(m, "_tool_output_ctrl")
            and 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

    tool_output_ctrl.cleanup = cleanup

    def ok_cancel_clicked(change):
        if change["new"] == "Apply":
            m.default_style = {"cursor": "wait"}
            file_path = filepath.value

            if file_path is not None:
                ext = os.path.splitext(file_path)[1]
                with tool_output:
                    if ext.lower() == ".shp":
                        if convert_bool.value:
                            ee_object = shp_to_ee(file_path)
                            m.addLayer(ee_object, {}, layer_name.value)
                        else:
                            m.add_shapefile(
                                file_path, style={}, layer_name=layer_name.value
                            )
                    elif ext.lower() == ".geojson":
                        if convert_bool.value:
                            ee_object = geojson_to_ee(file_path)
                            m.addLayer(ee_object, {}, layer_name.value)
                        else:
                            m.add_geojson(
                                file_path, style={}, layer_name=layer_name.value
                            )

                    elif ext.lower() == ".csv":
                        if convert_bool.value:
                            ee_object = csv_to_ee(
                                file_path, latitude.value, longitude.value
                            )
                            m.addLayer(ee_object, {}, layer_name.value)
                        else:
                            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 = bands.value.split(",")
                            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:
                        m.add_vector(file_path, style={}, layer_name=layer_name.value)
            else:
                print("Please select a file to open.")

            m.default_style = {"cursor": "default"}

        elif change["new"] == "Reset":
            file_chooser.reset()
            tool_output.outputs = ()
            with tool_output:
                display(main_widget)
        elif change["new"] == "Close":
            tool_output_ctrl.cleanup()
        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_control(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

geemap.Map.

required
Source code in geemap/toolbar.py
def plotly_basemap_gui(canvas, map_min_width="78%", map_max_width="98%"):
    """Widget for changing basemaps.

    Args:
        m (object): geemap.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 geemap/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 geemap/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://geemap.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

time_slider(m=None)

Creates a time slider for visualizing any ee.ImageCollection.

Parameters:

Name Type Description Default
m geemap.Map

A geemap Map instance. Defaults to None.

None

Returns:

Type Description
ipywidgets

The interactive GUI.

Source code in geemap/toolbar.py
def time_slider(m=None):
    """Creates a time slider for visualizing any ee.ImageCollection.

    Args:
        m (geemap.Map, optional): A geemap Map instance. Defaults to None.

    Returns:
        ipywidgets: The interactive GUI.
    """
    import matplotlib as mpl
    import matplotlib.pyplot as plt

    widget_width = "350px"
    padding = "0px 0px 0px 5px"  # upper, right, bottom, left
    style = {"description_width": "initial"}

    toolbar_button = widgets.ToggleButton(
        value=False,
        tooltip="Toolbar",
        icon="fast-forward",
        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"),
    )

    col_options_dict = {
        "Landsat TM-ETM-OLI Surface Reflectance": {
            "min": 0,
            "max": 4000,
            "bands": ["NIR", "Red", "Green"],
            "start_year": 1984,
            "end_year": 2021,
            "bandnames": ["Blue", "Green", "Red", "NIR", "SWIR1", "SWIR2", "pixel_qa"],
        },
        "MOD13A2.006 Terra Vegetation Indices": {
            "min": 0,
            "max": 9000,
            "start_year": 2000,
            "end_year": 2021,
            "palette": [
                "FFFFFF",
                "CE7E45",
                "DF923D",
                "F1B555",
                "FCD163",
                "99B718",
                "74A901",
                "66A000",
                "529400",
                "3E8601",
                "207401",
                "056201",
                "004C00",
                "023B01",
                "012E01",
                "011D01",
                "011301",
            ],
        },
        "Sentinel-2 Surface Relectance": {
            "min": 0,
            "max": 4000,
            "bands": ["NIR", "Red", "Green"],
            "start_year": 2015,
            "end_year": 2021,
            "bandnames": [
                "Blue",
                "Green",
                "Red",
                "Red Edge 1",
                "Red Edge 2",
                "Red Edge 3",
                "NIR",
                "Red Edge 4",
                "SWIR1",
                "SWIR2",
                "QA60",
            ],
        },
        "USDA NAIP Imagery": {
            "min": 0,
            "max": 255,
            "bands": ["R", "G", "B"],
            "start_year": 2003,
            "end_year": 2021,
            "bandnames": ["R", "G", "B", "N"],
        },
    }

    col_options = list(col_options_dict.keys())

    if m is not None:
        col_options += m.ee_raster_layers.keys()

    collection = widgets.Dropdown(
        options=col_options,
        value=col_options[0],
        description="Time series:",
        layout=widgets.Layout(width=widget_width, padding=padding),
        style=style,
    )

    region = widgets.Dropdown(
        options=["User-drawn ROI"] + list(m.ee_vector_layers.keys()),
        value="User-drawn ROI",
        description="Region:",
        layout=widgets.Layout(width=widget_width, padding=padding),
        style=style,
    )

    dropdown_width = "97px"
    landsat_bands = ["Blue", "Green", "Red", "NIR", "SWIR1", "SWIR2", "pixel_qa"]
    band1_dropdown = widgets.Dropdown(
        options=landsat_bands,
        value="NIR",
        layout=widgets.Layout(width=dropdown_width),
    )
    band2_dropdown = widgets.Dropdown(
        options=landsat_bands,
        value="Red",
        layout=widgets.Layout(width=dropdown_width),
    )
    band3_dropdown = widgets.Dropdown(
        options=landsat_bands,
        value="Green",
        layout=widgets.Layout(width=dropdown_width),
    )

    bands_label = widgets.Label("Bands:", layout=widgets.Layout(padding=padding))
    bands_hbox = widgets.HBox(
        [bands_label, band1_dropdown, band2_dropdown, band3_dropdown]
    )

    vis = widgets.Text(
        value="",
        description="Vis min value:",
        placeholder="{'min': 0, 'max': 1, 'palette': ['red', 'blue']}",
        style=style,
        layout=widgets.Layout(width=widget_width, padding=padding),
    )

    vis_min = widgets.Text(
        value="0",
        description="Vis min value:",
        style=style,
        layout=widgets.Layout(width="172px", padding=padding),
    )

    vis_max = widgets.Text(
        value="4000",
        description="Vis max value:",
        style=style,
        layout=widgets.Layout(width="172px", padding=padding),
    )

    opacity = widgets.FloatSlider(
        value=1,
        min=0,
        max=1,
        step=0.01,
        description="Opacity:",
        continuous_update=True,
        readout=False,
        readout_format=".2f",
        layout=widgets.Layout(width="130px", padding=padding),
        style={"description_width": "50px"},
    )

    opacity_label = widgets.Label(
        "1", layout=widgets.Layout(width="40px", padding=padding)
    )
    jslink_slider_label(opacity, opacity_label)

    gamma = widgets.FloatSlider(
        value=1,
        min=0.1,
        max=10,
        step=0.01,
        description="Gamma:",
        continuous_update=True,
        readout=False,
        readout_format=".2f",
        layout=widgets.Layout(width="123px", padding=padding),
        style={"description_width": "50px"},
    )

    gamma_label = widgets.Label(
        "1", layout=widgets.Layout(width="40px", padding=padding)
    )
    jslink_slider_label(gamma, gamma_label)

    color_picker = widgets.ColorPicker(
        concise=False,
        value="#000000",
        layout=widgets.Layout(width="97px"),
        style={"description_width": "initial"},
    )

    add_color = widgets.Button(
        icon="plus",
        tooltip="Add a hex color string to the palette",
        layout=widgets.Layout(width="32px"),
    )

    del_color = widgets.Button(
        icon="minus",
        tooltip="Remove a hex color string from the palette",
        layout=widgets.Layout(width="32px"),
    )

    reset_color = widgets.Button(
        icon="eraser",
        tooltip="Remove all color strings from the palette",
        layout=widgets.Layout(width="34px"),
    )

    classes = widgets.Dropdown(
        options=["Any"] + [str(i) for i in range(3, 13)],
        description="Classes:",
        layout=widgets.Layout(width="150px", padding=padding),
        style={"description_width": "initial"},
    )

    colormap = widgets.Dropdown(
        options=plt.colormaps(),
        value=None,
        description="Colormap:",
        layout=widgets.Layout(width="195px", padding=padding),
        style={"description_width": "initial"},
    )

    def classes_changed(change):
        if change["new"]:
            selected = change["owner"].value
            if colormap.value is not None:
                n_class = None
                if selected != "Any":
                    n_class = int(classes.value)

                try:
                    colors = plt.get_cmap(colormap.value, n_class)
                except:
                    colors = plt.cm.get_cmap(colormap.value, n_class)
                cmap_colors = [
                    mpl.colors.rgb2hex(colors(i))[1:] for i in range(colors.N)
                ]

                _, ax = plt.subplots(figsize=(6, 0.4))
                cmap = mpl.colors.LinearSegmentedColormap.from_list(
                    "custom", to_hex_colors(cmap_colors), N=256
                )

                vmin = 0
                vmax = 1
                try:
                    if vis_min.value != "":
                        vmin = float(vis_min.value)
                    if vis_max.value != "":
                        vmax = float(vis_max.value)
                except Exception as _:
                    pass

                norm = mpl.colors.Normalize(vmin=vmin, vmax=vmax)
                mpl.colorbar.ColorbarBase(
                    ax, norm=norm, cmap=cmap, orientation="horizontal"
                )

                palette.value = ", ".join([color for color in cmap_colors])

                if m._colorbar_widget is None:
                    m._colorbar_widget = widgets.Output(
                        layout=widgets.Layout(height="60px")
                    )

                if (not hasattr(m, "_colorbar_ctrl")) or (m._colorbar_ctrl is None):
                    m._colorbar_ctrl = ipyleaflet.WidgetControl(
                        widget=m._colorbar_widget, position="bottomright"
                    )
                    m.add_control(m._colorbar_ctrl)

                colorbar_output = m._colorbar_widget
                with colorbar_output:
                    colorbar_output.outputs = ()
                    plt.show()

    classes.observe(classes_changed, "value")

    palette = widgets.Text(
        value="",
        placeholder="",
        description="Palette:",
        tooltip="Enter a list of hex color code (RRGGBB)",
        layout=widgets.Layout(width="137px", padding=padding),
        style={"description_width": "initial"},
    )

    def add_color_clicked(b):
        if color_picker.value is not None:
            if len(palette.value) == 0:
                palette.value = color_picker.value[1:]
            else:
                palette.value += ", " + color_picker.value[1:]

    def del_color_clicked(b):
        if "," in palette.value:
            items = [item.strip() for item in palette.value.split(",")]
            palette.value = ", ".join(items[:-1])
        else:
            palette.value = ""

    def reset_color_clicked(b):
        palette.value = ""

    add_color.on_click(add_color_clicked)
    del_color.on_click(del_color_clicked)
    reset_color.on_click(reset_color_clicked)

    def colormap_changed(change):
        if change["new"]:
            n_class = None
            if classes.value != "Any":
                n_class = int(classes.value)

            try:
                colors = plt.get_cmap(colormap.value, n_class)
            except:
                colors = plt.cm.get_cmap(colormap.value, n_class)
            cmap_colors = [mpl.colors.rgb2hex(colors(i))[1:] for i in range(colors.N)]

            _, ax = plt.subplots(figsize=(6, 0.4))
            cmap = mpl.colors.LinearSegmentedColormap.from_list(
                "custom", to_hex_colors(cmap_colors), N=256
            )

            vmin = 0
            vmax = 1
            try:
                if vis_min.value != "":
                    vmin = float(vis_min.value)
                if vis_max.value != "":
                    vmax = float(vis_max.value)
            except Exception as _:
                pass

            norm = mpl.colors.Normalize(vmin=vmin, vmax=vmax)
            mpl.colorbar.ColorbarBase(
                ax, norm=norm, cmap=cmap, orientation="horizontal"
            )

            palette.value = ", ".join(cmap_colors)

            if m._colorbar_widget is None:
                m._colorbar_widget = widgets.Output(
                    layout=widgets.Layout(height="60px")
                )

            if hasattr(m, "_colorbar_ctrl") or (m._colorbar_ctrl is None):
                m._colorbar_ctrl = ipyleaflet.WidgetControl(
                    widget=m._colorbar_widget, position="bottomright"
                )
                m.add_control(m._colorbar_ctrl)

            colorbar_output = m._colorbar_widget
            with colorbar_output:
                colorbar_output.outputs = ()
                plt.show()

    colormap.observe(colormap_changed, "value")

    palette_vbox = widgets.VBox()

    labels = widgets.Text(
        value=", ".join([str(i) for i in range(1984, 2021)]),
        description="Labels:",
        style=style,
        layout=widgets.Layout(width="150px", padding=padding),
    )

    speed = widgets.FloatSlider(
        description="Speed (sec):",
        tooltip="Time interval in seconds",
        value=1,
        min=0.1,
        max=10,
        readout=False,
        style=style,
        layout=widgets.Layout(width="160px", padding=padding),
    )

    speed_label = widgets.Label(
        "1",
        layout=widgets.Layout(width="25px", padding=padding),
    )
    jslink_slider_label(speed, speed_label)

    prebuilt_options = widgets.VBox()

    cloud = widgets.Checkbox(
        value=True,
        description="Apply fmask (remove clouds, shadows, snow)",
        tooltip="Apply fmask (remove clouds, shadows, snow)",
        style=style,
    )

    current_year = get_current_year()

    start_year = widgets.IntSlider(
        description="Start Year:",
        value=1984,
        min=1984,
        max=current_year,
        readout=False,
        style=style,
        layout=widgets.Layout(width="138px", padding=padding),
    )

    def year_change(change):
        if change["new"]:
            if collection.value != "MOD13A2.006 Terra Vegetation Indices":
                labels.value = ", ".join(
                    str(i)
                    for i in range(int(start_year.value), int(end_year.value) + 1)
                )
            else:
                modis_labels = []
                for i in range(int(start_year.value), int(end_year.value) + 1):
                    for j in range(1, 13):
                        modis_labels.append(str(i) + "-" + str(j).zfill(2))
                labels.value = ", ".join(modis_labels)

    start_year.observe(year_change, "value")

    start_year_label = widgets.Label("1984")
    jslink_slider_label(start_year, start_year_label)

    end_year = widgets.IntSlider(
        description="End Year:",
        value=2020,
        min=1984,
        max=current_year,
        readout=False,
        style=style,
        layout=widgets.Layout(width="138px", padding=padding),
    )

    end_year.observe(year_change, "value")

    end_year_label = widgets.Label(str(current_year))
    jslink_slider_label(end_year, end_year_label)

    start_month = widgets.IntSlider(
        description="Start Month:",
        value=1,
        min=1,
        max=12,
        readout=False,
        style=style,
        layout=widgets.Layout(width="145px", padding=padding),
    )

    start_month_label = widgets.Label(
        "1",
        layout=widgets.Layout(width="20px", padding=padding),
    )
    jslink_slider_label(start_month, start_month_label)

    end_month = widgets.IntSlider(
        description="End Month:",
        value=12,
        min=1,
        max=12,
        readout=False,
        style=style,
        layout=widgets.Layout(width="155px", padding=padding),
    )

    end_month_label = widgets.Label("12")
    jslink_slider_label(end_month, end_month_label)

    prebuilt_options.children = [
        widgets.HBox([start_year, start_year_label, end_year, end_year_label]),
        widgets.HBox([start_month, start_month_label, end_month, end_month_label]),
        cloud,
    ]

    button_width = "113px"
    apply_btn = widgets.Button(
        description="Apply",
        button_style="primary",
        tooltip="Apply the settings to activate the time slider",
        style=style,
        layout=widgets.Layout(padding="0px", width=button_width),
    )

    def submit_clicked(b):
        output.outputs = ()
        with output:
            if start_year.value > end_year.value:
                print("The end year must be great than the start year.")
                return
            if start_month.value > end_month.value:
                print("The end month must be great than the start month.")
                return

        if m is not None:
            roi = None
            if region.value == "User-drawn ROI" and (m.user_roi is not None):
                roi = m.user_roi
            elif region.value == "User-drawn ROI" and (m.user_roi is None):
                with output:
                    print("Use the Drawing tool to create an ROI.")
                    return
            elif region.value in m.ee_layers:
                roi = m.ee_layers[region.value]["ee_object"]

            with output:
                print("Computing... Please wait...")

            layer_labels = None
            vis_params = {}

            try:
                if vis_min.value != "":
                    vis_params["min"] = float(vis_min.value)

                if vis_max.value != "":
                    vis_params["max"] = float(vis_max.value)

                vis_params["opacity"] = float(opacity.value)

                if len(bands_hbox.children) > 0 and (
                    band1_dropdown.value
                    and band2_dropdown.value
                    and band3_dropdown.value
                ):
                    vis_params["bands"] = [
                        band1_dropdown.value,
                        band2_dropdown.value,
                        band3_dropdown.value,
                    ]
                    vis_params["gamma"] = float(gamma.value)

                if len(palette_vbox.children) > 0:
                    if "," in palette.value:
                        vis_params["palette"] = [
                            i.strip() for i in palette.value.split(",")
                        ]
                    elif len(palette.value) > 0:
                        vis_params["palette"] = palette.value.strip()

            except Exception as _:
                with output:
                    print("The vis params are invalid.")
                    return

            if labels.value != "" and "," in labels.value:
                try:
                    layer_labels = [i.strip() for i in labels.value.split(",")]
                except Exception as e:
                    raise ValueError(e)

            if collection.value in m.ee_raster_layers:
                layer = m.ee_layers[collection.value]
                ee_object = layer["ee_object"]
            elif collection.value in col_options_dict:
                start_date = str(start_month.value).zfill(2) + "-01"
                end_date = str(end_month.value).zfill(2) + "-30"

                if collection.value == "Landsat TM-ETM-OLI Surface Reflectance":
                    ee_object = landsat_timeseries(
                        roi,
                        int(start_year.value),
                        int(end_year.value),
                        start_date,
                        end_date,
                        cloud.value,
                    )
                elif collection.value == "MOD13A2.006 Terra Vegetation Indices":
                    ee_object = modis_timeseries(
                        roi=roi,
                        start_year=int(start_year.value),
                        end_year=int(end_year.value),
                        start_date=start_date,
                        end_date=end_date,
                    )

                elif collection.value == "Sentinel-2 Surface Relectance":
                    ee_object = sentinel2_timeseries(
                        roi,
                        int(start_year.value),
                        int(end_year.value),
                        start_date,
                        end_date,
                        cloud.value,
                    )
                elif collection.value == "USDA NAIP Imagery":
                    if int(start_year.value) < 2009 and (
                        band1_dropdown.value == "N"
                        or band2_dropdown.value == "N"
                        or band3_dropdown.value == "N"
                    ):
                        with output:
                            output.outputs = ()
                            print("4-band NAIP imagery not available before 2009.")
                            return

                    ee_object = naip_timeseries(roi, start_year.value, end_year.value)

            m.add_time_slider(
                ee_object,
                region=roi,
                vis_params=vis_params,
                labels=layer_labels,
                time_interval=speed.value,
            )

            output.outputs = ()

            if hasattr(m, "_colorbar_ctrl") and (m._colorbar_ctrl is not None):
                m.remove_control(m._colorbar_ctrl)
                m._colorbar_ctrl = None

    apply_btn.on_click(submit_clicked)

    reset_btn = widgets.Button(
        description="Reset",
        button_style="primary",
        style=style,
        layout=widgets.Layout(padding="0px", width=button_width),
    )

    def reset_btn_click(change):
        output.outputs = ()
        collection.value = col_options[0]
        region.value = "User-drawn ROI"
        vis.value = ""
        labels.value = "1, 2, 3"
        speed.value = 1

        if hasattr(m, "_colorbar_ctrl") and (m._colorbar_ctrl is not None):
            m.remove_control(m._colorbar_ctrl)
            m._colorbar_ctrl = None

    reset_btn.on_click(reset_btn_click)

    close_btn = widgets.Button(
        description="Close",
        button_style="primary",
        style=style,
        layout=widgets.Layout(padding="0px", width=button_width),
    )

    def cleanup():
        toolbar_button.value = False
        if m is not None:
            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()

        if hasattr(m, "_colorbar_ctrl") and (m._colorbar_ctrl is not None):
            m.remove_control(m._colorbar_ctrl)
            m._colorbar_ctrl = None

    def close_btn_click(change):
        if change["new"]:
            m.tool_control.cleanup()

    close_button.observe(close_btn_click, "value")

    def collection_changed(change):
        if change["new"]:
            selected = change["owner"].value
            if selected in m.ee_layers:
                prebuilt_options.children = []
                labels.value = ""
                region.value = None

                ee_object = m.ee_layers[selected]["ee_object"]
                vis_params = m.ee_layers[selected]["vis_params"]
                if isinstance(ee_object, ee.Image):
                    palette_vbox.children = [
                        widgets.HBox([classes, colormap]),
                        widgets.HBox(
                            [palette, color_picker, add_color, del_color, reset_color]
                        ),
                    ]
                    bands_hbox.children = []

                elif isinstance(ee_object, ee.ImageCollection):
                    first = ee.Image(ee_object.first())
                    band_names = first.bandNames().getInfo()
                    band_count = len(band_names)

                    if band_count > 2:
                        band1_dropdown.options = band_names
                        band2_dropdown.options = band_names
                        band3_dropdown.options = band_names

                        band1_dropdown.value = band_names[2]
                        band2_dropdown.value = band_names[1]
                        band3_dropdown.value = band_names[0]

                        palette_vbox.children = []
                        bands_hbox.children = [
                            bands_label,
                            band1_dropdown,
                            band2_dropdown,
                            band3_dropdown,
                        ]

                    else:
                        palette_vbox.children = [
                            widgets.HBox([classes, colormap]),
                            widgets.HBox(
                                [
                                    palette,
                                    color_picker,
                                    add_color,
                                    del_color,
                                    reset_color,
                                ]
                            ),
                        ]
                        bands_hbox.children = []

                if "min" in vis_params:
                    vis_min.value = str(vis_params["min"])
                if "max" in vis_params:
                    vis_max.value = str(vis_params["max"])
                if "opacity" in vis_params:
                    opacity.value = str(vis_params["opacity"])
                if "gamma" in vis_params:
                    if isinstance(vis_params["gamma"], list):
                        gamma.value = str(vis_params["gamma"][0])
                    else:
                        gamma.value = str(vis_params["gamma"])
                if "palette" in vis_params:
                    palette.value = ", ".join(vis_params["palette"])

            else:
                prebuilt_options.children = [
                    widgets.HBox(
                        [start_year, start_year_label, end_year, end_year_label]
                    ),
                    widgets.HBox(
                        [start_month, start_month_label, end_month, end_month_label]
                    ),
                    cloud,
                ]

                if selected == "MOD13A2.006 Terra Vegetation Indices":
                    palette_vbox.children = [
                        widgets.HBox([classes, colormap]),
                        widgets.HBox(
                            [
                                palette,
                                color_picker,
                                add_color,
                                del_color,
                                reset_color,
                            ]
                        ),
                    ]
                    bands_hbox.children = []

                    palette.value = ", ".join(col_options_dict[selected]["palette"])
                    modis_labels = []
                    for i in range(int(start_year.value), int(end_year.value) + 1):
                        for j in range(1, 13):
                            modis_labels.append(str(i) + "-" + str(j).zfill(2))
                    labels.value = ", ".join(modis_labels)

                else:
                    bands_hbox.children = [
                        bands_label,
                        band1_dropdown,
                        band2_dropdown,
                        band3_dropdown,
                    ]

                    bandnames = col_options_dict[selected]["bandnames"]
                    band1_dropdown.options = bandnames
                    band2_dropdown.options = bandnames
                    band3_dropdown.options = bandnames

                if (
                    selected == "Landsat TM-ETM-OLI Surface Reflectance"
                    or selected == "Sentinel-2 Surface Relectance"
                ):
                    band1_dropdown.value = bandnames[2]
                    band2_dropdown.value = bandnames[1]
                    band3_dropdown.value = bandnames[0]
                    palette_vbox.children = []
                elif selected == "USDA NAIP Imagery":
                    band1_dropdown.value = bandnames[0]
                    band2_dropdown.value = bandnames[1]
                    band3_dropdown.value = bandnames[2]
                    palette_vbox.children = []

                labels.value = ", ".join(
                    str(i)
                    for i in range(int(start_year.value), int(end_year.value) + 1)
                )

                start_year.min = col_options_dict[selected]["start_year"]
                start_year.max = col_options_dict[selected]["end_year"]
                start_year.value = start_year.min
                end_year.min = col_options_dict[selected]["start_year"]
                end_year.max = col_options_dict[selected]["end_year"]
                end_year.value = end_year.max
                vis_min.value = str(col_options_dict[selected]["min"])
                vis_max.value = str(col_options_dict[selected]["max"])

                if selected == "MOD13A2.006 Terra Vegetation Indices":
                    start_year.value = "2001"
                    end_year.value = "2020"
                elif selected == "USDA NAIP Imagery":
                    start_year.value = "2009"
                    end_year.value = "2019"

    collection.observe(collection_changed, "value")

    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 = [
        collection,
        region,
        bands_hbox,
        widgets.HBox([vis_min, vis_max]),
        widgets.HBox([opacity, opacity_label, gamma, gamma_label]),
        palette_vbox,
        widgets.HBox([labels, speed, speed_label]),
        prebuilt_options,
        widgets.HBox([apply_btn, reset_btn, close_btn]),
        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")

    toolbar_button.value = True
    if m is not None:
        toolbar_control = ipyleaflet.WidgetControl(
            widget=toolbar_widget, position="topright"
        )
        toolbar_control.cleanup = cleanup

        if toolbar_control not in m.controls:
            m.add_control(toolbar_control)
            m.tool_control = toolbar_control
    else:
        return toolbar_widget

timelapse_gui(m=None, basemap='HYBRID')

Creates timelapse animations.

Parameters:

Name Type Description Default
m geemap.Map

A geemap Map instance. Defaults to None.

None
basemap str

The basemap to use. Defaults to "HYBRID".

'HYBRID'

Returns:

Type Description
ipywidgets

The interactive GUI.

Source code in geemap/toolbar.py
def timelapse_gui(m=None, basemap="HYBRID"):
    """Creates timelapse animations.

    Args:
        m (geemap.Map, optional): A geemap Map instance. Defaults to None.
        basemap (str, optional): The basemap to use. Defaults to "HYBRID".

    Returns:
        ipywidgets: The interactive GUI.
    """
    if m is not None and (basemap is not None):
        m.add_basemap(basemap)

    widget_width = "350px"
    padding = "0px 0px 0px 5px"  # upper, right, bottom, left
    style = {"description_width": "initial"}

    current_year = get_current_year()

    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"),
    )

    collection = widgets.Dropdown(
        options=[
            "Landsat TM-ETM-OLI Surface Reflectance",
        ],
        value="Landsat TM-ETM-OLI Surface Reflectance",
        description="Collection:",
        layout=widgets.Layout(width=widget_width, padding=padding),
        style=style,
    )

    title = widgets.Text(
        value="Timelapse",
        description="Title:",
        style=style,
        layout=widgets.Layout(width="181px", padding=padding),
    )

    bands = widgets.Dropdown(
        description="RGB:",
        options=[
            "Red/Green/Blue",
            "NIR/Red/Green",
            "SWIR2/SWIR1/NIR",
            "NIR/SWIR1/Red",
            "SWIR2/NIR/Red",
            "SWIR2/SWIR1/Red",
            "SWIR1/NIR/Blue",
            "NIR/SWIR1/Blue",
            "SWIR2/NIR/Green",
            "SWIR1/NIR/Red",
        ],
        value="SWIR1/NIR/Red",
        style=style,
        layout=widgets.Layout(width="165px", padding=padding),
    )

    speed = widgets.IntSlider(
        description="Frames/sec:",
        tooltip="Frames per second",
        value=10,
        min=1,
        max=30,
        readout=False,
        style=style,
        layout=widgets.Layout(width="142px", padding=padding),
    )

    speed_label = widgets.Label(
        "10",
        layout=widgets.Layout(width="20px", padding=padding),
    )
    jslink_slider_label(speed, speed_label)

    cloud = widgets.Checkbox(
        value=True,
        description="Apply fmask (remove clouds, shadows, snow)",
        tooltip="Apply fmask (remove clouds, shadows, snow)",
        style=style,
    )

    start_year = widgets.IntSlider(
        description="Start Year:",
        value=1984,
        min=1984,
        max=current_year,
        readout=False,
        style=style,
        layout=widgets.Layout(width="138px", padding=padding),
    )

    start_year_label = widgets.Label("1984")
    jslink_slider_label(start_year, start_year_label)

    end_year = widgets.IntSlider(
        description="End Year:",
        value=current_year,
        min=1984,
        max=current_year,
        readout=False,
        style=style,
        layout=widgets.Layout(width="138px", padding=padding),
    )
    end_year_label = widgets.Label(str(current_year))
    jslink_slider_label(end_year, end_year_label)

    start_month = widgets.IntSlider(
        description="Start Month:",
        value=5,
        min=1,
        max=12,
        readout=False,
        style=style,
        layout=widgets.Layout(width="145px", padding=padding),
    )

    start_month_label = widgets.Label(
        "5",
        layout=widgets.Layout(width="20px", padding=padding),
    )
    jslink_slider_label(start_month, start_month_label)

    end_month = widgets.IntSlider(
        description="End Month:",
        value=10,
        min=1,
        max=12,
        readout=False,
        style=style,
        layout=widgets.Layout(width="155px", padding=padding),
    )

    end_month_label = widgets.Label("10")
    jslink_slider_label(end_month, end_month_label)

    font_size = widgets.IntSlider(
        description="Font size:",
        value=30,
        min=10,
        max=50,
        readout=False,
        style=style,
        layout=widgets.Layout(width="152px", padding=padding),
    )

    font_size_label = widgets.Label("30")
    jslink_slider_label(font_size, font_size_label)

    font_color = widgets.ColorPicker(
        concise=False,
        description="Font color:",
        value="white",
        style=style,
        layout=widgets.Layout(width="170px", padding=padding),
    )

    progress_bar_color = widgets.ColorPicker(
        concise=False,
        description="Progress bar:",
        value="blue",
        style=style,
        layout=widgets.Layout(width="180px", padding=padding),
    )

    # Normalized Satellite Indices: https://www.usna.edu/Users/oceano/pguth/md_help/html/norm_sat.htm

    nd_options = [
        "Vegetation Index (NDVI)",
        "Water Index (NDWI)",
        "Modified Water Index (MNDWI)",
        "Snow Index (NDSI)",
        "Soil Index (NDSI)",
        "Burn Ratio (NBR)",
        "Customized",
    ]
    nd_indices = widgets.Dropdown(
        options=nd_options,
        value=None,
        description="Normalized Difference Index:",
        style=style,
        layout=widgets.Layout(width="347px", padding=padding),
    )

    first_band = widgets.Dropdown(
        description="1st band:",
        options=["Blue", "Green", "Red", "NIR", "SWIR1", "SWIR2"],
        value=None,
        style=style,
        layout=widgets.Layout(width="171px", padding=padding),
    )

    second_band = widgets.Dropdown(
        description="2nd band:",
        options=["Blue", "Green", "Red", "NIR", "SWIR1", "SWIR2"],
        value=None,
        style=style,
        layout=widgets.Layout(width="172px", padding=padding),
    )

    nd_threshold = widgets.FloatSlider(
        value=0,
        min=-1,
        max=1,
        step=0.01,
        description="Threshold:",
        orientation="horizontal",
        readout=False,
        style=style,
        layout=widgets.Layout(width="159px", padding=padding),
    )

    nd_threshold_label = widgets.Label(
        "0",
        layout=widgets.Layout(width="35px", padding=padding),
    )
    jslink_slider_label(nd_threshold, nd_threshold_label)

    nd_color = widgets.ColorPicker(
        concise=False,
        description="Color:",
        value="blue",
        style=style,
        layout=widgets.Layout(width="145px", padding=padding),
    )

    def nd_index_change(change):
        if nd_indices.value == "Vegetation Index (NDVI)":
            first_band.value = "NIR"
            second_band.value = "Red"
        elif nd_indices.value == "Water Index (NDWI)":
            first_band.value = "Green"
            second_band.value = "NIR"
        elif nd_indices.value == "Modified Water Index (MNDWI)":
            first_band.value = "Green"
            second_band.value = "SWIR1"
        elif nd_indices.value == "Snow Index (NDSI)":
            first_band.value = "Green"
            second_band.value = "SWIR1"
        elif nd_indices.value == "Soil Index (NDSI)":
            first_band.value = "SWIR1"
            second_band.value = "NIR"
        elif nd_indices.value == "Burn Ratio (NBR)":
            first_band.value = "NIR"
            second_band.value = "SWIR2"
        elif nd_indices.value == "Customized":
            first_band.value = None
            second_band.value = None

    nd_indices.observe(nd_index_change, names="value")

    button_width = "113px"
    create_gif = widgets.Button(
        description="Create timelapse",
        button_style="primary",
        tooltip="Click to create timelapse",
        style=style,
        layout=widgets.Layout(padding="0px", width=button_width),
    )

    def submit_clicked(b):
        if start_year.value > end_year.value:
            print("The end year must be great than the start year.")
            return
        if start_month.value > end_month.value:
            print("The end month must be great than the start month.")
            return
        if start_year.value == end_year.value:
            add_progress_bar = False
        else:
            add_progress_bar = True

        start_date = str(start_month.value).zfill(2) + "-01"
        end_date = str(end_month.value).zfill(2) + "-30"

        with output:
            output.clear_output()
            message = "Computing... Please wait..."
            if os.environ.get("EE_SOLARA", None) is None:
                output.append_stdout(message)
            else:
                print(message)

        nd_bands = None
        if (first_band.value is not None) and (second_band.value is not None):
            nd_bands = [first_band.value, second_band.value]

        temp_output = widgets.Output()

        if m is not None:
            m.default_style = {"cursor": "wait"}
            out_dir = get_temp_dir()
            out_gif = os.path.join(out_dir, "timelapse_" + random_string(3) + ".gif")

            with temp_output:
                temp_output.outputs = ()

                if m.find_layer("Timelapse") is not None:
                    m.remove(m.find_layer("Timelapse"))
                if m.find_layer("Timelapse ND") is not None:
                    m.remove(m.find_layer("Timelapse ND"))

                m.add_landsat_ts_gif(
                    roi=m.user_roi,
                    label=title.value,
                    start_year=start_year.value,
                    end_year=end_year.value,
                    start_date=start_date,
                    end_date=end_date,
                    bands=bands.value.split("/"),
                    font_color=font_color.value,
                    frames_per_second=speed.value,
                    font_size=font_size.value,
                    add_progress_bar=add_progress_bar,
                    progress_bar_color=progress_bar_color.value,
                    out_gif=out_gif,
                    apply_fmask=cloud.value,
                    nd_bands=nd_bands,
                    nd_threshold=nd_threshold.value,
                    nd_palette=["black", nd_color.value],
                )
                if m.user_roi is not None:
                    m.centerObject(m.user_roi)

            with output:
                output.clear_output()
                if os.path.exists(out_gif):
                    link = create_download_link(
                        out_gif,
                        title="Click here to download: ",
                    )
                    display(link)
                    if nd_bands is not None:
                        link_nd = create_download_link(
                            out_gif.replace(".gif", "_nd.gif"),
                            title="Click here to download: ",
                        )
                        display(link_nd)

            m.default_style = {"cursor": "default"}

    create_gif.on_click(submit_clicked)

    reset_btn = widgets.Button(
        description="Reset",
        button_style="primary",
        style=style,
        layout=widgets.Layout(padding="0px", width=button_width),
    )

    def reset_btn_click(change):
        output.outputs = ()

    reset_btn.on_click(reset_btn_click)

    close_btn = widgets.Button(
        description="Close",
        button_style="primary",
        style=style,
        layout=widgets.Layout(padding="0px", width=button_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 = [
        collection,
        widgets.HBox([title, bands]),
        widgets.HBox([speed, speed_label, progress_bar_color]),
        widgets.HBox([start_year, start_year_label, end_year, end_year_label]),
        widgets.HBox([start_month, start_month_label, end_month, end_month_label]),
        widgets.HBox([font_size, font_size_label, font_color]),
        cloud,
        nd_indices,
        widgets.HBox([first_band, second_band]),
        widgets.HBox([nd_threshold, nd_threshold_label, nd_color]),
        widgets.HBox([create_gif, reset_btn, close_btn]),
        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 cleanup():
        if m is not None:
            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()

    def close_btn_click(change):
        if change["new"]:
            m.tool_control.cleanup()

    close_btn.on_click(lambda _: m.tool_control.cleanup())
    close_button.observe(close_btn_click, "value")

    toolbar_button.value = True
    if m is not None:
        toolbar_control = ipyleaflet.WidgetControl(
            widget=toolbar_widget, position="topright"
        )
        toolbar_control.cleanup = cleanup
        if toolbar_control not in m.controls:
            m.add_control(toolbar_control)
            m.tool_control = toolbar_control
    else:
        return toolbar_widget

tool_gui(tool_dict, max_width='420px', max_height='600px')

Create a GUI for a tool based on the tool dictionary.

Parameters:

Name Type Description Default
tool_dict dict

The dictionary containing the tool info.

required
max_width str

The max width of the tool dialog.

'420px'
max_height str

The max height of the tool dialog.

'600px'

Returns:

Type Description
object

An ipywidget object representing the tool interface.

Source code in geemap/toolbar.py
def tool_gui(tool_dict, max_width="420px", max_height="600px"):
    """Create a GUI for a tool based on the tool dictionary.

    Args:
        tool_dict (dict): The dictionary containing the tool info.
        max_width (str, optional): The max width of the tool dialog.
        max_height (str, optional): The max height of the tool dialog.

    Returns:
        object: An ipywidget object representing the tool interface.
    """
    tool_widget = widgets.VBox(
        layout=widgets.Layout(max_width=max_width, max_height=max_height)
    )
    children = []
    args = {}
    required_inputs = []
    style = {"description_width": "initial"}
    max_width = str(int(max_width.replace("px", "")) - 10) + "px"

    header_width = str(int(max_width.replace("px", "")) - 104) + "px"
    header = widgets.Label(
        value=f'Current Tool: {tool_dict["label"]}',
        style=style,
        layout=widgets.Layout(width=header_width),
    )
    code_btn = widgets.Button(
        description="View Code", layout=widgets.Layout(width="100px")
    )

    children.append(widgets.HBox([header, code_btn]))

    desc = widgets.Textarea(
        value=f'Description: {tool_dict["description"]}',
        layout=widgets.Layout(width="410px", max_width=max_width),
        disabled=True,
    )
    children.append(desc)

    run_btn = widgets.Button(description="Run", layout=widgets.Layout(width="100px"))
    cancel_btn = widgets.Button(
        description="Cancel", layout=widgets.Layout(width="100px")
    )
    help_btn = widgets.Button(description="Help", layout=widgets.Layout(width="100px"))
    import_btn = widgets.Button(
        description="Import",
        tooltip="Import the script to a new cell",
        layout=widgets.Layout(width="98px"),
    )
    tool_output = widgets.Output(layout=widgets.Layout(max_height="200px"))
    children.append(widgets.HBox([run_btn, cancel_btn, help_btn, import_btn]))
    children.append(tool_output)
    tool_widget.children = children

    def run_button_clicked(b):
        tool_output.outputs = ()

        required_params = required_inputs.copy()
        args2 = []
        for arg in args:
            line = ""
            if isinstance(args[arg], FileChooser):
                if arg in required_params and args[arg].selected is None:
                    with tool_output:
                        print(f"Please provide inputs for required parameters.")
                        break
                elif arg in required_params:
                    required_params.remove(arg)
                if arg == "i":
                    line = f"-{arg}={args[arg].selected}"
                else:
                    line = f"--{arg}={args[arg].selected}"
            elif isinstance(args[arg], widgets.Text):
                if arg in required_params and len(args[arg].value) == 0:
                    with tool_output:
                        print(f"Please provide inputs for required parameters.")
                        break
                elif arg in required_params:
                    required_params.remove(arg)
                if args[arg].value is not None and len(args[arg].value) > 0:
                    line = f"--{arg}={args[arg].value}"
            elif isinstance(args[arg], widgets.Checkbox):
                line = f"--{arg}={args[arg].value}"
            args2.append(line)

        if len(required_params) == 0:
            with tool_output:
                # wbt.run_tool(tool_dict["name"], args2)
                pass

    def help_button_clicked(b):
        import webbrowser

        tool_output.outputs = ()
        with tool_output:
            html = widgets.HTML(
                value=f'<a href={tool_dict["link"]} target="_blank">{tool_dict["link"]}</a>'
            )
            display(html)
        webbrowser.open_new_tab(tool_dict["link"])

    def code_button_clicked(b):
        import webbrowser

        with tool_output:
            html = widgets.HTML(
                value=f'<a href={tool_dict["link"]} target="_blank">{tool_dict["link"]}</a>'
            )
            display(html)
        webbrowser.open_new_tab(tool_dict["link"])

    def cancel_btn_clicked(b):
        tool_output.outputs = ()

    def import_button_clicked(b):
        tool_output.outputs = ()

        content = []

        create_code_cell("\n".join(content))

    import_btn.on_click(import_button_clicked)
    run_btn.on_click(run_button_clicked)
    help_btn.on_click(help_button_clicked)
    code_btn.on_click(code_button_clicked)
    cancel_btn.on_click(cancel_btn_clicked)

    return tool_widget

tool_header_template(m=None, opened=True, show_close_button=True)

Create a toolbar widget.

Parameters:

Name Type Description Default
m geemap.Map

The geemap.Map instance. Defaults to None.

None
opened bool

Whether to open the toolbar. Defaults to True.

True
show_close_button bool

Whether to show the close button. Defaults to True.

True
Source code in geemap/toolbar.py
def tool_header_template(m=None, opened=True, show_close_button=True):
    """Create a toolbar widget.

    Args:
        m (geemap.Map, optional): The geemap.Map instance. Defaults to None.
        opened (bool, optional): Whether to open the toolbar. Defaults to True.
        show_close_button (bool, optional): Whether to show the close button. Defaults to True.
    """

    widget_width = "250px"
    padding = "0px 0px 0px 5px"  # upper, right, bottom, left

    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"),
    )

    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))

    toolbar_widget = widgets.VBox()
    toolbar_widget.children = [toolbar_button]
    toolbar_header = widgets.HBox()
    if show_close_button:
        toolbar_header.children = [close_button, toolbar_button]
    else:
        toolbar_header.children = [toolbar_button]
    toolbar_footer = widgets.VBox()
    toolbar_footer.children = [
        buttons,
        output,
    ]

    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":
            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="topright"
        )

        if toolbar_control not in m.controls:
            m.add_control(toolbar_control)
            m.tool_control = toolbar_control
    else:
        return toolbar_widget

tool_template(m=None, opened=True)

Create a toolbar widget.

Parameters:

Name Type Description Default
m geemap.Map

The geemap.Map instance. Defaults to None.

None
opened bool

Whether to open the toolbar. Defaults to True.

True
Source code in geemap/toolbar.py
def tool_template(m=None, opened=True):
    """Create a toolbar widget.

    Args:
        m (geemap.Map, optional): The geemap.Map instance. Defaults to None.
        opened (bool, optional): Whether to open the toolbar. Defaults to True.
    """

    widget_width = "250px"
    padding = "0px 0px 0px 5px"  # upper, right, bottom, left

    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={"description_width": "initial"},
    )

    int_slider = widgets.IntSlider(
        min=1,
        max=100,
        description="Int Slider: ",
        readout=False,
        continuous_update=True,
        layout=widgets.Layout(width="220px", padding=padding),
        style={"description_width": "initial"},
    )

    int_slider_label = widgets.Label(str(int_slider.value))

    def update_int_slider(change):
        int_slider_label.value = str(change["new"])

    int_slider.observe(update_int_slider, "value")

    float_slider = widgets.FloatSlider(
        min=1,
        max=100,
        description="Float Slider: ",
        readout=False,
        continuous_update=True,
        layout=widgets.Layout(width="210px", padding=padding),
        style={"description_width": "initial"},
    )

    float_slider_label = widgets.Label(str(float_slider.value))

    def update_float_slider(change):
        float_slider_label.value = str(change["new"])

    float_slider.observe(update_float_slider, "value")

    color = widgets.ColorPicker(
        concise=False,
        description="Color:",
        value="white",
        style={"description_width": "initial"},
        layout=widgets.Layout(width=widget_width, padding=padding),
    )

    text = widgets.Text(
        value="",
        description="Textbox:",
        placeholder="Placeholder",
        style={"description_width": "initial"},
        layout=widgets.Layout(width=widget_width, padding=padding),
    )

    textarea = widgets.Textarea(
        placeholder="Placeholder",
        layout=widgets.Layout(width=widget_width, padding=padding),
    )

    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))

    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 = opened
    if m is not None:
        toolbar_control = ipyleaflet.WidgetControl(
            widget=toolbar_widget, position="topright"
        )

        if toolbar_control not in m.controls:
            m.add_control(toolbar_control)
            m.tool_control = toolbar_control
    else:
        return toolbar_widget