Source code for sepal_ui.mapping.legend_control

"""Customized control to display a legend on the map."""

from typing import Optional, Union

import traitlets as t
from ipyleaflet import WidgetControl
from ipywidgets import HTML
from traitlets import observe

import sepal_ui.sepalwidgets as sw
from sepal_ui.message import ms
from sepal_ui.scripts import utils as su


[docs] class LegendControl(WidgetControl): title: t.Unicode = t.Unicode(None).tag(sync=True) "Title of the legend." legend_dict: t.Dict = t.Dict(None).tag(sync=True) "Dictionary with key as label name and value as color" vertical: t.Bool = t.Bool(None).tag(sync=True) "Whether to display the legend in a vertical or horizontal way" _html_table: Optional[sw.Html] = None "The table containing the legend display" _html_title: Optional[sw.Html] = None "The tilte of the legend"
[docs] def __init__( self, legend_dict: dict = {}, title: str = ms.mapping.legend, vertical: bool = True, **kwargs, ) -> None: """A custom Legend widget ready to be embed in a map. This Legend can be control though it's different attributes, changing it's position of course but also the orientation ,the keys and their colors. .. versionadded:: 2.10.4 Args: legend_dict: the dictionary to fill the legend values. cannot be empty. title: title of the legend, if not set a default value in the current language will be used vertical: the orientation of the legend. default to True """ # init traits self.title = title self.legend_dict = legend_dict self.vertical = vertical # generate the content based on the init options self._html_title = sw.Html(tag="h4", children=[f"{self.title}"]) self._html_table = sw.Html(tag="table", children=[]) # create a card inside the widget # Be sure that the scroll bar will be shown up when legend horizontal self.legend_card = sw.Card( attributes={"id": "legend_card"}, style_="overflow-x:auto; white-space: nowrap;", max_width=450, max_height=350, children=[self._html_title, self._html_table], ).hide() # set some parameters for the actual widget kwargs["widget"] = self.legend_card kwargs.setdefault("position", "bottomright") super().__init__(**kwargs) self._set_legend(legend_dict)
def __len__(self) -> int: """Returns the number of elements in the legend.""" return len(self.legend_dict)
[docs] def hide(self) -> None: """Hide control by hiding its content.""" self.legend_card.hide() return
[docs] def show(self) -> None: """Show control by displaying its content.""" self.legend_card.show() return
@observe("legend_dict", "vertical") def _set_legend(self, *args) -> None: """Creates/update a legend based on the class legend_dict member.""" # Do this to avoid crash when called by trait for the first time if self._html_table is None: return if not self.legend_dict: self.hide() return self.show() if self.vertical: elements = [ sw.Html( tag="tr" if self.vertical else "td", children=[ sw.Html(tag="td", children=self.color_box(color)), label.capitalize(), ], ) for label, color in self.legend_dict.items() ] else: elements = [ ( sw.Html( tag="td", children=[label.capitalize()], ), sw.Html( tag="td", children=self.color_box(color), ), ) for label, color in self.legend_dict.items() ] # Flat nested list elements = [e for row in elements for e in row] self._html_table.children = elements return @observe("title") def _update_title(self, change: dict) -> None: """Trait method to update the title of the legend.""" # Do this to avoid crash when called by trait if self._html_title is None: return self._html_title.children = change["new"] return
[docs] @staticmethod def color_box(color: Union[str, tuple], size: int = 35) -> HTML: """Returns an rectangular SVG html element with the provided color. Args: color: It can be a string (e.g., 'red', '#ffff00', 'ffff00') or RGB tuple (e.g., (255, 127, 0)) size: the size of the legend square Returns: The HTML rendered color box """ # Define height and width based on the size w = size h = size / 2 return [ HTML( f""" <th> <svg width='{w}' height='{h}'> <rect width='{w}' height='{h}' style='fill:{su.to_colors(color)}; stroke-width:1;stroke:rgb(255,255,255)'/> </svg> </th> """ ) ]