"""Utility functions for the creation of interactive maps using 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 pandas as pd
import numpy as np
import json
import random
from pathlib import Path
from ipywidgets import widgets, Layout, HTML, Label
from ipyleaflet import Map, basemaps, GeoJSON, Popup, SearchControl, WidgetControl, LegendControl, ScaleControl, FullScreenControl
try:
from . import colors
from . import geojsonUtils
except:
import colors
import geojsonUtils
###########################################################################################################################################################################
# Simplified way to create a vector layer displaying the countries of the world.
# Vector data is taken from folder data/ne_50m_admin_0_countries.geojson
# Input values to assign to countries come from a Pandas Dataframe df containing a column with iso2code and a column with values
# Returns a Map instance
###########################################################################################################################################################################
[docs]def countriesMap(df, # Pandas dataframe indexed on iso2_code and containing 'value' and 'label' columns
code_column=None, # Name of the column containing the code of the country (None = the country code is the index of the dataframe)
value_column='value', # Name of the column containing the value
codes_selected=[], # codes of the countries selected
center=None, # [lat,lon] to center the map
zoom=None, # initial zoom level for the map
width ='99%', # width of the map
height='400px', # height of the map
min_width=None, # min_width of the map
basemap=basemaps.OpenStreetMap.Mapnik, # Basemap to use
detailedcountries=False, # If True loads the detailed country geojson
colorlist=['#0d0887', '#46039f', '#7201a8', '#9c179e', '#bd3786', '#d8576b', '#ed7953', '#fb9f3a', '#fdca26', '#f0f921'], # default color scale
stdevnumber=2.0, # Number of stddev to calculate (minvalue,maxvalue) range
stroke='#232323', # stroke color for countries border
stroke_selected='#00ffff', # stroke color for border of selected country
stroke_width=3.0, # border width for countries polygons
decimals=2, # Number of decimals for the legend number display
minallowed_value=None, # Minimum value allowed
maxallowed_value=None, # Maximum value allowed
style ={'opacity': 1, 'dashArray': '0', 'fillOpacity': 0.6}, # Style to apply to the features
hover_style={'opacity': 1, 'dashArray': '0', 'fillOpacity': 0.85}): # Style to apply to the features when hover
"""
Creation of an interactive map to display the countries of the world. An input Pandas DataFrame df is used to join a column of numeric values to the countries, using the iso2code (ISO 3166-2) as internal key attribute. Once the values are assigned to the countries, a graduated legend is calculated based on mean and standard deviation of the assigned values. A input list of colors is used to represent the countries given their assigned value.
Parameters
----------
df : Pandas DataFrame
Pandas DataFrame to use for assigning values to the countries. It has to contain at least a column with numeric values.
code_column : str, optional
Name of the column of the Pandas DataFrame containing the unique code of the countries in the ISO-3166-2 standard. This column is used to perform the join with the internal attribute of the countries vector dataset that contains the country code. If the code_column is None, the code is taken from the index of the DataFrame, (default is None)
value_column : str, optional
Name of the column of the Pandas DataFrame containing the values to be assigned to the countries using the join on the ISO-3166-2 codes (default is 'value')
codes_selected : list of strings, optional
List of codes of countries to display as selected (default is [])
center : tuple of (lat,lon), optional
Geographical coordinates of the initial center of the interactive map visualization (default is None)
zoom : int, optional
Initial zoom level of the interactive map (default is None)
width : str, optional
Width of the map widget to create (default is '99%')
height : str, optional
Height of the map widget to create (default is '400px')
min_width : str, optional
Minimum width of the layout of the map widget (default is None)
basemap : instance of basemaps type, optional
Basemap to use as background map (default is basemaps.OpenStreetMap.Mapnik)
detailedcountries : bool, optional
If True loads the more detailed version of the countries dataset (default is False()
colorlist : list of colors, optional
List of colors to assign to the country polygons (default is the Plotly px.colors.sequential.Plasma, see `Plotly sequential color scales <https://plotly.com/python/builtin-colorscales/#builtin-sequential-color-scales>`_ and `Plotly qualitative color sequences <https://plotly.com/python/discrete-color/#color-sequences-in-plotly-express>`_ )
stdevnumber : float, optional
The correspondance between the values assigned to country polygons and the colors list is done by calculating a range of values [min,max] to linearly map the values to the colors. This range is defined by calculating the mean and standard deviation of the country values and applying this formula [mean - stdevnumber*stddev, mean + stdevnumber*stddev]. Default is 2.0
stroke : str, optional
Color to use for the border of countries (default is '#232323')
stroke_selected : str, optional
Color to use for the border of the selected countries (default is '#00ffff')
stroke_width: float, optional
Width of the border of the country polygons in pixels (default is 3.0)
decimals : int, optional
Number of decimals for the legend numbers display (default is 2)
minallowed_value : float, optional
Minimum value allowed, to force the calculation of the [min,max] range to map the values to the colors
maxallowed_value : float, optional
Maximum value allowed, to force the calculation of the [min,max] range to map the values to the colors
style : dict, optional
Style to apply to the features (default is {'opacity': 1, 'dashArray': '0', 'fillOpacity': 0.6})
hover_style : dict, optional
Style to apply to the features when hover (default is {'opacity': 1, 'dashArray': '0', 'fillOpacity': 0.85})
Returns
-------
a ipyleaflet.Map instance
Example
-------
Creation of a map displaying a random variable on 4 european countries. The numerical values assigned to each of the countries are randomly generated using numpy.random.uniform and saved into a dictionary having the country code as the key. This dict is transformed to a Pandas DataFrame with 4 rows and having 'iso2code' and 'value' as columns. The graduated legend is build using the 'inverted' Reds Plotly colorscale (low values are dark red, intermediate values are red, high values are white)::
import numpy as np
import pandas as pd
import plotly.express as px
from ipyleaflet import basemaps
from vois import leafletMap
countries = ['DE', 'ES', 'FR', 'IT']
# Generate random values and create a dictionary: key=countrycode, value=random in [0.0,100.0]
d = dict(zip(countries, list(np.random.uniform(size=len(countries),low=0.0,high=100.0))))
# Create a pandas dataframe from the dictionary
df = pd.DataFrame(d.items(), columns=['iso2code', 'value'])
m = leafletMap.countriesMap(df,
code_column='iso2code',
height='400px',
stroke_width=2.0,
stroke_selected='yellow',
basemap=basemaps.Stamen.Terrain,
colorlist=px.colors.sequential.Reds[::-1],
codes_selected=['IT'],
center=[43,12], zoom=5)
display(m)
.. figure:: figures/leafletCountries.png
:scale: 100 %
:alt: leafletCountries example
Example of an ipyleaflet Map displaying 4 european countries.
"""
if df.shape[0] <= 0:
minvalue = 1.0
maxvalue = 2.0
else:
mean = df[value_column].mean()
if df.shape[0] <= 1:
minvalue = mean
maxvalue = mean
else:
stddev = df[value_column].std()
valuemin = df[value_column].min()
valuemax = df[value_column].max()
minvalue = mean - stdevnumber*stddev
maxvalue = mean + stdevnumber*stddev
if minvalue < valuemin: minvalue = valuemin
if maxvalue > valuemax: maxvalue = valuemax
if not minallowed_value is None:
if minvalue < minallowed_value: minvalue = minallowed_value
if not maxallowed_value is None:
if maxvalue > maxallowed_value: maxvalue = maxallowed_value
#print(mean,stddev, minvalue, maxvalue)
if minvalue >= maxvalue: maxvalue = minvalue + 1
ci = colors.colorInterpolator(colorlist,minvalue,maxvalue)
# Assign colors for a feature
def interpolate_color(feature):
code = feature['properties']['ISO_A2_EH']
value = feature['properties']['value']
if code in codes_selected:
return { 'color': stroke_selected, 'weight': stroke_width+2, 'fillColor': ci.GetColor(value) }
else:
return { 'color': stroke, 'weight': stroke_width, 'fillColor': ci.GetColor(value) }
# Creation of the Map
m = Map(layout=Layout(width=width, height=height), scroll_wheel_zoom=True, basemap=basemap)
if not min_width is None:
m.layout.min_width = min_width
if not center is None:
m.center = center
if not zoom is None:
m.zoom = zoom
# Add map controls
m.add_control(FullScreenControl(position="topleft"))
m.add_control(SearchControl(position="topleft",url='https://nominatim.openstreetmap.org/search?format=json&q={s}',zoom=12))
m.add_control(ScaleControl(position='bottomright'))
poslabel = widgets.Label(value='')
widget_coordinate = WidgetControl(widget=poslabel, position='topright')
m.add_control(widget_coordinate)
lat = lon = 0
def handle_interaction(**kwargs):
nonlocal lat,lon
if kwargs.get('type') == 'mousemove':
lat = kwargs.get('coordinates')[0]
lon = kwargs.get('coordinates')[1]
poslabel.value = '{:.{prec}f}'.format(lat, prec=4) + ' - ' + '{:.{prec}f}'.format(lon, prec=4)
m.on_interaction(handle_interaction)
# Manage click on a feature
def click_on_a_feature(*args, **kvargs):
event = kvargs['event']
feature = kvargs['feature']
code = feature['properties']['ISO_A2_EH']
name = feature['properties']['NAME']
value = feature['properties']['value']
val = '{:.{prec}f}'.format(value, prec=decimals)
s = name + ': ' + val
pos = [lat,lon]
message = widgets.HTML()
message.value = "<style> p.small {line-height: 1.2; }</style><p class=\"small\">" + s + "</p>"
popup = Popup(location=pos,child=message, close_button=True,auto_close=True,close_on_escape_key=True)
m.add_layer(popup)
# Add countries (taken from the setupfolder/data )
path = Path(geojsonUtils.__file__)
datafolder = str(path.parent.absolute()) + '/data'
filepath = datafolder + '/ne_110m_admin_0_countries.geojson'
if detailedcountries:
filepath = datafolder + '/ne_50m_admin_0_countries.geojson'
geojsonstr = geojsonUtils.geojsonLoadFile(filepath)
countries = [str(x) for x in list(df[code_column])]
values = list(df[value_column])
d = dict(zip(countries,values))
geojsonnew = geojsonUtils.geojsonJoin(geojsonstr, 'ISO_A2_EH', 'value', d, innerMode=True)
data = json.loads(geojsonnew)
geo_json = GeoJSON(data=data, style=style, hover_style=hover_style, style_callback=interpolate_color)
geo_json.on_click(click_on_a_feature)
m.add_layer(geo_json)
return m
###########################################################################################################################################################################
# Simplified way to create a vector layer displaying a custom geojson.
# Input values to assign to features come from a Pandas Dataframe df containing a column with unique code and a column with values
# Returns a Map instance
###########################################################################################################################################################################
[docs]def geojsonMap(df, # Pandas dataframe containing 'value' and 'label' columns
geojson_path, # Path of the geojson containing the geographic features
geojson_attribute, # Name of the attribute of the geojson containing the unique code
code_column=None, # Name of the column containing the code of the features (None = the feature code is the index of the dataframe)
value_column='value', # Name of the column containing the value
codes_selected=[], # codes of the features selected
center=None, # [lat,lon] to center the map
zoom=None, # initial zoom level for the map
width ='99%', # width of the map
height='400px', # height of the map
min_width=None, # min_width of the map
basemap=basemaps.OpenStreetMap.Mapnik, # Basemap to use
colorlist=['#0d0887', '#46039f', '#7201a8', '#9c179e', '#bd3786', '#d8576b', '#ed7953', '#fb9f3a', '#fdca26', '#f0f921'], # default color scale
stdevnumber=2.0, # Number of stddev to calculate (minvalue,maxvalue) range
stroke='#232323', # stroke color for polygons border
stroke_selected='#00ffff', # stroke color for border of selected polygons
stroke_width=3.0, # border width for polygons
decimals=2, # Number of decimals for the legend number display
minallowed_value=None, # Minimum value allowed
maxallowed_value=None, # Maximum value allowed
style ={'opacity': 1, 'dashArray': '0', 'fillOpacity': 0.6}, # Style to apply to the features
hover_style={'opacity': 1, 'dashArray': '0', 'fillOpacity': 0.85}): # Style to apply to the features when hover
"""
Creation of an interactive map to display a custom geojson dataset. An input Pandas DataFrame df is used to join a column of numeric values to the geojson features, using the <geojson_attribute> as the internal key attribute. Once the values are assigned to the features, a graduated legend is calculated based on mean and standard deviation of the assigned values. A input list of colors is used to represent the featuress given their assigned value.
Parameters
----------
df : Pandas DataFrame
Pandas DataFrame to use for assigning values to features. It has to contain at least a column with numeric values.
geojson_path : str
Path of the geojson file to load that contains the geographic features in geojson format
geojson_attribute : str
Name of the attribute of the geojson dataset that contains the unique codes of the features. This attribute will be use as internal key in the join operation with the df Pandas DataFrame
code_column : str, optional
Name of the column of the df Pandas DataFrame containing the unique code of the features. This column is used to perform the join with the internal attribute of the geojson vector dataset that contains the unique code. If the code_column is None, the code is taken from the index of the DataFrame, (default is None)
value_column : str, optional
Name of the column of the Pandas DataFrame containing the values to be assigned to the features using the join on geojson unique codes (default is 'value')
codes_selected : list of strings, optional
List of codes of features to display as selected (default is [])
center : tuple of (lat,lon), optional
Geographical coordinates of the initial center of the interactive map visualization (default is None)
zoom : int, optional
Initial zoom level of the interactive map (default is None)
width : str, optional
Width of the map widget to create (default is '99%')
height : str, optional
Height of the map widget to create (default is '400px')
min_width : str, optional
Minimum width of the layout of the map widget (default is None)
basemap : basemap instance, optional
Basemap to use as background in the map visualization (default is basemaps.OpenStreetMap.Mapnik). See `Documentation of ipyleaflet <https://ipyleaflet.readthedocs.io/en/latest/map_and_basemaps/basemaps.html>`_ for details
colorlist : list of colors, optional
List of colors to assign to the country polygons (default is the Plotly px.colors.sequential.Plasma, see `Plotly sequential color scales <https://plotly.com/python/builtin-colorscales/#builtin-sequential-color-scales>`_ and `Plotly qualitative color sequences <https://plotly.com/python/discrete-color/#color-sequences-in-plotly-express>`_ )
stdevnumber : float, optional
The correspondance between the values assigned to features and the colors list is done by calculating a range of values [min,max] to linearly map the values to the colors. This range is defined by calculating the mean and standard deviation of the country values and applying this formula [mean - stdevnumber*stddev, mean + stdevnumber*stddev]. Default is 2.0
stroke : str, optional
Color to use for the border of polygons (default is '#232323')
stroke_selected : str, optional
Color to use for the border of the selected polygons (default is '#00ffff')
stroke_width: float, optional
Width of the border of the polygons in pixels (default is 3.0)
decimals : int, optional
Number of decimals for the legend numbers display (default is 2)
minallowed_value : float, optional
Minimum value allowed, to force the calculation of the [min,max] range to map the values to the colors
maxallowed_value : float, optional
Maximum value allowed, to force the calculation of the [min,max] range to map the values to the colors
style : dict, optional
Style to apply to the features (default is {'opacity': 1, 'dashArray': '0', 'fillOpacity': 0.6})
hover_style : dict, optional
Style to apply to the features when hover (default is {'opacity': 1, 'dashArray': '0', 'fillOpacity': 0.85})
Returns
-------
a ipyleaflet.Map instance
Example
-------
Creation of a map displaying a custom geojson. The numerical values assigned to each of the countries are randomly generated using numpy.random.uniform and saved into a dictionary having the country code as the key. This dict is transformed to a Pandas DataFrame with 4 rows and having 'iso2code' and 'value' as columns. The graduated legend is build using the 'inverted' Viridis Plotly colorscale::
import numpy as np
import pandas as pd
import plotly.express as px
from vois import leafletMap
countries = ['DE', 'ES', 'FR', 'IT']
# Generate random values and create a dictionary: key=countrycode, value=random in [0.0,100.0]
d = dict(zip(countries, list(np.random.uniform(size=len(countries),low=0.0,high=100.0))))
# Create a pandas dataframe from the dictionary
df = pd.DataFrame(d.items(), columns=['iso2code', 'value'])
m = leafletMap.geojsonMap(df,
'./data/ne_50m_admin_0_countries.geojson',
'ISO_A2_EH', # Internal attribute used as key
code_column='iso2code',
height='400px',
stroke_width=1.5,
stroke_selected='yellow',
colorlist=px.colors.sequential.Viridis[::-1],
codes_selected=['IT'],
center=[43,12], zoom=5)
display(m)
.. figure:: figures/leafletMap.png
:scale: 100 %
:alt: leafletMap example
Example of an ipyleaflet Map displaying 4 european countries from a custom geojson file.
"""
if df.shape[0] <= 0:
minvalue = 1.0
maxvalue = 2.0
else:
mean = df[value_column].mean()
if df.shape[0] <= 1:
minvalue = mean
maxvalue = mean
else:
stddev = df[value_column].std()
valuemin = df[value_column].min()
valuemax = df[value_column].max()
minvalue = mean - stdevnumber*stddev
maxvalue = mean + stdevnumber*stddev
if minvalue < valuemin: minvalue = valuemin
if maxvalue > valuemax: maxvalue = valuemax
if not minallowed_value is None:
if minvalue < minallowed_value: minvalue = minallowed_value
if not maxallowed_value is None:
if maxvalue > maxallowed_value: maxvalue = maxallowed_value
#print(mean,stddev, minvalue, maxvalue)
if minvalue >= maxvalue: maxvalue = minvalue + 1
ci = colors.colorInterpolator(colorlist,minvalue,maxvalue)
# Assign colors for a feature
def interpolate_color(feature):
code = feature['properties'][geojson_attribute]
value = feature['properties']['value']
if code in codes_selected:
return { 'color': stroke_selected, 'weight': stroke_width+2, 'fillColor': ci.GetColor(value) }
else:
return { 'color': stroke, 'weight': stroke_width, 'fillColor': ci.GetColor(value) }
# Creation of the Map
m = Map(layout=Layout(width=width, height=height), scroll_wheel_zoom=True, basemap=basemap)
if not min_width is None:
m.layout.min_width = min_width
if not center is None:
m.center = center
if not zoom is None:
m.zoom = zoom
# Add map controls
m.add_control(FullScreenControl(position="topleft"))
m.add_control(SearchControl(position="topleft",url='https://nominatim.openstreetmap.org/search?format=json&q={s}',zoom=12))
m.add_control(ScaleControl(position='bottomright'))
poslabel = widgets.HTML(value='')
widget_coordinate = WidgetControl(widget=poslabel, position='topright')
m.add_control(widget_coordinate)
lat = lon = 0
def handle_interaction(**kwargs):
nonlocal lat,lon
if kwargs.get('type') == 'mousemove':
lat = kwargs.get('coordinates')[0]
lon = kwargs.get('coordinates')[1]
poslabel.value = '{:.{prec}f}'.format(lat, prec=4) + ' - ' + '{:.{prec}f}'.format(lon, prec=4)
m.on_interaction(handle_interaction)
# Manage click on a feature
def click_on_a_feature(*args, **kvargs):
event = kvargs['event']
feature = kvargs['feature']
code = feature['properties'][geojson_attribute]
value = feature['properties']['value']
val = '{:.{prec}f}'.format(value, prec=decimals)
s = code + ': ' + val
pos = [lat,lon]
message = widgets.HTML()
message.value = "<style> p.small {line-height: 1.2; }</style><p class=\"small\">" + s + "</p>"
popup = Popup(location=pos,child=message, close_button=True,auto_close=True,close_on_escape_key=True)
m.add_layer(popup)
# Add layer
geojsonstr = geojsonUtils.geojsonLoadFile(geojson_path)
countries = [str(x) for x in list(df[code_column])]
values = list(df[value_column])
d = dict(zip(countries,values))
geojsonnew = geojsonUtils.geojsonJoin(geojsonstr, geojson_attribute, 'value', d, innerMode=True)
data = json.loads(geojsonnew)
geo_json = GeoJSON(data=data, style=style, hover_style=hover_style, style_callback=interpolate_color)
geo_json.on_click(click_on_a_feature)
m.add_layer(geo_json)
return m
###########################################################################################################################################################################
# Simplified way to create a vector layer displaying a custom geojson with a categorical map on an attribute of the geojson
# Returns a Map instance
###########################################################################################################################################################################
[docs]def geojsonCategoricalMap(geojson_path, # Path of the geojson containing the geographic features
geojson_attribute, # Name of the attribute of the geojson containing the attribute to use for assigning the colors
center=None, # [lat,lon] to center the map
zoom=None, # initial zoom level for the map
width ='99%', # width of the map
height='400px', # height of the map
min_width=None, # min_width of the map
basemap=basemaps.OpenStreetMap.Mapnik, # Basemap to use
colormap={}, # dictionary containing geojson_attribute values as keys and colors as values
stroke='#232323', # stroke color for polygons border
stroke_width=3.0, # border width for polygons
fill='#aaaaaa', # default fill color for polygons
style ={'opacity': 1, 'dashArray': '0', 'fillOpacity': 0.6}, # Style to apply to the features
hover_style={'opacity': 1, 'dashArray': '0', 'fillOpacity': 0.85}): # Style to apply to the features when hover
"""
Creation of an interactive map to display a custom geojson dataset where colors are assigned to feature based on the values of an internal attribute of the input geojson file. The colormap parameter is a dictionary with keys corresponding to all the unique values of the internal attribute, which are mapped to the colors to use for representing each class.
Parameters
----------
geojson_path : str
Path of the geojson file to load that contains the geographic features in geojson format
geojson_attribute : str
Name of the attribute of the geojson dataset that contains the thematisatio attribute of the features. This attribute will be used to retrieve the colors to assign to the features using the colormap parameter
center : tuple of (lat,lon), optional
Geographical coordinates of the initial center of the interactive map visualization (default is None)
zoom : int, optional
Initial zoom level of the interactive map (default is None)
width : str, optional
Width of the map widget to create (default is '99%')
height : str, optional
Height of the map widget to create (default is '400px')
min_width : str, optional
Minimum width of the layout of the map widget (default is None)
basemap : basemap instance, optional
Basemap to use as background in the map visualization (default is basemaps.OpenStreetMap.Mapnik). See `Documentation of ipyleaflet <https://ipyleaflet.readthedocs.io/en/latest/map_and_basemaps/basemaps.html>`_ for details
colormap : dictionary containing geojson_attribute values as keys and colors as values
Colors to assign to each distinct value of the geojson_attribute
stroke : str, optional
Color to use for the border of polygons (default is '#232323')
stroke_width: float, optional
Width of the border of the polygons in pixels (default is 3.0)
fill : str, optional
Default fill color to use for the polygons (default is '#aaaaaa')
style : dict, optional
Style to apply to the features (default is {'opacity': 1, 'dashArray': '0', 'fillOpacity': 0.6})
hover_style : dict, optional
Style to apply to the features when hover (default is {'opacity': 1, 'dashArray': '0', 'fillOpacity': 0.85})
Returns
-------
a ipyleaflet.Map instance
Example
-------
Creation of a map displaying a custom geojson with data on landuse. The colors are assigned to the polygons based on the values of the 'fclass' attribute of the input geojson file. The colormap parameter is a dictionary with keys corresponding to all the unique landuse classes, which are mapped to the colors of a Plotly discrete color scale (see `Color Sequences in Plotly Express <https://plotly.com/python/discrete-color/#color-sequences-in-plotly-express>`_)::
import plotly.express as px
from IPython.display import display
from ipywidgets import widgets, Layout
from vois import leafletMap, svgUtils, geojsonUtils
# Load landuse example and get unique landuse classes
filepath = './data/landuse.geojson'
geojson = geojsonUtils.geojsonLoadFile(filepath)
landuses = sorted(list(set(geojsonUtils.geojsonAll(geojson,'fclass'))))
# Create a colormap (dictionary that maps landuses to colors)
colors = px.colors.qualitative.Dark24
colormap = dict(zip(landuses, colors))
m = leafletMap.geojsonCategoricalMap(filepath,
'fclass',
stroke_width=1.0,
stroke='black',
colormap=colormap,
width='79%',
height='700px',
center=[51.005,13.6],
zoom=12,
basemap=basemaps.CartoDB.Positron,
style={'opacity': 1, 'dashArray': '0', 'fillOpacity': 1})
outlegend = widgets.Output(layout=Layout(width='230px',height='680px'))
with outlegend:
display(HTML(svgUtils.categoriesLegend("Landuse legend",
landuses,
colorlist=colors[:len(landuses)])))
widgets.HBox([m,outlegend])
.. figure:: figures/leafletMapCat.png
:scale: 100 %
:alt: leafletMap example
Example of an ipyleaflet Map displaying 4 european countries from a custom geojson file.
"""
# Assign colors for a feature
def get_color(feature):
code = feature['properties'][geojson_attribute]
if code in colormap:
return { 'color': stroke, 'weight': stroke_width, 'fillColor': colormap[code] }
else:
return { 'color': stroke, 'weight': stroke_width, 'fillColor': fill }
# Creation of the Map
m = Map(layout=Layout(width=width, height=height), scroll_wheel_zoom=True, basemap=basemap)
if not min_width is None:
m.layout.min_width = min_width
if not center is None:
m.center = center
if not zoom is None:
m.zoom = zoom
# Add map controls
m.add_control(FullScreenControl(position="topleft"))
m.add_control(SearchControl(position="topleft",url='https://nominatim.openstreetmap.org/search?format=json&q={s}',zoom=12))
m.add_control(ScaleControl(position='bottomright'))
poslabel = widgets.HTML(value='')
widget_coordinate = WidgetControl(widget=poslabel, position='topright')
m.add_control(widget_coordinate)
lat = lon = 0
def handle_interaction(**kwargs):
nonlocal lat,lon
if kwargs.get('type') == 'mousemove':
lat = kwargs.get('coordinates')[0]
lon = kwargs.get('coordinates')[1]
poslabel.value = '{:.{prec}f}'.format(lat, prec=4) + ' - ' + '{:.{prec}f}'.format(lon, prec=4)
m.on_interaction(handle_interaction)
# Manage click on a feature
def click_on_a_feature(*args, **kvargs):
event = kvargs['event']
feature = kvargs['feature']
code = feature['properties'][geojson_attribute]
pos = [lat,lon]
message = widgets.HTML()
message.value = "<style> p.small {line-height: 1.2; }</style><p class=\"small\">" + code + "</p>"
popup = Popup(location=pos,child=message, close_button=True,auto_close=True,close_on_escape_key=True)
m.add_layer(popup)
# Add layer
geojsonstr = geojsonUtils.geojsonLoadFile(geojson_path)
data = json.loads(geojsonstr)
geo_json = GeoJSON(data=data, style=style, hover_style=hover_style, style_callback=get_color)
geo_json.on_click(click_on_a_feature)
m.add_layer(geo_json)
return m