"""Widget to manage the layers Table Of Content for a ipyleaflet Map"""
# 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.
import ipyvuetify as v
from ipywidgets import widgets, Layout
from IPython.display import display, HTML
import ipyleaflet
import base64
import json
try:
from jeodpp import inter
except:
pass
try:
from . import settings
from . import sortableList
from . import switch
from . import tooltip
from . import dialogGeneric
from . import selectSingle
from . import tabs
from . import radio
from . import palettePicker
from . import paletteEditor
from . import upload
from . import dialogWait
from . import dialogMessage
except:
import settings
import sortableList
import switch
import tooltip
import dialogGeneric
import selectSingle
import tabs
import radio
import palettePicker
import paletteEditor
import upload
import dialogWait
import dialogMessage
#####################################################################################################################################################
# interapro Layer class
#####################################################################################################################################################
[docs]class interaproLayer():
"""
A Layer to be visualized by interapro
"""
def __init__(self,
name, visible=True, opacity=1.0,
path=None,
file=None,
epsg=None,
nodata=None,
colorfile=None,
colortable=None,
colormap=None,
valuemap=None,
colorscheme=None,
colorcustom=None,
scalemin=None,
scalemax=None,
interpolate=None,
bands=None,
rmin=None,
rmax=None,
gmin=None,
gmax=None,
bmin=None,
bmax=None):
self.name = str(name)
self.visible = bool(visible)
self.opacity = float(opacity)
self.path = path
self.file = file
self.epsg = epsg
self.nodata = nodata
self.colorfile = colorfile
self.colortable = colortable
self.colormap = colormap
self.valuemap = valuemap
self.colorscheme = colorscheme
self.colorcustom = colorcustom
self.scalemin = scalemin
self.scalemax = scalemax
self.interpolate = interpolate
self.bands = bands
self.rmin = rmin
self.rmax = rmax
self.gmin = gmin
self.gmax = gmax
self.bmin = bmin
self.bmax = bmax
self.colorscheme_reverse = False
if not self.colorscheme is None:
if self.colorscheme[:3] == 'inv':
self.colorscheme_reverse = True
self.colorscheme = self.colorscheme[3:]
if self.interpolate is None or len(self.interpolate) == 0:
self.interpolate = 'NEAREST'
self.items = [] # for discrete mode
self.colors = [] # for continuous mode
self.predefined = 0
if not self.colormap is None:
self.mode = 0 # discrete values
self.getItems(self.colormap,self.valuemap)
else:
self.mode = 1 # continuous values
if not self.colorcustom is None:
self.predefined = 1
self.colors = self.colorcustom.split(',')
# Direct download of a .txt file containing a string
def downloadText(self, textobj, output, fileName="layer.json"):
string_bytes = textobj.encode("ascii","ignore")
base64_bytes = base64.b64encode(string_bytes)
base64_string = base64_bytes.decode("ascii")
output.clear_output()
with output:
display(HTML('<script>function downloadURI(uri, name) { var link = document.createElement("a"); link.download = name; link.href = uri; link.click();} downloadURI("data:application/octet-stream;charset=utf-8;base64,' + base64_string + '","' + fileName + '"); </script>'))
# Calculate self.items from a colormap and a valuemap string
def getItems(self, colormapstr, valuemapstr):
self.items = []
dcolor = {}
dvalue = {}
if colormapstr and len(colormapstr) > 0:
try:
dcolor = eval(colormapstr)
except:
dcolor = {}
if valuemapstr and len(valuemapstr) > 0:
try:
dvalue = eval(valuemapstr)
except:
dvalue = {}
for key,value in dcolor.items():
item = { "value": key, "class": "", "color": value }
if key in dvalue:
item["class"] = dvalue[key]
self.items.append(item)
# Return a ipyleaflet.TileLayer instance
[docs] def tileLayer(self):
"""
Return a ipyleaflet.TileLayer instance
"""
if not self.path is None :
if isinstance(self.path , str):
coll = inter.Collection(eval(self.path))
else:
coll = inter.Collection(self.path)
else:
coll = inter.ImageCollection("SIMPLE")
if not self.file is None:
coll.parameter('file',self.file)
if not self.epsg is None:
coll.parameter('epsg',str(self.epsg))
if not self.nodata is None:
coll.parameter('nodata',str(self.nodata))
if not self.colorfile is None and len(self.colorfile) > 0:
coll.parameter('colorfile',str(self.colorfile))
if not self.colortable is None and len(self.colortable) > 0:
coll.parameter('colortable',str(self.colortable))
if not self.colormap is None and len(self.colormap) > 0:
coll.parameter('colormap',str(self.colormap))
if not self.valuemap is None and len(self.valuemap) > 0:
coll.parameter('valuemap',str(self.valuemap))
if not self.colorscheme is None and len(self.colorscheme) > 0:
coll.parameter('colorscheme',str(self.colorscheme))
if not self.colorcustom is None and len(self.colorcustom) > 0:
coll.parameter('colorcustom',str(self.colorcustom))
if not self.scalemin is None:
coll.parameter('scalemin',str(self.scalemin))
if not self.scalemax is None:
coll.parameter('scalemax',str(self.scalemax))
if not self.interpolate is None:
coll.parameter('interpolate',str(self.interpolate))
if not self.bands is None:
coll.parameter('bands',str(self.bands))
if not self.rmin is None:
coll.parameter('rmin',str(self.rmin))
if not self.rmax is None:
coll.parameter('rmax',str(self.rmax))
if not self.gmin is None:
coll.parameter('gmin',str(self.gmin))
if not self.gmax is None:
coll.parameter('gmax',str(self.gmax))
if not self.bmin is None:
coll.parameter('bmin',str(self.bmin))
if not self.bmax is None:
coll.parameter('bmax',str(self.bmax))
p = coll.process()
procid = p.toLayer()
#p.printProcess()
return ipyleaflet.TileLayer(name=self.name, visible=self.visible, opacity=self.opacity, url='https://jeodpp.jrc.ec.europa.eu/jeodpp-inter-view/?x={x}&y={y}&z={z}&procid=%s'%procid)
# Save layer info into a dictionary (for downloading it as a .json file)
def toDict(self):
d = { "format": "BDAP layer 1.0" }
if not self.path is None:
d['path'] = str(self.path)
else:
if not self.file is None:
d['file'] = self.file
if not self.epsg is None:
d['epsg'] = self.epsg
if not self.nodata is None:
d['nodata'] = self.nodata
if not self.colorfile is None and len(self.colorfile) > 0:
d['colorfile'] = self.colorfile
if not self.colortable is None and len(self.colortable) > 0:
d['colortable'] = self.colortable
if not self.colormap is None and len(self.colormap) > 0:
d['colormap'] = self.colormap
if not self.valuemap is None and len(self.valuemap) > 0:
d['valuemap'] = self.valuemap
if not self.colorscheme is None and len(self.colorscheme) > 0:
d['colorscheme'] = self.colorscheme
if not self.colorcustom is None and len(self.colorcustom) > 0:
d['colorcustom'] = self.colorcustom
if not self.scalemin is None:
d['scalemin'] = self.scalemin
if not self.scalemax is None:
d['scalemax'] = self.scalemax
if not self.interpolate is None:
d['interpolate'] = self.interpolate
if not self.bands is None:
d['bands'] = self.bands
if not self.rmin is None:
d['rmin'] = self.rmin
if not self.rmax is None:
d['rmax'] = self.rmax
if not self.gmin is None:
d['gmin'] = self.gmin
if not self.gmax is None:
d['gmax'] = self.gmax
if not self.bmin is None:
d['bmin'] = self.bmin
if not self.bmax is None:
d['bmax'] = self.bmax
return d
# Load layer info from a dictionary (for uploading it from a .json file)
def fromDict(self, d):
self.name = ''
self.visible = True
self.opacity = 1.0
self.path = None
self.file = None
self.epsg = None
self.nodata = None
self.colorfile = None
self.colortable = None
self.colormap = None
self.valuemap = None
self.colorscheme = None
self.colorcustom = None
self.scalemin = None
self.scalemax = None
self.interpolate = None
self.bands = None
self.rmin = None
self.rmax = None
self.gmin = None
self.gmax = None
self.bmin = None
self.bmax = None
if d['format'] == "BDAP layer 1.0":
if 'path' in d and len(d['path']) > 0:
self.path = d['path']
else:
if 'file' in d:
self.file = d['file']
if 'epsg' in d:
self.epsg = d['epsg']
if 'nodata' in d:
self.nodata = d['nodata']
if 'colorfile' in d and len(d['colorfile']) > 0:
self.colorfile = d['colorfile']
if 'colortable' in d and len(d['colortable']) > 0:
self.colortable = d['colortable']
if 'colormap' in d and len(d['colormap']) > 0:
self.colormap = d['colormap']
if 'valuemap' in d and len(d['valuemap']) > 0:
self.valuemap = d['valuemap']
if 'colorscheme' in d and len(d['colorscheme']) > 0:
self.colorscheme = d['colorscheme']
if 'colorcustom' in d and len(d['colorcustom']) > 0:
self.colorcustom = d['colorcustom']
if 'scalemin' in d:
self.scalemin = d['scalemin']
if 'scalemax' in d:
self.scalemax = d['scalemax']
if 'interpolate' in d:
self.interpolate = d['interpolate']
if 'bands' in d:
self.bands = d['bands']
if 'rmin' in d:
self.rmin = d['rmin']
if 'rmax' in d:
self.rmax = d['rmax']
if 'gmin' in d:
self.gmin = d['gmin']
if 'gmax' in d:
self.gmax = d['gmax']
if 'bmin' in d:
self.bmin = d['bmin']
if 'bmax' in d:
self.bmax = d['bmax']
self.colorscheme_reverse = False
if not self.colorscheme is None:
if self.colorscheme[:3] == 'inv':
self.colorscheme_reverse = True
self.colorscheme = self.colorscheme[3:]
if self.interpolate is None or len(self.interpolate) == 0:
self.interpolate = 'NEAREST'
self.items = [] # for discrete mode
self.colors = [] # for continuous mode
self.predefined = 0
if not self.colormap is None:
self.mode = 0 # discrete values
self.getItems(self.colormap,self.valuemap)
else:
self.mode = 1 # continuous values
if not self.colorcustom is None:
self.predefined = 1
self.colors = self.colorcustom.split(',')
# Open a dialog-box to edit the layer
[docs] def edit(self, output, outdebug, onok=None, oncancel=None):
"""
Open a dialog-box to edit the layer
"""
pconteditor = None
outscheme = widgets.Output(layout=Layout(width='90%', height='80px'))
outpalette = widgets.Output(layout=Layout(width='90%', height='80px'))
outdiscrete = widgets.Output(layout=Layout(width='95%', height='130px'))
outcontinuous = widgets.Output(layout=Layout(width='95%', height='130px'))
btnEditDisc = v.Btn(icon=True, class_="pa-0 ma-0 mt-4", children=[v.Icon(children=['mdi-palette-outline'])])
btnEditCont = v.Btn(icon=True, class_="pa-0 ma-0 mt-4", children=[v.Icon(children=['mdi-palette-outline'])])
tf_colormap = v.TextField(label='Colormap:', v_model=self.colormap, color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4", style_='width: 650px; max-width: 650px;')
tf_valuemap = v.TextField(label='Valuemap:', v_model=self.valuemap, color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4", style_='width: 650px; max-width: 650px;')
# Selected a discrete palette
def on_palette_discrete_ok():
self.items = pconteditor.items
tf_colormap.v_model = str(dict([ (x['value'],x['color']) for x in self.items]))
tf_valuemap.v_model = str(dict([ (x['value'],x['class']) for x in self.items]))
# Edit of a discrete palette
def onEditDiscrete(widget, event, data):
nonlocal pconteditor
#with outdebug:
# print(tf_colormap.v_model)
self.getItems(tf_colormap.v_model,tf_valuemap.v_model)
pconteditor = paletteEditor.paletteEditor(items=self.items, width=450, interpolate=False)
dlg = dialogGeneric.dialogGeneric(title='Discrete palette editor', text='', show=True,
addclosebuttons=True, width=560, persistent=True,
addokcancelbuttons=True, on_ok=on_palette_discrete_ok,
fullscreen=False, content=[pconteditor.draw()], output=output)
# Selected a continuous palette
def on_palette_continuous_ok():
self.colors = pconteditor.colors
outpalette.clear_output(wait=True)
with outpalette:
c = v.Card(outlined=True, class_="pa-0 ma-0 mt-3", style_='width: 400px; max-width: 400px; height: 40px; max-height: 40px;' ,
children=[v.Img(class_="pa-0 ma-1", src=palettePicker.image2Base64(palettePicker.paletteImage(self.colors, width=360, height=29, interpolate=True)))])
display(widgets.HBox([c,spacer,tooltip.tooltip("Edit palette",btnEditCont)]))
# Edit a continuous palette
def onEditContinuous(widget, event, data):
nonlocal pconteditor
items = paletteEditor.colorlist2Items(self.colors)
pconteditor = paletteEditor.paletteEditor(items=items, width=450, interpolate=True)
dlg = dialogGeneric.dialogGeneric(title='Continuous palette editor', text='', show=True,
addclosebuttons=True, width=560, persistent=True,
addokcancelbuttons=True, on_ok=on_palette_continuous_ok,
fullscreen=False, content=[pconteditor.draw()], output=output)
btnEditDisc.on_event('click', onEditDiscrete)
btnEditCont.on_event('click', onEditContinuous)
spacer = v.Html(tag='div',children=[' '], style_='width: 15px;')
outcolorschema = widgets.Output(layout=Layout(width='330px', height='66px'))
layer_text = ''
# Called when a .json file is selected for upload
def on_upload_layer(files):
nonlocal layer_text
if len(files) > 0:
f = files[0]
layer_text = f['file_obj'].read().decode("utf-8")
try:
j = json.loads(layer_text)
if ("format" in j) and (j["format"] == "BDAP layer 1.0"):
pass
else:
upload_file.clear()
e = dialogMessage.dialogMessage(title='Error',
text='The uploaded file is not in the \"BDAP layer 1.0\" format',
addclosebuttons=False, show=True, width=400, output=output)
except:
upload_file.clear()
e = dialogMessage.dialogMessage(title='Error',
text='Cannot read the uploaded file!',
addclosebuttons=False, show=True, width=400, output=output)
else:
layer_text = ''
upload_file = upload.upload(accept="application/json", multiple=False, show_progress=False, onchange=on_upload_layer,
label='Layer file:', placeholder='Click to select the layer to upload', width='480px', margins="pa-0 ma-0 ml-4")
# Display of the selected color scheme
def display_colorschema(arg=None):
outcolorschema.clear_output()
if not self.colorscheme is None:
with outcolorschema:
colorscheme = ss_colorscheme.value
if s_reverse.value:
colorscheme = 'inv' + colorscheme
display(HTML("<br/>"))
display(HTML(inter.colorSchemaLegend(colorscheme, Height=25)))
# Save values from widgets to self. members
def widgets2members():
with outdebug:
if len(tf_path.v_model) > 0:
try:
self.path = 'inter.' + tf_path.v_model.replace('inter.','')
except:
self.path = None
else:
self.path = None
self.name = tf_name.v_model
self.file = tf_file.v_model
self.epsg = ss_epsg.value
self.nodata = float(tf_nodata.v_model)
self.scalemin = float(tf_scalemin.v_model)
self.scalemax = float(tf_scalemax.v_model)
self.interpolate = ss_interpolate.value
self.colorfile = tf_colorfile.v_model
self.colortable = tf_colortable.v_model
self.colorscheme_reverse = s_reverse.value
if tmode.value == 0: # Discrete
self.colormap = tf_colormap.v_model
self.valuemap = tf_valuemap.v_model
else: # Continuous
self.colormap = None
self.valuemap = None
if tscheme.value == 0: # Predefined schemas
self.colorscheme = ss_colorscheme.value
if self.colorscheme_reverse:
self.colorscheme = 'inv' + self.colorscheme
self.colorcustom = None
else: # Custom palette
self.colorscheme = None
self.colorcustom = ','.join(self.colors)
if s_rgb.value:
self.bands = str(ss_bandR.value) + '#' + str(ss_bandG.value) + '#' + str(ss_bandB.value)
self.rmin = float(tf_rmin.v_model)
self.rmax = float(tf_rmax.v_model)
self.gmin = float(tf_gmin.v_model)
self.gmax = float(tf_gmax.v_model)
self.bmin = float(tf_bmin.v_model)
self.bmax = float(tf_bmax.v_model)
else:
self.bands = None
self.rmin = None
self.rmax = None
self.gmin = None
self.gmax = None
self.bmin = None
self.bmax = None
# load self. members to widgets
def members2widgets():
with outdebug:
pathvalue = ''
if not self.path is None: pathvalue = str(self.path)
tf_path.v_model = pathvalue
tf_name.v_model = self.name
tf_file.v_model = self.file
ss_epsg.value = self.epsg
tf_nodata.v_model = self.nodata
tf_scalemin.v_model = self.scalemin
tf_scalemax.v_model = self.scalemax
ss_interpolate.value = self.interpolate
tf_colorfile.v_model = self.colorfile
tf_colortable.v_model = self.colortable
tmode.value = self.mode
tscheme.value = self.predefined
s_reverse.value = self.colorscheme_reverse
if self.colormap and len(self.colormap) > 0: # Discrete
tf_colormap.v_model = self.colormap
tf_valuemap.v_model = self.valuemap
else: # Continuous
tf_colormap.v_model = None
tf_valuemap.v_model = None
if self.colorscheme and len(self.colorscheme) > 0: # Predefined schemas
if self.colorscheme[:3] == 'inv':
self.colorscheme_reverse = True
s_reverse.value = self.colorscheme_reverse
self.colorscheme = self.colorscheme[3:]
ss_colorscheme.value = self.colorscheme
if self.bands and len(self.bands) > 0:
v = self.bands.split('#')
if len(v) >= 3:
ss_bandR.value = v[0]
ss_bandG.value = v[1]
ss_bandB.value = v[2]
tf_rmin.v_model = self.rmin
tf_rmax.v_model = self.rmax
tf_gmin.v_model = self.gmin
tf_gmax.v_model = self.gmax
tf_bmin.v_model = self.bmin
tf_bmax.v_model = self.bmax
# Exit with OK: update the values
def edit_onok():
widgets2members()
if onok:
onok()
# Manage rgb switch
def on_rgb(arg=None):
disabled = True
if s_rgb.value: disabled = False
ss_bandR.disabled = disabled
ss_bandG.disabled = disabled
ss_bandB.disabled = disabled
tf_rmin.disabled = disabled
tf_rmax.disabled = disabled
tf_gmin.disabled = disabled
tf_gmax.disabled = disabled
tf_bmin.disabled = disabled
tf_bmax.disabled = disabled
# Control disable state when path is entered
def onpathchanged(widget, event, data):
disabled = False
if tf_path.v_model and len(tf_path.v_model) > 0: disabled = True
tf_file.disabled = disabled
ss_epsg.disabled = disabled
tf_nodata.disabled = disabled
tf_scalemin.disabled = disabled
tf_scalemax.disabled = disabled
ss_interpolate.disabled = disabled
tf_colorfile.disabled = disabled
tf_colortable.disabled = disabled
tscheme.disabled = disabled
tmode.disabled = disabled
ss_colorscheme.disabled = disabled
s_reverse.disabled = disabled
btnEditDisc.disabled = disabled
btnEditCont.disabled = disabled
tf_colormap.disabled = disabled
tf_valuemap.disabled = disabled
s_rgb.disabled = disabled
if (not disabled) and (not s_rgb.value): disabled = True
ss_bandR.disabled = disabled
ss_bandG.disabled = disabled
ss_bandB.disabled = disabled
tf_rmin.disabled = disabled
tf_rmax.disabled = disabled
tf_gmin.disabled = disabled
tf_gmax.disabled = disabled
tf_bmin.disabled = disabled
tf_bmax.disabled = disabled
# Called when the file upload dialog is closed with the "OK" button
def on_upload_ok():
nonlocal layer_text
if len(layer_text) > 2:
dlg = dialogWait.dialogWait(text='Loading layer...', output=output)
try:
d = json.loads(layer_text)
if ("format" in d) and (d["format"] == "BDAP layer 1.0"):
self.fromDict(d)
members2widgets()
else:
e = dialogMessage.dialogMessage(title='Error',
text='The uploaded file is not in the \"BDAP layer 1.0\" format',
addclosebuttons=False, show=True, width=400, output=output)
except:
e = dialogMessage.dialogMessage(title='Error',
text='Cannot read the uploaded file!',
addclosebuttons=False, show=True, width=400, output=output)
dlg.close()
layer_text = ''
upload_file.clear()
# Called when the file upload dialog is closed with the "cancel" button
def on_upload_cancel():
upload_file.clear()
# Upload
def onupload(widget, event, data):
dlg = dialogGeneric.dialogGeneric(title='Load a layer from local disk', text='', show=True,
addclosebuttons=False, width=520,
addokcancelbuttons=True, on_ok=on_upload_ok, on_cancel=on_upload_cancel,
fullscreen=False, content=[upload_file.draw()], output=output)
# Download
def ondownload(widget, event, data):
def on_ok():
widgets2members()
d = self.toDict()
txt = json.dumps(d)
filename = tf.v_model
if filename[-5:] != ".json": filename += ".json"
self.downloadText(txt, fileName=filename, output=output)
tf = v.TextField(v_model="Layer", label="Layer file name", autofocus=True, color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4")
dlg = dialogGeneric.dialogGeneric(title='Save current layer to local disk', text='', show=True,
addclosebuttons=False, width=500,
addokcancelbuttons=True, on_ok=on_ok,
fullscreen=False, content=[tf], output=output)
with outdebug:
pathvalue = ''
if not self.path is None: pathvalue = str(self.path)
tf_name = v.TextField(label='Layer name:', v_model=self.name, color=settings.color_first, class_="pa-0 ma-0 mt-7 ml-4 mr-4", autofocus=True)
btnupload = v.Btn(icon=True, class_="mr-0", children=[v.Icon(children=['mdi-folder-open-outline'])])
btndownload = v.Btn(icon=True, class_="mr-0", children=[v.Icon(children=['mdi-content-save'])])
btnupload.on_event('click', onupload)
btndownload.on_event('click', ondownload)
r0 = v.Row(no_gutters=True, justify="space-between", children=[tf_name, btnupload, btndownload], class_="pa-0 ma-0")
tf_path = v.TextField(label='Collections Path:', v_model=pathvalue, color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4")
tf_path.on_event('input', onpathchanged)
tf_file = v.TextField(label='File:', v_model=self.file, color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4")
ss_epsg = selectSingle.selectSingle(label='Epsg:', values=['3857', '4326', '3035'], selection=str(self.epsg), width=180, newvalues_enabled=True, newvalues_type='number', clearable=False)
tf_nodata = v.TextField(label='Nodata:', v_model=self.nodata, type='number', color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4 mt-2", style_="width: 110px; max-width: 120px")
tf_scalemin = v.TextField(label='Scalemin:', v_model=self.scalemin, type='number', color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4 mt-2", style_="width: 110px; max-width: 120px")
tf_scalemax = v.TextField(label='Scalemax:', v_model=self.scalemax, type='number', color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4 mt-2", style_="width: 110px; max-width: 120px")
ss_interpolate = selectSingle.selectSingle(label='Interpolation:',
values=['NEAREST', 'BILINEAR', 'CUBIC', 'CUBICSPLINE', 'LANCZOS', 'AVERAGE', 'MODE', 'MAX', 'MIN', 'MED'],
selection=self.interpolate, width=200, newvalues_enabled=False, clearable=False)
r1 = v.Row(no_gutters=True, justify="space-between", children=[ss_epsg.draw(),tf_nodata, tf_scalemin, tf_scalemax, ss_interpolate.draw()], class_="pa-0 ma-0 ml-4 mr-4")
tf_colorfile = v.TextField(label='Color file:', v_model=self.colorfile, color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4")
tf_colortable = v.TextField(label='Color table file:', v_model=self.colortable, color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4")
coll = inter.ImageCollection("SIMPLE")
colorschemes = coll.listColorSchemes()
ss_colorscheme = selectSingle.selectSingle(label='Color scheme:', values=colorschemes, selection=self.colorscheme, width=220, newvalues_enabled=False, marginy=4, clearable=False, onchange=display_colorschema)
s_reverse = switch.switch(self.colorscheme_reverse, 'Reverse color order', inset=True, dense=True, onchange=display_colorschema)
tscheme = tabs.tabs(self.predefined, ['Predefined color schemes', 'Custom color palette'], contents=[outscheme,outpalette], row=True)
display_colorschema()
outscheme.clear_output(wait=True)
with outscheme:
display(widgets.HBox([ss_colorscheme.draw(),spacer,v.Row(no_gutters=True, justify="start", children=[s_reverse.draw()], class_="pa-0 ma-0 mt-3"),spacer,outcolorschema]))
outpalette.clear_output(wait=True)
with outpalette:
c = v.Card(outlined=True, class_="pa-0 ma-0 mt-3", style_='width: 400px; max-width: 400px; height: 40px; max-height: 40px;' ,
children=[v.Img(class_="pa-0 ma-1", src=palettePicker.image2Base64(palettePicker.paletteImage(self.colors, width=360, height=29, interpolate=True)))])
display(widgets.HBox([c,spacer,tooltip.tooltip("Edit palette",btnEditCont)]))
outdiscrete.clear_output(wait=True)
with outdiscrete:
display(widgets.HBox([btnEditDisc,spacer,widgets.VBox([tf_colormap,tf_valuemap])]))
outcontinuous.clear_output(wait=True)
with outcontinuous:
display(tscheme.draw())
tmode = tabs.tabs(self.mode, ['Discrete values', 'Continuous values'], contents=[outdiscrete,outcontinuous], row=False)
r2 = v.Row(no_gutters=True, justify="start", children=[tmode.draw()], class_="pa-0 ma-0 ml-4 mr-4")
s_rgb = switch.switch(self.colorscheme_reverse, 'RGB', inset=True, dense=True, onchange=on_rgb)
bands = [str(x) for x in range(1,11)]
ss_bandR = selectSingle.selectSingle(label='R:', values=bands, selection="1", width=75, newvalues_enabled=True, newvalues_type='number', clearable=False)
ss_bandG = selectSingle.selectSingle(label='G:', values=bands, selection="2", width=75, newvalues_enabled=True, newvalues_type='number', clearable=False)
ss_bandB = selectSingle.selectSingle(label='B:', values=bands, selection="3", width=75, newvalues_enabled=True, newvalues_type='number', clearable=False)
tf_rmin = v.TextField(label='Rmin:', v_model=self.rmin, type='number', color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4 mt-2", style_="width: 65px; max-width: 65px")
tf_rmax = v.TextField(label='Rmax:', v_model=self.rmax, type='number', color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4 mt-2", style_="width: 65px; max-width: 65px")
tf_gmin = v.TextField(label='Gmin:', v_model=self.gmin, type='number', color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4 mt-2", style_="width: 65px; max-width: 65px")
tf_gmax = v.TextField(label='Gmax:', v_model=self.gmax, type='number', color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4 mt-2", style_="width: 65px; max-width: 65px")
tf_bmin = v.TextField(label='Bmin:', v_model=self.bmin, type='number', color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4 mt-2", style_="width: 65px; max-width: 65px")
tf_bmax = v.TextField(label='Bmax:', v_model=self.bmax, type='number', color=settings.color_first, class_="pa-0 ma-0 ml-4 mr-4 mt-2", style_="width: 65px; max-width: 65px")
r3 = v.Row(no_gutters=True, justify="start", children=[s_rgb.draw(), spacer, ss_bandR.draw(),spacer,ss_bandG.draw(),spacer,ss_bandB.draw(),
tf_rmin,tf_rmax,tf_gmin,tf_gmax,tf_bmin,tf_bmax], class_="pa-0 ma-0 ml-4 mr-4")
on_rgb()
dlg = dialogGeneric.dialogGeneric(title='Edit Layer', text='', show=True,
addclosebuttons=False, width=1000, persistent=True,
addokcancelbuttons=True, on_ok=edit_onok, on_cancel=oncancel,
fullscreen=False, content=[r0, tf_path, tf_file, r1, tf_colorfile, tf_colortable, r2, r3], output=output)
#####################################################################################################################################################
# Layers TOC widget class
#####################################################################################################################################################
[docs]class layers():
"""
Widget to manage the layers Table Of Content for a ipyleaflet Map
Parameters
----------
m : ipyleaflet.Map instance
Map instance to connect to the layers widget
color : str, optional
Color to use for the widget (default is settings.color_first)
dark : bool, optional
If True, the widget will have a dark background (default is settings.dark_mode)
width : int, optional
Width of the widget in pixels (default is 320)
Example
-------
Creation of a layers management widget::
import ipyleaflet
from jeodpp import inter, imap
from ipywidgets import widgets, Layout
from vois.vuetify import layers
# Given a collection path returns a ipyleaflet.TileLayer
def tileLayer(name, collectionpath):
p = inter.Collection(collectionpath).process()
procid = p.toLayer()
return ipyleaflet.TileLayer(name=name, url='https://jeodpp.jrc.ec.europa.eu/jeodpp-inter-view/?x={x}&y={y}&z={z}&procid=%s'%procid)
layer1 = tileLayer('Merit DEM', inter.collections.BaseData.Elevation.MERIT.Hillshade)
layer2 = tileLayer('Corine 2018', inter.collections.BaseData.Landcover.CLC2018)
layer3 = tileLayer('Gisco Labels', inter.collections.Basemaps.Gisco.OSMCartoLabels)
height = 500
m = imap.Map(layout=Layout(height='%dpx'%height))
m.add_layer(layer1)
m.add_layer(layer2)
m.add_layer(layer3)
ly = layers.layers(m, width=400, dark=False)
display(widgets.HBox([ly.draw(),m]))
.. figure:: figures/layers.png
:scale: 100 %
:alt: layers widget
Example of a layers management widget
"""
def __init__(self, m, color=settings.color_first, dark=settings.dark_mode, width=400):
self.map = m
self.color = color
self.dark = dark
self.width = width
# Retrieve non-base layers from the map and assign a unique layerid
self.layers = []
self.nextlayerid = 0
for index,layer in enumerate(self.map.layers):
if not layer.base:
self.layers.insert(0, { "name": layer.name,
"base": layer.base,
"visible": layer.visible,
"opacity": layer.opacity,
"mapindex": index, # Index of the layer in the map
"layerid": self.nextlayerid}) # Unique layer identifier (unmutable)
self.nextlayerid += 1
# Calculate indexing of the layers: self.layerid2mapindex and self.mapindex2layerid
self.indexLayers()
# Create the sortableList widget
self.s = sortableList.sortableList(items=self.layers, width=width, dark=dark,
allowNew=True, newOnTop=True, buttonstooltip=True,
itemNew=self.layerNew,
itemContent=self.layerContent,
onmovedown=self._onmovedown,
onmoveup=self._onmoveup,
onremoving=self._onremoving,
onremoved=self._onremoved)
self.outservice = widgets.Output(layout=Layout(width='0px', height='0px'))
self.debug = widgets.Output()
# Calculate indexing of the layers: self.layerid2mapindex and self.mapindex2layerid
def indexLayers(self):
self.layerid2mapindex = {}
self.mapindex2layerid = {}
mapindex = len(self.layers)
for layer in self.layers:
layer['mapindex'] = mapindex
mapindex -= 1
self.layerid2mapindex[layer['layerid'] ] = layer['mapindex']
self.mapindex2layerid[layer['mapindex']] = layer['layerid']
# Manage movedown event
def _onmovedown(self, index):
a = self.layers[index]['mapindex']
b = self.layers[index+1]['mapindex']
layerlist = list(self.map.layers)
layerlist[b], layerlist[a] = layerlist[a], layerlist[b]
self.map.layers = tuple(layerlist)
self.indexLayers()
# Manage moveup event
def _onmoveup(self, index):
a = self.layers[index]['mapindex']
b = self.layers[index-1]['mapindex']
layerlist = list(self.map.layers)
layerlist[b], layerlist[a] = layerlist[a], layerlist[b]
self.map.layers = tuple(layerlist)
self.indexLayers()
# Manage pre-remove event
def _onremoving(self, index):
indexinmap = self.layers[index]['mapindex']
self.map.layers = self.map.layers[:indexinmap] + self.map.layers[indexinmap+1:]
# Manage post-remove event
def _onremoved(self, index):
self.indexLayers()
# Add the new edited layer to the map
def addNewLayer(self):
with self.debug:
mapindex = len(self.map.layers)
layerid = self.nextlayerid
self.nextlayerid += 1
# Define the new item of the sortableList
newItem = { "name": self.newlayer.name,
"base": False,
"visible": self.newlayer.visible,
"opacity": self.newlayer.opacity,
"mapindex": mapindex,
"layerid": layerid
}
# Add the new item to the sortableList widget
self.s.doAddItem(newItem)
# Add the new layer to the map
self.map.add_layer(self.newlayer.tileLayer())
# Add element to the index dicts
self.layerid2mapindex[layerid] = mapindex
self.mapindex2layerid[mapindex] = layerid
# Creation of a new layer
def layerNew(self):
self.newlayer = interaproLayer('Population 2020', visible=True, opacity=1.0,
file='/eos/jeodpp/data/base/Population/GLOBAL/WorldPop/VER1-0/Data/VRT/MOSAIC_ppp_prj_2020.vrt',
epsg=4326,
nodata=-99999.0,
colorscheme='RdYlGn_mixed',
scalemin=0.0,
scalemax=15.0)
self.newlayer.edit(self.outservice, self.debug, onok=self.addNewLayer)
return None
# Content of a layer
def layerContent(self, layer, index):
# Toggle visibility of the layer
def onvisible(flag):
layerid = layer['layerid']
mapindex = self.layerid2mapindex[layerid]
layer['visible'] = not layer['visible']
self.map.layers[mapindex].visible = layer['visible']
# Change opacity of a layer
def onopacity(widget, event, data):
layerid = layer['layerid']
mapindex = self.layerid2mapindex[layerid]
layer['opacity'] = opacity.v_model/10.0
self.map.layers[mapindex].opacity = layer['opacity']
visible = switch.switch(layer['visible'], '', onchange=onvisible, inset=True, dense=True)
opacity = v.Slider(v_model=int(10*layer['opacity']),
dense=True, xsmall=True,
ticks=True, thumb_size=10, dark=self.dark,
color=self.color, track_color="grey",
class_="pa-0 ma-0 ml-5 mr-5 mt-3 mb-n1",
style_='max-width: 140px; width: 140px;',
min=0, max=10, vertical=False, height=32)
opacity.on_event('input', onopacity)
htmlopacity = v.Card(dark=self.dark, children=[v.Html(tag='div', children=[opacity], style_='overflow: hidden;')])
m = v.Menu(offset_y=True, open_on_hover=False, dense=True, dark=self.dark,
v_slots=[{
'name': 'activator',
'variable': 'menuData',
'children': v.Btn(v_on='menuData.on', icon=True, depressed=True,
large=False, dense=True, class_='pa-0 ma-0',
children=[v.Icon(color='#aaaaaa', children=['mdi-opacity'])]),
}],
children=[htmlopacity] )
bcolor = v.Btn(icon=True, depressed=True,
large=False, dense=True, class_='pa-0 ma-0',
children=[v.Icon(color='#aaaaaa', children=['mdi-palette-outline'])])
name = v.CardSubtitle(class_="pa-0 ma-0 ml-1 mt-2 mb-2", children=[layer['name']])
spacer = v.Html(tag='div',children=[' '], style_='width: 3px; height: 44px;')
return [ v.Row(class_="pa-0 ma-0", no_gutters=True,
children=[tooltip.tooltip('Hide/Show the layer', visible.draw()),
tooltip.tooltip('Layer colors',bcolor),
spacer,
tooltip.tooltip('Layer opacity',m),
spacer,
name]) ]
# Returns the vuetify object to display (the treeview widget)
[docs] def draw(self):
"""Returns the ipyvuetify object to display (the internal card containing the widgets)"""
#return widgets.VBox([self.outservice, self.s.draw()])
return widgets.VBox([self.outservice, self.s.draw(), self.debug])