-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor colorbar into an ipywidgets subclass (#1640)
* Refactor colorbar into an ipywidgets subclass. * Add map_widgets to docs * Fix vertical colorbar bug --------- Co-authored-by: Qiusheng Wu <giswqs@gmail.com>
- Loading branch information
Showing
6 changed files
with
390 additions
and
131 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# map_widgets module | ||
|
||
::: geemap.map_widgets |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
"""Various ipywidgets that can be added to a map.""" | ||
|
||
import ipywidgets | ||
|
||
from . import common | ||
|
||
|
||
class Colorbar(ipywidgets.Output): | ||
"""A matplotlib colorbar widget that can be added to the map.""" | ||
|
||
def __init__( | ||
self, | ||
vis_params=None, | ||
cmap="gray", | ||
discrete=False, | ||
label=None, | ||
orientation="horizontal", | ||
transparent_bg=False, | ||
font_size=9, | ||
axis_off=False, | ||
max_width=None, | ||
**kwargs, | ||
): | ||
"""Add a matplotlib colorbar to the map. | ||
Args: | ||
vis_params (dict): Visualization parameters as a dictionary. See https://developers.google.com/earth-engine/guides/image_visualization for options. | ||
cmap (str, optional): Matplotlib colormap. Defaults to "gray". See https://matplotlib.org/3.3.4/tutorials/colors/colormaps.html#sphx-glr-tutorials-colors-colormaps-py for options. | ||
discrete (bool, optional): Whether to create a discrete colorbar. Defaults to False. | ||
label (str, optional): Label for the colorbar. Defaults to None. | ||
orientation (str, optional): Orientation of the colorbar, such as "vertical" and "horizontal". Defaults to "horizontal". | ||
transparent_bg (bool, optional): Whether to use transparent background. Defaults to False. | ||
font_size (int, optional): Font size for the colorbar. Defaults to 9. | ||
axis_off (bool, optional): Whether to turn off the axis. Defaults to False. | ||
max_width (str, optional): Maximum width of the colorbar in pixels. Defaults to None. | ||
Raises: | ||
TypeError: If the vis_params is not a dictionary. | ||
ValueError: If the orientation is not either horizontal or vertical. | ||
ValueError: If the provided min value is not scalar type. | ||
ValueError: If the provided max value is not scalar type. | ||
ValueError: If the provided opacity value is not scalar type. | ||
ValueError: If cmap or palette is not provided. | ||
""" | ||
|
||
import matplotlib # pylint: disable=import-outside-toplevel | ||
import numpy # pylint: disable=import-outside-toplevel | ||
|
||
if max_width is None: | ||
if orientation == "horizontal": | ||
max_width = "270px" | ||
else: | ||
max_width = "100px" | ||
|
||
if isinstance(vis_params, (list, tuple)): | ||
vis_params = {"palette": list(vis_params)} | ||
elif not vis_params: | ||
vis_params = {} | ||
|
||
if not isinstance(vis_params, dict): | ||
raise TypeError("The vis_params must be a dictionary.") | ||
|
||
if isinstance(kwargs.get("colors"), (list, tuple)): | ||
vis_params["palette"] = list(kwargs["colors"]) | ||
|
||
width, height = self._get_dimensions(orientation, kwargs) | ||
|
||
vmin = vis_params.get("min", kwargs.pop("vmin", 0)) | ||
if type(vmin) not in (int, float): | ||
raise TypeError("The provided min value must be scalar type.") | ||
|
||
vmax = vis_params.get("max", kwargs.pop("mvax", 1)) | ||
if type(vmax) not in (int, float): | ||
raise TypeError("The provided max value must be scalar type.") | ||
|
||
alpha = vis_params.get("opacity", kwargs.pop("alpha", 1)) | ||
if type(alpha) not in (int, float): | ||
raise TypeError("The provided opacity or alpha value must be type scalar.") | ||
|
||
if "palette" in vis_params.keys(): | ||
hexcodes = common.to_hex_colors(common.check_cmap(vis_params["palette"])) | ||
if discrete: | ||
cmap = matplotlib.colors.ListedColormap(hexcodes) | ||
linspace = numpy.linspace(vmin, vmax, cmap.N + 1) | ||
norm = matplotlib.colors.BoundaryNorm(linspace, cmap.N) | ||
else: | ||
cmap = matplotlib.colors.LinearSegmentedColormap.from_list( | ||
"custom", hexcodes, N=256 | ||
) | ||
norm = matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) | ||
elif cmap: | ||
cmap = matplotlib.pyplot.get_cmap(cmap) | ||
norm = matplotlib.colors.Normalize(vmin=vmin, vmax=vmax) | ||
else: | ||
raise ValueError( | ||
'cmap keyword or "palette" key in vis_params must be provided.' | ||
) | ||
|
||
fig, ax = matplotlib.pyplot.subplots(figsize=(width, height)) | ||
cb = matplotlib.colorbar.ColorbarBase( | ||
ax, | ||
norm=norm, | ||
alpha=alpha, | ||
cmap=cmap, | ||
orientation=orientation, | ||
**kwargs, | ||
) | ||
|
||
label = label or vis_params.get("bands") or kwargs.pop("caption", None) | ||
if label: | ||
cb.set_label(label, fontsize=font_size) | ||
|
||
if axis_off: | ||
ax.set_axis_off() | ||
ax.tick_params(labelsize=font_size) | ||
|
||
# Set the background color to transparent. | ||
if transparent_bg: | ||
fig.patch.set_alpha(0.0) | ||
|
||
super().__init__(layout=ipywidgets.Layout(width=max_width)) | ||
with self: | ||
self.outputs = () | ||
matplotlib.pyplot.show() | ||
|
||
def _get_dimensions(self, orientation, kwargs): | ||
default_dims = {"horizontal": (3.0, 0.3), "vertical": (0.3, 3.0)} | ||
if orientation in default_dims: | ||
default = default_dims[orientation] | ||
return ( | ||
kwargs.get("width", default[0]), | ||
kwargs.get("height", default[1]), | ||
) | ||
raise ValueError( | ||
f"orientation must be one of [{', '.join(default_dims.keys())}]." | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.