Source code for textpopup

"""Map popup widget to display titles and texts in a geographic position on 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.

# Widgets import
import ipyleaflet
from ipyleaflet import AwesomeIcon, DivIcon, Marker

# Python imports
from threading import Timer


#####################################################################################################################################################
# textpopup class
#####################################################################################################################################################
[docs]class textpopup(): """ Widget to vertically display a list of titles and texts strings as a popup on a ipyleaflet Map. Each couple of title and text occupies a row. Parameters ---------- m : instance of ipyleaflet.Map class Map to which the popup has to be added lat : float Latitude where the popup has to be displayed (default is 0.0) lon : float Longitude where the popup has to be displayed (default is 0.0) titles : list of strings Strings to be displayed as title of each row (default is []) texts : list of strings Strings to be displayed as the content of each row (default is []) width : int Width of the popup in pixels (default is 200) height : int Height of the popup in pixel (default is None meaning that the height will be automatically calcolated) autoremovedelay : float Time in seconds for automatic remnoving of the popup (default is 0.0 which disables autoremove) titlesbold : list of strings, optional List of titles whose corresponding texts in the left column should be displayed using bold font (default is []) titlefontsize : int, optional Size in pixel of the font used for the titles (default is 12) textsbold : list of strings, optional List of titles whose corresponding texts in the right column should be displayed using bold font (default is []) textfontsize : int, optional Size in pixel of the font used for the texts (default is 12) titlecolor : str, optional Color to use for the titles (default is 'black') textcolor : str, optional Color to use for the texts (default is 'black') lineheightfactor : float, optional Factor to multiply to the font-size to calculate the height of each row (default is 1.5) Example ------- Creation and display of a widget to display some textual information:: from ipywidgets import widgets, HTML, CallbackDispatcher from ipyleaflet import Map from IPython.display import display from vois import textpopup m = Map(center=[43.66737, 12.5504], scroll_wheel_zoom=True, zoom=13) t = None def handle_interaction_popup(**kwargs): global t if kwargs.get('type') == 'click': lat = kwargs.get('coordinates')[0] lon = kwargs.get('coordinates')[1] textpopup.textpopup.removeAll(m) t = textpopup.textpopup(m, lat=lat, lon=lon, autoremovedelay=5.0, width=340, height=None, titlewidth=70, titles=['Pixel values', 'Class'], texts=['(120,34,189)', 'Woodland and Shrubland (incl. permanent crops)'], titlesbold=[], titlefontsize=11, textsbold=['Pixel'], textfontsize=11, titlecolor='darkgreen', textcolor='darkred') m._interaction_callbacks = CallbackDispatcher() m.on_interaction(handle_interaction_popup) display(m) .. figure:: figures/textpopup.png :scale: 100 % :alt: textpopup widget Map popup widget for displaying textual information. """ def __init__(self, m, lat=0.0, lon=0.0, titles=[], texts=[], width=200, height=None, autoremovedelay=10.0, titlesbold=[], titlefontsize=12, textsbold=[], textfontsize=12, titlewidth=50, titlecolor='black', textcolor='black', lineheightfactor=1.1 ): self.m = m self.autoremovedelay = autoremovedelay lineheight = "line-height: %dpx;"%(int(lineheightfactor*(max(titlefontsize,textfontsize)))) # To ensure vertical center alignment # Autocalc height if height is None: height = max(titlefontsize,textfontsize)*lineheightfactor*1.1*max(len(titles),len(texts)) + 12 margin = 5 width += 2*margin height += margin self.h = '<table border="0" style="border-collapse: collapse; margin-right: %dpx; margin-left: %dpx; margin-top: %dpx; margin-bottom: 0px; display: block; width: %dpx; height: %dpx; overflow-y: auto;"><tbody>'%(margin,margin,margin, width,height) for i, title in enumerate(titles): if i < len(texts): text = texts[i] else: text = '' if title in titlesbold: tdtitle = 'th' else: tdtitle = 'td' if title in textsbold: tdtext = 'th' else: tdtext = 'td' self.h += '''<tr style="border-bottom: 1px solid lightgrey;"> <%s align="center" style="width: %dpx; font-size: %dpx; color: %s; %s">%s</%s> <%s align="center" style="width: %dpx; font-size: %dpx; color: %s; %s">%s</%s> </tr>'''%(tdtitle,titlewidth,titlefontsize,titlecolor,lineheight,title,tdtitle, tdtext,width-titlewidth-4*margin,textfontsize,textcolor,lineheight,text,tdtext) self.h += '</tbody></table>' center = (lat,lon) icon1 = AwesomeIcon(name='', marker_color='white', icon_color='white', spin=False) self.marker1 = Marker(name='textpopup', icon=icon1, location=center) icon2 = DivIcon(html=self.h, icon_anchor=[width/2, height+14], icon_size=[width, height]) self.marker2 = Marker(name='textpopup', location=center, icon=icon2) self.m.add_layer(self.marker1) self.m.add_layer(self.marker2) # Auto-remove after some time if self.autoremovedelay > 0: self.timer = Timer(self.autoremovedelay, self.remove) self.timer.start() # Remove the textpopup from the map def remove(self): if self.autoremovedelay > 0: self.timer.cancel() if self.marker1 in self.m.layers: self.m.remove_layer(self.marker1) if self.marker2 in self.m.layers: self.m.remove_layer(self.marker2) # Remove all textpopups from a map @staticmethod def removeAll(m): for layer in reversed(m.layers): if isinstance(layer, ipyleaflet.leaflet.Marker) and layer.name == 'textpopup': m.remove_layer(layer)