Source code for palettePickerEx

"""Extended selection of a palette of different families (sequential, divergent, etc.)"""
# Author(s): Davide.De-Marchi@ec.europa.eu
# Copyright © European Union 2022-2023
# 
# Licensed under the EUPL, Version 1.2 or as soon they will be approved by 
# the European Commission subsequent versions of the EUPL (the "Licence");
# 
# You may not use this work except in compliance with the Licence.
# 
# You may obtain a copy of the Licence at:
# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12

# Unless required by applicable law or agreed to in writing, software
# distributed under the Licence is distributed on an "AS IS"
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied.
# 
# See the Licence for the specific language governing permissions and
# limitations under the Licence.


# DOCUMENTATION ON MAPNIK:
# https://get-map.org/mapnik-lost-manual/book.pdf
# https://github.com/mapnik/mapnik-reference/blob/gh-pages/3.0.20/reference.json#L1517

from vois.vuetify import palettePicker, selectSingle, switch, sliderFloat

import ipyvuetify as v
from ipywidgets import widgets
from IPython.display import display


# Utility: convert three integers to '#RRGGBB'
def RGB(r,g,b):
    return '#{:02X}{:02X}{:02X}'.format(r, g, b)
    
custompalettes = [
    { "name": "Greyscale", "colors": ['#000000', '#FFFFFF']},
    
    { "name": "Simple", "colors": ['#000000', '#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF', '#FFFFFF']},
    
    { "name": "Dem",    "colors": [RGB(255,255,170), RGB( 39,168, 39), RGB( 11,128, 64), RGB(255,255,  0), RGB(255,186,  3),
                                   RGB(158, 30,  2), RGB(110, 40, 10), RGB(138, 94, 66), RGB(255,255,255)]},
    
    { "name": "NDVI",   "colors": [RGB(120,69,25), RGB(255,178,74), RGB(255,237,166), RGB(173,232,94),
                                   RGB(135,181,64), RGB(3,156,0), RGB(1,100,0), RGB(1,80,0)]}
]

families = ['carto', 'cmocean', 'cyclical', 'diverging', 'plotlyjs', 'qualitative', 'sequential', 'custom']



# Selection of a family and of a palette
[docs]class palettePickerEx(): """ Advanced selection of a palette of colors managing all the palette families and the interpolate flag Parameters ---------- family : str, optional Family of the palette, one of these values: ['carto', 'cmocean', 'cyclical', 'diverging', 'plotlyjs', 'qualitative', 'sequential', 'custom'] (default is 'sequential') value : str, optional Name of the initially selected palette (default is 'Viridis') interpolate : bool, optional If True the colors are displayed as interpolated (default is True) show_interpolate_switch : bool, optional If True an interpolate switch is shown to enable or disable the interpolated display of the selected palette (default is True) width : int, optional Width of the widget in pixels (default is 400) clearable : bool, optional If True the dwopdown list of palettes will allow for no selection (default is True) onchange : function, optional Python function to call when the user selects one of the palettes. The function will pass as parameters the list of colors and the interpolate flag (default is None) show_opacity_slider : bool, optional If True an slider to select opacity is shown (default is False) onchangeOpacity : function, optional Python function to call when the user changes the opacity. The function will as parameter the selected opacity in [0.0,1.0] (default is None) Examples -------- Creation of a selection widget for the palettes managing all the families:: from vois.vuetify import palettePickerEx from ipywidgets import widgets from IPython.display import display output = widgets.Output() def onchange(colors, interpolate): with output: print(colors, interpolate) p = palettePickerEx.palettePickerEx(onchange=onchange) display(p.draw()) display(output) .. figure:: figures/palettePickerEx.png :scale: 100 % :alt: palettePicker widget Example of an extended palette picker managing all the palette families and the interpolate flag """ # Initialization def __init__(self, family='sequential', value='Viridis', interpolate=True, show_interpolate_switch=True, onchange=None, width=400, clearable=True, show_opacity_slider=True, onchangeOpacity=None): self.family = family self.interpolate = interpolate self.onchange = onchange self.width = width self.show_interpolate_switch = show_interpolate_switch self.show_opacity_slider = show_opacity_slider self.onchangeOpacity = onchangeOpacity self.p = None self.sel = selectSingle.selectSingle('Family:', families, selection=family, width=150, onchange=self.onchangeFamily, marginy=1, clearable=False) self.sw = switch.switch(self.interpolate, "Interpolate", onchange=self.onchangeInterpolate) if not self.show_interpolate_switch and self.show_opacity_slider: self.op = sliderFloat.sliderFloat(1.0, text='Opacity:', minvalue=0.0, maxvalue=1.0, sliderwidth=self.width-128, onchange=self.onchangeOpacity) self.p = palettePicker.palettePicker(family=self.family, custompalettes=custompalettes, label='Palette:', clearable=clearable, width=self.width, height=26, onchange=self.onchangePalette) self.p.value = value self.spacer = v.Html(tag='div',children=[' '], style_='width: 10px;') # Draw the widget def draw(self): if self.show_interpolate_switch: r = widgets.HBox([self.sel.draw(), self.spacer, self.sw.draw()]) else: if self.show_opacity_slider: r = widgets.HBox([self.sel.draw(), self.spacer, self.op.draw()]) else: r = self.sel.draw() return widgets.VBox([r, self.p.draw()]) # Selection of a palette def onchangePalette(self): if not self.p is None: if not self.onchange is None: self.onchange(self.p.colors, self.interpolate) # Changed the family def onchangeFamily(self): self.family = self.sel.value if self.family == 'carto' or self.family == 'qualitative': self.interpolate = False self.sw.value = self.interpolate else: self.interpolate = True self.sw.value = self.interpolate self.p.updatePalettes(self.family,self.interpolate) self.p.value = self.p.images[0]['name'] if not self.onchange is None: self.onchange(self.p.colors, self.interpolate) # Changed the interpolation flag def onchangeInterpolate(self, flag): self.interpolate = flag value = self.p.value oldonchange = self.onchange self.onchange = None self.p.updatePalettes(self.family,self.interpolate) self.onchange = oldonchange self.p.value = value if not self.onchange is None: self.onchange(self.p.colors, self.interpolate) # familyname property @property def familyname(self): """ Get/Set name of the selected family. Returns -------- name : str Name of the currently selected family Example ------- Set and then get the current palette family:: picker.familyname = 'qualitative' print(picker.familyname) """ return self.sel.value # Select one of the families @familyname.setter def familyname(self, name): self.sel.value = name # value property @property def value(self): """ Get/Set name of the selected palette. Returns -------- name : str Name of the currently selected palette Example ------- Set and then get the current palette name:: picker.value = 'Viridis' print(picker.value) """ return self.p.value # Select one of the palette given its name @value.setter def value(self, name): self.p.value = name # colors property @property def colors(self): """ Get the colors of the selected palette. Returns -------- colorlist : list of strings in '#RRGGBB' format List of colors of the selected palette Example ------- Get the selected palette colors:: print(picker.colors) """ return self.p.colors # opacity property @property def opacity(self): """ Get/Set the opacity value. Returns -------- opacity : float Current value of opacity in thenrange [0.0,1.0] Example ------- Set and then get the opacity:: picker.opacity = 0.5 print(picker.opacity) """ if self.show_opacity_slider: return self.op.value return 1.0 # Set the opacity @opacity.setter def opacity(self, value): self.op.value = value