Skip to content
Snippets Groups Projects
Commit 528937cb authored by David Rouquet's avatar David Rouquet
Browse files

few lines added

parent 2dc71ca0
Branches main
No related tags found
No related merge requests found
%% Cell type:code id:13f6dc62-8ea6-482d-9115-707cd6b712fc tags: %% Cell type:code id:13f6dc62-8ea6-482d-9115-707cd6b712fc tags:
``` python ``` python
def get_tl_config(): def get_tl_config():
import socket, errno, os import socket, errno, os
# Find a free port # Find a free port
host = '0.0.0.0' host = '0.0.0.0'
port = 8050 port = 8050
end = 9999 end = 9999
found = False found = False
while not found: while not found:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try: try:
s.bind((host, port)) s.bind((host, port))
found = True found = True
except socket.error as e: except socket.error as e:
if e.errno == errno.EADDRINUSE: if e.errno == errno.EADDRINUSE:
port = port + 1 port = port + 1
if (port > end): if (port > end):
raise "No available APP port" raise "No available APP port"
else: else:
raise e raise e
if (os.getenv('HOST', None) is not None): if (os.getenv('HOST', None) is not None):
proto = os.getenv('PROTO') proto = os.getenv('PROTO')
actualhost = os.getenv('JUPYTER_HOST', os.getenv('VOILA_HOST', "")) actualhost = os.getenv('JUPYTER_HOST', os.getenv('VOILA_HOST', ""))
localport = os.getenv('PORT', 80) localport = os.getenv('PORT', 80)
intermediatehost = os.getenv('HOST', 'localhost') intermediatehost = os.getenv('HOST', 'localhost')
base_path = f'/{actualhost}/app_proxy/{port}/' base_path = f'/{actualhost}/app_proxy/{port}/'
proxified= f'{proto}://{intermediatehost}:{localport}{base_path}' proxified= f'{proto}://{intermediatehost}:{localport}{base_path}'
localurl = f'http://{host}:{port}' localurl = f'http://{host}:{port}'
proxy = f'{localurl}::{proxified}' proxy = f'{localurl}::{proxified}'
return ((proxified, host, port, proxy, base_path)) return ((proxified, host, port, proxy, base_path))
return ((f'http://localhost:{port}', host, port, None, '/')) return ((f'http://localhost:{port}', host, port, None, '/'))
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config()
#print(get_tl_config()) #print(get_tl_config())
``` ```
%% Output %% Output
<IPython.core.display.HTML object> <IPython.core.display.HTML object>
%% Cell type:markdown id:adaf50a1-4fb2-4ed6-bbb2-7b18d2758e6f tags: %% Cell type:markdown id:adaf50a1-4fb2-4ed6-bbb2-7b18d2758e6f tags:
<h1><span style="color:green"> Tetras-Lab for interactive geographical data visualisation</span></h1> <h1><span style="color:green"> Tetras-Lab for interactive geographical data visualisation using Dash Leaflet</span></h1>
In this galery you will learn how to create dashboards using the open source data intelligence platform Tetras-Lab and easily visualize and share them as a web app. In this galery you will learn how to create dashboards using the open source data intelligence platform Tetras-Lab and easily visualize and share them as a web app.
<h2><span style="color:green"> Set up </span></h2> <h2><span style="color:green"> Set up </span></h2>
We will be using the python libraries Dash and Dash-Leaflet, which can be easily added to Tetras-Lab, to create the components to be displayed in our dashboard. We will be using the python libraries Dash and Dash-Leaflet, which can be easily added to Tetras-Lab, to create the components to be displayed in our dashboard.
The following imports are gonna be required : The following imports are gonna be required :
```Python ```Python
from dash import html from dash import html
import dash_leaflet as dl import dash_leaflet as dl
import dash_leaflet.express as dlx import dash_leaflet.express as dlx
from jupyter_dash import JupyterDash as Dash from jupyter_dash import JupyterDash as Dash
from dash_extensions.javascript import arrow_function, assign from dash_extensions.javascript import arrow_function, assign
from dash_extensions.enrich import Output, DashProxy, Input, MultiplexerTransform, State from dash_extensions.enrich import Output, DashProxy, Input, MultiplexerTransform, State
from dash.dependencies import Output, Input from dash.dependencies import Output, Input
from dash import dcc from dash import dcc
import dash_bootstrap_components as dbc import dash_bootstrap_components as dbc
import plotly.express as px import plotly.express as px
``` ```
<h2><span style="color:green"> Basic layout </span></h2> <h2><span style="color:green"> Basic layout </span></h2>
The first part of a Dash application is the layout, first of all we have to initialize our app : The first part of a Dash application is the layout, first of all we have to initialize our app :
```Python ```Python
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config() #This line is mandatory to run Dash apps in Tetras-Lab and has to be put in each cell
app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path)
``` ```
Then we can add the components we want to the app layout, each component works like a HTML element to which we can set a CSS style attribute to manage its size, color, font, etc... Then we can add the components we want to the app layout, each component works like a HTML element to which we can set a CSS style attribute to manage its size, color, font, etc...
Exemple for a dropdown selector, first we declare the component : Exemple for a dropdown selector, first we declare the component :
```Python ```Python
dropdown = dcc.Dropdown( dropdown = dcc.Dropdown(
id='demo-dropdown', id='demo-dropdown',
options=[ options=[
{'label': 'Paris', 'value': 'paris'}, {'label': 'Paris', 'value': 'paris'},
{'label': 'Lyon', 'value': 'lyon'}, {'label': 'Lyon', 'value': 'lyon'},
{'label': 'Marseille', 'value': 'marseille'}, {'label': 'Marseille', 'value': 'marseille'},
{'label': 'Grenoble', 'value': 'grenoble'} {'label': 'Grenoble', 'value': 'grenoble'}
], ],
value='paris' #The value that will be intially selected value='paris' #The value that will be intially selected
) )
``` ```
Then we add it to the layout Then we add it to the layout
```Python ```Python
app.layout = html.Div(dropdown) app.layout = html.Div(dropdown)
``` ```
Finnaly ,to run the app we add : Finnaly ,to run the app we add :
```Python ```Python
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Cell type:code id:ed671aec-502e-458d-87fb-8034a2b2e79e tags: %% Cell type:code id:ed671aec-502e-458d-87fb-8034a2b2e79e tags:
``` python ``` python
from dash import html from dash import html
import dash_leaflet as dl import dash_leaflet as dl
import dash_leaflet.express as dlx import dash_leaflet.express as dlx
from jupyter_dash import JupyterDash as Dash from jupyter_dash import JupyterDash as Dash
from dash_extensions.javascript import arrow_function, assign from dash_extensions.javascript import arrow_function, assign
from dash_extensions.enrich import Output, DashProxy, Input, MultiplexerTransform, State from dash_extensions.enrich import Output, DashProxy, Input, MultiplexerTransform, State
from dash.dependencies import Output, Input from dash.dependencies import Output, Input
from dash import dcc from dash import dcc
import dash_bootstrap_components as dbc import dash_bootstrap_components as dbc
import plotly.express as px import plotly.express as px
app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path)
dropdown = dcc.Dropdown( dropdown = dcc.Dropdown(
id='demo-dropdown', id='demo-dropdown',
options=[ options=[
{'label': 'Paris', 'value': 'paris'}, {'label': 'Paris', 'value': 'paris'},
{'label': 'Lyon', 'value': 'lyon'}, {'label': 'Lyon', 'value': 'lyon'},
{'label': 'Marseille', 'value': 'marseille'}, {'label': 'Marseille', 'value': 'marseille'},
{'label': 'Grenoble', 'value': 'grenoble'} {'label': 'Grenoble', 'value': 'grenoble'}
], ],
value='paris' value='paris'
) )
app.layout = html.Div(dropdown) app.layout = html.Div(dropdown)
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Output %% Output
%% Cell type:markdown id:0d6f8c87-d80e-4520-9415-2a7be591b6b1 tags: %% Cell type:markdown id:0d6f8c87-d80e-4520-9415-2a7be591b6b1 tags:
We will now use the Dash_Leaflet Library to display an interactive map, the component to use is ```dl.Map()``` , without adding anything else, the only thing that will be displayed is a grey square. We will now use the Dash_Leaflet Library to display an interactive map, the component to use is ```dl.Map()``` , without adding anything else, the only thing that will be displayed is a grey square.
We need to draw some map tiles, by adding ``` dl.TileLayer()``` as an argument, Dash Leaflet will add a default map layer based on OpenStreetMap. We need to draw some map tiles, by adding ``` dl.TileLayer()``` as an argument, Dash Leaflet will add a default map layer based on OpenStreetMap.
```Python ```Python
mapComponent = dl.Map(children= mapComponent = dl.Map(children=
[dl.TileLayer()], [dl.TileLayer()],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map' id = 'first_map'
) )
app.layout = html.Div(mapComponent) app.layout = html.Div(mapComponent)
``` ```
We can add attributes to the map such as the center point or the zoom level. We can add attributes to the map such as the center point or the zoom level.
%% Cell type:code id:06e83bd3-ba85-48f4-b3e0-99014887e3d1 tags: %% Cell type:code id:06e83bd3-ba85-48f4-b3e0-99014887e3d1 tags:
``` python ``` python
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config()
app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path)
mapComponent = dl.Map(children= mapComponent = dl.Map(children=
[dl.TileLayer()],style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, [dl.TileLayer()],style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
app.layout = html.Div(mapComponent) app.layout = html.Div(mapComponent)
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Output %% Output
%% Cell type:markdown id:ef8cbf39-d2f4-4426-b76a-8e6dc1311541 tags: %% Cell type:markdown id:ef8cbf39-d2f4-4426-b76a-8e6dc1311541 tags:
Now that we have our map, let's see what we can do with it. Now that we have our map, let's see what we can do with it.
The ```children``` attribute of the map component allows us to add different kinds of layers to the map such as markers, polygons, and many others. The ```children``` attribute of the map component allows us to add different kinds of layers to the map such as markers, polygons, and many others.
```Python ```Python
marker = dl.Marker(position = [46.5,2.25], id ='marker') marker = dl.Marker(position = [46.5,2.25], id ='marker')
polygon = dl.Polygon(positions =[[43,2],[46,2],[46,4],[43,3]], id='polygon') polygon = dl.Polygon(positions =[[43,2],[46,2],[46,4],[43,3]], id='polygon')
mapComponent = dl.Map(children = #When we want our map to have multiple layers, we have to pass them as a list in the 'children' argument mapComponent = dl.Map(children = #When we want our map to have multiple layers, we have to pass them as a list in the 'children' argument
[dl.TileLayer(),marker,polygon], [dl.TileLayer(),marker,polygon],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
``` ```
We assign an id to each component because the callbacks which we will define in the next part will use them to refer to the components we want to use as inputs or outputs. We assign an id to each component because the callbacks which we will define in the next part will use them to refer to the components we want to use as inputs or outputs.
%% Cell type:code id:ad986a0f-ea66-42ee-8e7e-5482ee1ddc8f tags: %% Cell type:code id:ad986a0f-ea66-42ee-8e7e-5482ee1ddc8f tags:
``` python ``` python
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config()
app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path)
marker = dl.Marker(position = [46.5,2.25], id ='marker') marker = dl.Marker(position = [46.5,2.25], id ='marker')
polygon = dl.Polygon(positions =[[43,2],[46,2],[46,4],[43,3]], id='polygon') polygon = dl.Polygon(positions =[[43,2],[46,2],[46,4],[43,3]], id='polygon')
mapComponent = dl.Map(children = mapComponent = dl.Map(children =
[dl.TileLayer(),marker,polygon], [dl.TileLayer(),marker,polygon],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
app.layout = html.Div(children = mapComponent) app.layout = html.Div(children = mapComponent)
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Output %% Output
%% Cell type:markdown id:e1937848-90cb-4021-8ff0-a0488e8131bc tags: %% Cell type:markdown id:e1937848-90cb-4021-8ff0-a0488e8131bc tags:
<h2><span style="color:green">Interactivity using callbacks </span></h2> <h2><span style="color:green">Interactivity using callbacks </span></h2>
Once we have a map and know how to display elements on it, we would like to be able to click on a map component and have its information ( e.g the name of a country ) retrieved outside the map and usable in our program.
A Dash callback is made of 2 parts : A function that will be called when a specified component is updated, and a function decorator which will define the inputs : the variables the app is gonna watch for updates, and the output : the component that's gonna be changed when the function is called. A Dash callback is made of 2 parts : A function that will be called when a specified component is updated, and a function decorator which will define the inputs : the variables the app is gonna watch for updates, and the output : the component that's gonna be changed when the function is called.
```Python ```Python
@app.callback(Output('result','children'),Input('marker','n_clicks')) @app.callback(Output('result','children'),Input('marker','n_clicks'))
``` ```
This decorator means the app will be looking for any updates to the number of times the marker has been clicked, and that the result of the following function will be sent to the component with the 'result' id. This decorator means the app will be looking for any updates to the number of times the marker has been clicked, and that the result of the following function will be sent to the component with the 'result' id.
```Python ```Python
def print_click(n): def print_click(n):
return ('Marker clicks : ' + str(n)) return ('Marker clicks : ' + str(n))
``` ```
The print_click function will be called every time the app detects an update on the 'n_clicks' component of the marker. The print_click function will be called every time the app detects an update on the 'n_clicks' component of the marker.
```Python ```Python
txt_output = html.Div(id='result') txt_output = html.Div(id='result')
app.layout = html.Div(children = (txt_output, mapComponent)) #Just like the map component, to have multiple components in our layout they have to be in a list in the children attribute. app.layout = html.Div(children = (txt_output, mapComponent)) #Just like the map component, to have multiple components in our layout they have to be in a list in the children attribute.
@app.callback(Output('result','children'),Input('marker','n_clicks')) @app.callback(Output('result','children'),Input('marker','n_clicks'))
def print_click(n): def print_click(n):
return ('Marker clicks : ' + str(n)) return ('Marker clicks : ' + str(n))
``` ```
%% Cell type:code id:1d00d6d3-8c1f-4336-83ca-dcb40e408d2d tags: %% Cell type:code id:1d00d6d3-8c1f-4336-83ca-dcb40e408d2d tags:
``` python ``` python
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config()
app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=True, server_url=server_url, requests_pathname_prefix=base_path)
marker = dl.Marker(position = [46.5,2.25], id ='marker') marker = dl.Marker(position = [46.5,2.25], id ='marker')
txt_output = html.Div(id='result',style={'float': 'left','margin': 'auto','color':'green'}) txt_output = html.Div(id='result',children='Click the marker !', style={'float': 'left','margin': 'auto','color':'green'})
mapComponent = dl.Map(children = mapComponent = dl.Map(children =
[dl.TileLayer(),marker], [dl.TileLayer(),marker],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
app.layout = html.Div(children = (txt_output,mapComponent )) app.layout = html.Div(children = (txt_output,mapComponent ))
@app.callback(Output('result','children'),Input('marker','n_clicks')) @app.callback(Output('result','children'),Input('marker','n_clicks'))
def print_click(n): def print_click(n):
return ('Marker clicks : ' + str(n)) return ('Marker clicks : ' + str(n))
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Output %% Output
%% Cell type:markdown id:6bcdc84b-d5e4-4ff5-973c-c5070539275c tags: %% Cell type:markdown id:6bcdc84b-d5e4-4ff5-973c-c5070539275c tags:
A callback may have as many inputs and outputs as needed, the linked function will be called when any one of the inputs is updated. A callback may have as many inputs and outputs as needed, the linked function will be called when any one of the inputs is updated.
But two different callbacks can not have the same output, this is to prevent overlapping issues that could happen if the two callbacks are triggered at the same time. But two different callbacks can not have the same output, this is to prevent overlapping issues that could happen if the two callbacks are triggered at the same time.
The function decorator may also have a third kind of argument : States. Just like inputs they are entry variables to the function, but updating a State will not trigger the callback, their purpose is to give information on the state of the app. The function decorator may also have a third kind of argument : States. Just like inputs they are entry variables to the function, but updating a State will not trigger the callback, their purpose is to give information on the state of the app.
```Python ```Python
polygon = dl.Polygon(positions =[[40,0],[49,0],[46,7],[43,7]], id='polygon') polygon = dl.Polygon(positions =[[40,0],[49,0],[46,7],[43,7]], id='polygon')
colorselector = dcc.Dropdown( colorselector = dcc.Dropdown(
id='color-dropdown', id='color-dropdown',
options=[ options=[
{'label': 'Red', 'value': 'red'}, {'label': 'Red', 'value': 'red'},
{'label': 'Green', 'value': 'green'}, {'label': 'Green', 'value': 'green'},
{'label': 'Yellow', 'value': 'yellow'} {'label': 'Yellow', 'value': 'yellow'}
], ],
value='red' value='red'
) )
button = html.Button(id='input_button', children='Change color',style={'float':'left','width':'10%','height':'7vh'}) button = html.Button(id='input_button', children='Change color',style={'float':'left','width':'10%','height':'7vh'})
mapComponent = dl.Map(children = mapComponent = dl.Map(children =
[dl.TileLayer(),polygon], [dl.TileLayer(),polygon],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
app.layout = html.Div(children = (colorselector,button, mapComponent)) app.layout = html.Div(children = (colorselector,button, mapComponent))
@app.callback(Output('polygon','fillColor'),Input('input_button','n_clicks'),State('color-dropdown','value')) @app.callback(Output('polygon','fillColor'),Input('input_button','n_clicks'),State('color-dropdown','value'))
def poly_color(click,color): def poly_color(click,color):
if click is not None: if click is not None:
return color return color
``` ```
Here we choose a color for the polygon with the selector, its values is a State, but the change only happens once the button is clicked because it is the input. Here we choose a color for the polygon with the selector, its values is a State, but the change only happens once the button is clicked because it is the input.
%% Cell type:code id:d1bc382b-d86a-425b-96c9-53bbb0291267 tags: %% Cell type:code id:d1bc382b-d86a-425b-96c9-53bbb0291267 tags:
``` python ``` python
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config()
app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path)
polygon = dl.Polygon(positions =[[40,0],[49,0],[49,7],[40,7]], id='polygon') polygon = dl.Polygon(positions =[[40,0],[49,0],[49,7],[40,7]], id='polygon')
colorselector = dcc.Dropdown( colorselector = dcc.Dropdown(
id='color-dropdown', id='color-dropdown',
options=[ options=[
{'label': 'Red', 'value': 'red'}, {'label': 'Red', 'value': 'red'},
{'label': 'Green', 'value': 'green'}, {'label': 'Green', 'value': 'green'},
{'label': 'Yellow', 'value': 'yellow'} {'label': 'Yellow', 'value': 'yellow'}
], ],
value='red' value='red'
) )
button = html.Button(id='input_button', children='Change color',style={'float':'left','width':'10%','height':'7vh'}) button = html.Button(id='input_button', children='Change color',style={'float':'left','width':'10%','height':'7vh'})
mapComponent = dl.Map(children = mapComponent = dl.Map(children =
[dl.TileLayer(),polygon], [dl.TileLayer(),polygon],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
app.layout = html.Div(children = (colorselector,button, mapComponent )) app.layout = html.Div(children = (colorselector,button, mapComponent ))
@app.callback(Output('polygon','fillColor'),Input('input_button','n_clicks'),State('color-dropdown','value')) @app.callback(Output('polygon','fillColor'),Input('input_button','n_clicks'),State('color-dropdown','value'))
def poly_color(click,color): def poly_color(click,color):
if click is not None: if click is not None:
return color return color
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Output %% Output
%% Cell type:markdown id:5f7e56c9-e08f-47f1-907d-43dccad26996 tags: %% Cell type:markdown id:5f7e56c9-e08f-47f1-907d-43dccad26996 tags:
<h2><span style="color:green">GeoJSON rendering </span></h2> <h2><span style="color:green">GeoJSON rendering </span></h2>
The Dash_Leaflet library offers the possibility to create map layers based on GeoJSON files. The Dash_Leaflet library offers the possibility to create map layers based on GeoJSON files.
`dl.GeoJSON` works just like other layers we have seen before. We can give the layer an id and use its different attributes inside callbacks. `dl.GeoJSON` works just like other layers we have seen before. We can give the layer an id and use its different attributes inside callbacks.
Here with 2 different GeoJSON files : departements.geojson for France's departments and regions.geojson for France's regions we can create a toggle which will make the geojson layer display either the regions or the departements. Here with 2 different GeoJSON files : departements.geojson for France's departments and regions.geojson for France's regions we can create a toggle which will make the geojson layer display either the regions or the departements.
```Python ```Python
geojson = dl.GeoJSON(url=base_path+'assets/regions.geojson', # url to geojson file geojson = dl.GeoJSON(url=base_path+'assets/regions.geojson', # url to geojson file
zoomToBounds=True, # when true, zooms to bounds when data changes (e.g. on load) zoomToBounds=True, # when true, zooms to bounds when data changes (e.g. on load)
zoomToBoundsOnClick=True, # when true, zooms to bounds of feature (e.g. polygon) on click zoomToBoundsOnClick=True, # when true, zooms to bounds of feature (e.g. polygon) on click
id="geojson-layer" id="geojson-layer"
) )
radioButtons = dcc.RadioItems( radioButtons = dcc.RadioItems(
options=[ options=[
{'label': 'Regions', 'value': 'region'}, {'label': 'Regions', 'value': 'region'},
{'label': 'Departements', 'value': 'departement'} {'label': 'Departements', 'value': 'departement'}
], ],
value='regions', value='regions',
id='radio-buttons', id='radio-buttons',
style ={'float':'center'} style ={'float':'center'}
) )
mapComponent = dl.Map(children = mapComponent = dl.Map(children =
[dl.TileLayer(),geojson], [dl.TileLayer(),geojson],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
@app.callback(Output('geojson-layer','url'),Input('radio-buttons','value')) @app.callback(Output('geojson-layer','url'),Input('radio-buttons','value'))
def switch_file(value): def switch_file(value):
return (base_path+'assets/' + value + 's.geojson') return (base_path+'assets/' + value + 's.geojson')
``` ```
So the GeoJSON layer works as a callback output, but it can also work as an input. So the GeoJSON layer works as a callback output, but it can also work as an input.
Each polygon drawn in the layer has different features such as the `click_feature` which updates when any of the polygon is clicked and allows us to get informations on it. Each polygon drawn in the layer has different features such as the `click_feature` which updates when any of the polygon is clicked and allows us to get informations on it.
```Python ```Python
txt_output = html.Div(id='result',style={'float': 'left','margin': 'auto','color':'red'}) txt_output = html.Div(id='result',style={'float': 'left','margin': 'auto','color':'red'})
@app.callback(Output('result','children'),Input('geojson-layer','click_feature'),State('radio-buttons','value')) @app.callback(Output('result','children'),Input('geojson-layer','click_feature'),State('radio-buttons','value'))
def get_name(feature,value): def get_name(feature,value):
return (value + ' : ' + feature['properties']['nom']) return (value + ' : ' + feature['properties']['nom'])
app.layout = html.Div(children = (radioButtons, mapComponent, txt_output)) app.layout = html.Div(children = (radioButtons, mapComponent, txt_output))
``` ```
%% Cell type:code id:72ade10f-9d80-4507-8e54-15d667ae15a9 tags: %% Cell type:code id:72ade10f-9d80-4507-8e54-15d667ae15a9 tags:
``` python ``` python
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config()
app = Dash(prevent_initial_callbacks=True, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=True, server_url=server_url, requests_pathname_prefix=base_path)
geojson = dl.GeoJSON(url=base_path+'assets/regions.geojson', # url to geojson file geojson = dl.GeoJSON(url=base_path+'assets/regions.geojson', # url to geojson file
zoomToBounds=True, # when true, zooms to bounds when data changes (e.g. on load) zoomToBounds=True, # when true, zooms to bounds when data changes (e.g. on load)
zoomToBoundsOnClick=True, # when true, zooms to bounds of feature (e.g. polygon) on click zoomToBoundsOnClick=True, # when true, zooms to bounds of feature (e.g. polygon) on click
id="geojson-layer" id="geojson-layer"
) )
radioButtons = dcc.RadioItems( radioButtons = dcc.RadioItems(
options=[ options=[
{'label': 'Regions', 'value': 'region'}, {'label': 'Regions', 'value': 'region'},
{'label': 'Departements', 'value': 'departement'} {'label': 'Departements', 'value': 'departement'}
], ],
value='region', value='region',
id='radio-buttons', id='radio-buttons',
style ={'float':'center'} style ={'float':'center'}
) )
txt_output = html.P(id='result',children='Click a polygon !', style={'text-align': 'center','margin': 'auto','color':'green',}) txt_output = html.P(id='result',children='Click a polygon !', style={'text-align': 'center','margin': 'auto','color':'green',})
mapComponent = dl.Map(children = mapComponent = dl.Map(children =
[dl.TileLayer(),geojson], [dl.TileLayer(),geojson],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
@app.callback(Output('geojson-layer','url'),Input('radio-buttons','value')) @app.callback(Output('geojson-layer','url'),Input('radio-buttons','value'))
def switch_file(value): def switch_file(value):
return (base_path+'assets/' + value + 's.geojson') return (base_path+'assets/' + value + 's.geojson')
@app.callback(Output('result','children'),Input('geojson-layer','click_feature'),State('radio-buttons','value')) @app.callback(Output('result','children'),Input('geojson-layer','click_feature'),State('radio-buttons','value'))
def get_name(feature,value): def get_name(feature,value):
return (value + ' : ' + feature['properties']['nom']) return (value + ' : ' + feature['properties']['nom'])
app.layout = html.Div(children = (radioButtons,txt_output,mapComponent)) app.layout = html.Div(children = (radioButtons,txt_output,mapComponent))
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Output %% Output
%% Cell type:code id:6ab4e665-61a5-4338-9251-d4a20439d426 tags: %% Cell type:code id:6ab4e665-61a5-4338-9251-d4a20439d426 tags:
``` python ``` python
``` ```
......
%% Cell type:code id:13f6dc62-8ea6-482d-9115-707cd6b712fc tags: %% Cell type:code id:13f6dc62-8ea6-482d-9115-707cd6b712fc tags:
``` python ``` python
def get_tl_config(): def get_tl_config():
import socket, errno, os import socket, errno, os
# Find a free port # Find a free port
host = '0.0.0.0' host = '0.0.0.0'
port = 8050 port = 8050
end = 9999 end = 9999
found = False found = False
while not found: while not found:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
try: try:
s.bind((host, port)) s.bind((host, port))
found = True found = True
except socket.error as e: except socket.error as e:
if e.errno == errno.EADDRINUSE: if e.errno == errno.EADDRINUSE:
port = port + 1 port = port + 1
if (port > end): if (port > end):
raise "No available APP port" raise "No available APP port"
else: else:
raise e raise e
if (os.getenv('HOST', None) is not None): if (os.getenv('HOST', None) is not None):
proto = os.getenv('PROTO') proto = os.getenv('PROTO')
actualhost = os.getenv('JUPYTER_HOST', os.getenv('VOILA_HOST', "")) actualhost = os.getenv('JUPYTER_HOST', os.getenv('VOILA_HOST', ""))
localport = os.getenv('PORT', 80) localport = os.getenv('PORT', 80)
intermediatehost = os.getenv('HOST', 'localhost') intermediatehost = os.getenv('HOST', 'localhost')
base_path = f'/{actualhost}/app_proxy/{port}/' base_path = f'/{actualhost}/app_proxy/{port}/'
proxified= f'{proto}://{intermediatehost}:{localport}{base_path}' proxified= f'{proto}://{intermediatehost}:{localport}{base_path}'
localurl = f'http://{host}:{port}' localurl = f'http://{host}:{port}'
proxy = f'{localurl}::{proxified}' proxy = f'{localurl}::{proxified}'
return ((proxified, host, port, proxy, base_path)) return ((proxified, host, port, proxy, base_path))
return ((f'http://localhost:{port}', host, port, None, '/')) return ((f'http://localhost:{port}', host, port, None, '/'))
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config()
#print(get_tl_config()) #print(get_tl_config())
``` ```
%% Output %% Output
<IPython.core.display.HTML object> <IPython.core.display.HTML object>
%% Cell type:markdown id:adaf50a1-4fb2-4ed6-bbb2-7b18d2758e6f tags: %% Cell type:markdown id:adaf50a1-4fb2-4ed6-bbb2-7b18d2758e6f tags:
<h1><span style="color:green"> Tetras-Lab for interactive geographical data visualisation</span></h1> <h1><span style="color:green"> Tetras-Lab for interactive geographical data visualisation using Dash Leaflet</span></h1>
In this galery you will learn how to create dashboards using the open source data intelligence platform Tetras-Lab and easily visualize and share them as a web app. In this galery you will learn how to create dashboards using the open source data intelligence platform Tetras-Lab and easily visualize and share them as a web app.
<h2><span style="color:green"> Set up </span></h2> <h2><span style="color:green"> Set up </span></h2>
We will be using the python libraries Dash and Dash-Leaflet, which can be easily added to Tetras-Lab, to create the components to be displayed in our dashboard. We will be using the python libraries Dash and Dash-Leaflet, which can be easily added to Tetras-Lab, to create the components to be displayed in our dashboard.
The following imports are gonna be required : The following imports are gonna be required :
```Python ```Python
from dash import html from dash import html
import dash_leaflet as dl import dash_leaflet as dl
import dash_leaflet.express as dlx import dash_leaflet.express as dlx
from jupyter_dash import JupyterDash as Dash from jupyter_dash import JupyterDash as Dash
from dash_extensions.javascript import arrow_function, assign from dash_extensions.javascript import arrow_function, assign
from dash_extensions.enrich import Output, DashProxy, Input, MultiplexerTransform, State from dash_extensions.enrich import Output, DashProxy, Input, MultiplexerTransform, State
from dash.dependencies import Output, Input from dash.dependencies import Output, Input
from dash import dcc from dash import dcc
import dash_bootstrap_components as dbc import dash_bootstrap_components as dbc
import plotly.express as px import plotly.express as px
``` ```
<h2><span style="color:green"> Basic layout </span></h2> <h2><span style="color:green"> Basic layout </span></h2>
The first part of a Dash application is the layout, first of all we have to initialize our app : The first part of a Dash application is the layout, first of all we have to initialize our app :
```Python ```Python
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config() #This line is mandatory to run Dash apps in Tetras-Lab and has to be put in each cell
app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path)
``` ```
Then we can add the components we want to the app layout, each component works like a HTML element to which we can set a CSS style attribute to manage its size, color, font, etc... Then we can add the components we want to the app layout, each component works like a HTML element to which we can set a CSS style attribute to manage its size, color, font, etc...
Exemple for a dropdown selector, first we declare the component : Exemple for a dropdown selector, first we declare the component :
```Python ```Python
dropdown = dcc.Dropdown( dropdown = dcc.Dropdown(
id='demo-dropdown', id='demo-dropdown',
options=[ options=[
{'label': 'Paris', 'value': 'paris'}, {'label': 'Paris', 'value': 'paris'},
{'label': 'Lyon', 'value': 'lyon'}, {'label': 'Lyon', 'value': 'lyon'},
{'label': 'Marseille', 'value': 'marseille'}, {'label': 'Marseille', 'value': 'marseille'},
{'label': 'Grenoble', 'value': 'grenoble'} {'label': 'Grenoble', 'value': 'grenoble'}
], ],
value='paris' #The value that will be intially selected value='paris' #The value that will be intially selected
) )
``` ```
Then we add it to the layout Then we add it to the layout
```Python ```Python
app.layout = html.Div(dropdown) app.layout = html.Div(dropdown)
``` ```
Finnaly ,to run the app we add : Finnaly ,to run the app we add :
```Python ```Python
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Cell type:code id:ed671aec-502e-458d-87fb-8034a2b2e79e tags: %% Cell type:code id:ed671aec-502e-458d-87fb-8034a2b2e79e tags:
``` python ``` python
from dash import html from dash import html
import dash_leaflet as dl import dash_leaflet as dl
import dash_leaflet.express as dlx import dash_leaflet.express as dlx
from jupyter_dash import JupyterDash as Dash from jupyter_dash import JupyterDash as Dash
from dash_extensions.javascript import arrow_function, assign from dash_extensions.javascript import arrow_function, assign
from dash_extensions.enrich import Output, DashProxy, Input, MultiplexerTransform, State from dash_extensions.enrich import Output, DashProxy, Input, MultiplexerTransform, State
from dash.dependencies import Output, Input from dash.dependencies import Output, Input
from dash import dcc from dash import dcc
import dash_bootstrap_components as dbc import dash_bootstrap_components as dbc
import plotly.express as px import plotly.express as px
app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path)
dropdown = dcc.Dropdown( dropdown = dcc.Dropdown(
id='demo-dropdown', id='demo-dropdown',
options=[ options=[
{'label': 'Paris', 'value': 'paris'}, {'label': 'Paris', 'value': 'paris'},
{'label': 'Lyon', 'value': 'lyon'}, {'label': 'Lyon', 'value': 'lyon'},
{'label': 'Marseille', 'value': 'marseille'}, {'label': 'Marseille', 'value': 'marseille'},
{'label': 'Grenoble', 'value': 'grenoble'} {'label': 'Grenoble', 'value': 'grenoble'}
], ],
value='paris' value='paris'
) )
app.layout = html.Div(dropdown) app.layout = html.Div(dropdown)
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Output %% Output
%% Cell type:markdown id:0d6f8c87-d80e-4520-9415-2a7be591b6b1 tags: %% Cell type:markdown id:0d6f8c87-d80e-4520-9415-2a7be591b6b1 tags:
We will now use the Dash_Leaflet Library to display an interactive map, the component to use is ```dl.Map()``` , without adding anything else, the only thing that will be displayed is a grey square. We will now use the Dash_Leaflet Library to display an interactive map, the component to use is ```dl.Map()``` , without adding anything else, the only thing that will be displayed is a grey square.
We need to draw some map tiles, by adding ``` dl.TileLayer()``` as an argument, Dash Leaflet will add a default map layer based on OpenStreetMap. We need to draw some map tiles, by adding ``` dl.TileLayer()``` as an argument, Dash Leaflet will add a default map layer based on OpenStreetMap.
```Python ```Python
mapComponent = dl.Map(children= mapComponent = dl.Map(children=
[dl.TileLayer()], [dl.TileLayer()],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map' id = 'first_map'
) )
app.layout = html.Div(mapComponent) app.layout = html.Div(mapComponent)
``` ```
We can add attributes to the map such as the center point or the zoom level. We can add attributes to the map such as the center point or the zoom level.
%% Cell type:code id:06e83bd3-ba85-48f4-b3e0-99014887e3d1 tags: %% Cell type:code id:06e83bd3-ba85-48f4-b3e0-99014887e3d1 tags:
``` python ``` python
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config()
app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path)
mapComponent = dl.Map(children= mapComponent = dl.Map(children=
[dl.TileLayer()],style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, [dl.TileLayer()],style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
app.layout = html.Div(mapComponent) app.layout = html.Div(mapComponent)
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Output %% Output
%% Cell type:markdown id:ef8cbf39-d2f4-4426-b76a-8e6dc1311541 tags: %% Cell type:markdown id:ef8cbf39-d2f4-4426-b76a-8e6dc1311541 tags:
Now that we have our map, let's see what we can do with it. Now that we have our map, let's see what we can do with it.
The ```children``` attribute of the map component allows us to add different kinds of layers to the map such as markers, polygons, and many others. The ```children``` attribute of the map component allows us to add different kinds of layers to the map such as markers, polygons, and many others.
```Python ```Python
marker = dl.Marker(position = [46.5,2.25], id ='marker') marker = dl.Marker(position = [46.5,2.25], id ='marker')
polygon = dl.Polygon(positions =[[43,2],[46,2],[46,4],[43,3]], id='polygon') polygon = dl.Polygon(positions =[[43,2],[46,2],[46,4],[43,3]], id='polygon')
mapComponent = dl.Map(children = #When we want our map to have multiple layers, we have to pass them as a list in the 'children' argument mapComponent = dl.Map(children = #When we want our map to have multiple layers, we have to pass them as a list in the 'children' argument
[dl.TileLayer(),marker,polygon], [dl.TileLayer(),marker,polygon],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
``` ```
We assign an id to each component because the callbacks which we will define in the next part will use them to refer to the components we want to use as inputs or outputs. We assign an id to each component because the callbacks which we will define in the next part will use them to refer to the components we want to use as inputs or outputs.
%% Cell type:code id:ad986a0f-ea66-42ee-8e7e-5482ee1ddc8f tags: %% Cell type:code id:ad986a0f-ea66-42ee-8e7e-5482ee1ddc8f tags:
``` python ``` python
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config()
app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path)
marker = dl.Marker(position = [46.5,2.25], id ='marker') marker = dl.Marker(position = [46.5,2.25], id ='marker')
polygon = dl.Polygon(positions =[[43,2],[46,2],[46,4],[43,3]], id='polygon') polygon = dl.Polygon(positions =[[43,2],[46,2],[46,4],[43,3]], id='polygon')
mapComponent = dl.Map(children = mapComponent = dl.Map(children =
[dl.TileLayer(),marker,polygon], [dl.TileLayer(),marker,polygon],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
app.layout = html.Div(children = mapComponent) app.layout = html.Div(children = mapComponent)
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Output %% Output
%% Cell type:markdown id:e1937848-90cb-4021-8ff0-a0488e8131bc tags: %% Cell type:markdown id:e1937848-90cb-4021-8ff0-a0488e8131bc tags:
<h2><span style="color:green">Interactivity using callbacks </span></h2> <h2><span style="color:green">Interactivity using callbacks </span></h2>
Once we have a map and know how to display elements on it, we would like to be able to click on a map component and have its information ( e.g the name of a country ) retrieved outside the map and usable in our program.
A Dash callback is made of 2 parts : A function that will be called when a specified component is updated, and a function decorator which will define the inputs : the variables the app is gonna watch for updates, and the output : the component that's gonna be changed when the function is called. A Dash callback is made of 2 parts : A function that will be called when a specified component is updated, and a function decorator which will define the inputs : the variables the app is gonna watch for updates, and the output : the component that's gonna be changed when the function is called.
```Python ```Python
@app.callback(Output('result','children'),Input('marker','n_clicks')) @app.callback(Output('result','children'),Input('marker','n_clicks'))
``` ```
This decorator means the app will be looking for any updates to the number of times the marker has been clicked, and that the result of the following function will be sent to the component with the 'result' id. This decorator means the app will be looking for any updates to the number of times the marker has been clicked, and that the result of the following function will be sent to the component with the 'result' id.
```Python ```Python
def print_click(n): def print_click(n):
return ('Marker clicks : ' + str(n)) return ('Marker clicks : ' + str(n))
``` ```
The print_click function will be called every time the app detects an update on the 'n_clicks' component of the marker. The print_click function will be called every time the app detects an update on the 'n_clicks' component of the marker.
```Python ```Python
txt_output = html.Div(id='result') txt_output = html.Div(id='result')
app.layout = html.Div(children = (txt_output, mapComponent)) #Just like the map component, to have multiple components in our layout they have to be in a list in the children attribute. app.layout = html.Div(children = (txt_output, mapComponent)) #Just like the map component, to have multiple components in our layout they have to be in a list in the children attribute.
@app.callback(Output('result','children'),Input('marker','n_clicks')) @app.callback(Output('result','children'),Input('marker','n_clicks'))
def print_click(n): def print_click(n):
return ('Marker clicks : ' + str(n)) return ('Marker clicks : ' + str(n))
``` ```
%% Cell type:code id:1d00d6d3-8c1f-4336-83ca-dcb40e408d2d tags: %% Cell type:code id:1d00d6d3-8c1f-4336-83ca-dcb40e408d2d tags:
``` python ``` python
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config()
app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=True, server_url=server_url, requests_pathname_prefix=base_path)
marker = dl.Marker(position = [46.5,2.25], id ='marker') marker = dl.Marker(position = [46.5,2.25], id ='marker')
txt_output = html.Div(id='result',style={'float': 'left','margin': 'auto','color':'green'}) txt_output = html.Div(id='result',children='Click the marker !', style={'float': 'left','margin': 'auto','color':'green'})
mapComponent = dl.Map(children = mapComponent = dl.Map(children =
[dl.TileLayer(),marker], [dl.TileLayer(),marker],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
app.layout = html.Div(children = (txt_output,mapComponent )) app.layout = html.Div(children = (txt_output,mapComponent ))
@app.callback(Output('result','children'),Input('marker','n_clicks')) @app.callback(Output('result','children'),Input('marker','n_clicks'))
def print_click(n): def print_click(n):
return ('Marker clicks : ' + str(n)) return ('Marker clicks : ' + str(n))
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Output %% Output
%% Cell type:markdown id:6bcdc84b-d5e4-4ff5-973c-c5070539275c tags: %% Cell type:markdown id:6bcdc84b-d5e4-4ff5-973c-c5070539275c tags:
A callback may have as many inputs and outputs as needed, the linked function will be called when any one of the inputs is updated. A callback may have as many inputs and outputs as needed, the linked function will be called when any one of the inputs is updated.
But two different callbacks can not have the same output, this is to prevent overlapping issues that could happen if the two callbacks are triggered at the same time. But two different callbacks can not have the same output, this is to prevent overlapping issues that could happen if the two callbacks are triggered at the same time.
The function decorator may also have a third kind of argument : States. Just like inputs they are entry variables to the function, but updating a State will not trigger the callback, their purpose is to give information on the state of the app. The function decorator may also have a third kind of argument : States. Just like inputs they are entry variables to the function, but updating a State will not trigger the callback, their purpose is to give information on the state of the app.
```Python ```Python
polygon = dl.Polygon(positions =[[40,0],[49,0],[46,7],[43,7]], id='polygon') polygon = dl.Polygon(positions =[[40,0],[49,0],[46,7],[43,7]], id='polygon')
colorselector = dcc.Dropdown( colorselector = dcc.Dropdown(
id='color-dropdown', id='color-dropdown',
options=[ options=[
{'label': 'Red', 'value': 'red'}, {'label': 'Red', 'value': 'red'},
{'label': 'Green', 'value': 'green'}, {'label': 'Green', 'value': 'green'},
{'label': 'Yellow', 'value': 'yellow'} {'label': 'Yellow', 'value': 'yellow'}
], ],
value='red' value='red'
) )
button = html.Button(id='input_button', children='Change color',style={'float':'left','width':'10%','height':'7vh'}) button = html.Button(id='input_button', children='Change color',style={'float':'left','width':'10%','height':'7vh'})
mapComponent = dl.Map(children = mapComponent = dl.Map(children =
[dl.TileLayer(),polygon], [dl.TileLayer(),polygon],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
app.layout = html.Div(children = (colorselector,button, mapComponent)) app.layout = html.Div(children = (colorselector,button, mapComponent))
@app.callback(Output('polygon','fillColor'),Input('input_button','n_clicks'),State('color-dropdown','value')) @app.callback(Output('polygon','fillColor'),Input('input_button','n_clicks'),State('color-dropdown','value'))
def poly_color(click,color): def poly_color(click,color):
if click is not None: if click is not None:
return color return color
``` ```
Here we choose a color for the polygon with the selector, its values is a State, but the change only happens once the button is clicked because it is the input. Here we choose a color for the polygon with the selector, its values is a State, but the change only happens once the button is clicked because it is the input.
%% Cell type:code id:d1bc382b-d86a-425b-96c9-53bbb0291267 tags: %% Cell type:code id:d1bc382b-d86a-425b-96c9-53bbb0291267 tags:
``` python ``` python
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config()
app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=False, server_url=server_url, requests_pathname_prefix=base_path)
polygon = dl.Polygon(positions =[[40,0],[49,0],[49,7],[40,7]], id='polygon') polygon = dl.Polygon(positions =[[40,0],[49,0],[49,7],[40,7]], id='polygon')
colorselector = dcc.Dropdown( colorselector = dcc.Dropdown(
id='color-dropdown', id='color-dropdown',
options=[ options=[
{'label': 'Red', 'value': 'red'}, {'label': 'Red', 'value': 'red'},
{'label': 'Green', 'value': 'green'}, {'label': 'Green', 'value': 'green'},
{'label': 'Yellow', 'value': 'yellow'} {'label': 'Yellow', 'value': 'yellow'}
], ],
value='red' value='red'
) )
button = html.Button(id='input_button', children='Change color',style={'float':'left','width':'10%','height':'7vh'}) button = html.Button(id='input_button', children='Change color',style={'float':'left','width':'10%','height':'7vh'})
mapComponent = dl.Map(children = mapComponent = dl.Map(children =
[dl.TileLayer(),polygon], [dl.TileLayer(),polygon],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
app.layout = html.Div(children = (colorselector,button, mapComponent )) app.layout = html.Div(children = (colorselector,button, mapComponent ))
@app.callback(Output('polygon','fillColor'),Input('input_button','n_clicks'),State('color-dropdown','value')) @app.callback(Output('polygon','fillColor'),Input('input_button','n_clicks'),State('color-dropdown','value'))
def poly_color(click,color): def poly_color(click,color):
if click is not None: if click is not None:
return color return color
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Output %% Output
%% Cell type:markdown id:5f7e56c9-e08f-47f1-907d-43dccad26996 tags: %% Cell type:markdown id:5f7e56c9-e08f-47f1-907d-43dccad26996 tags:
<h2><span style="color:green">GeoJSON rendering </span></h2> <h2><span style="color:green">GeoJSON rendering </span></h2>
The Dash_Leaflet library offers the possibility to create map layers based on GeoJSON files. The Dash_Leaflet library offers the possibility to create map layers based on GeoJSON files.
`dl.GeoJSON` works just like other layers we have seen before. We can give the layer an id and use its different attributes inside callbacks. `dl.GeoJSON` works just like other layers we have seen before. We can give the layer an id and use its different attributes inside callbacks.
Here with 2 different GeoJSON files : departements.geojson for France's departments and regions.geojson for France's regions we can create a toggle which will make the geojson layer display either the regions or the departements. Here with 2 different GeoJSON files : departements.geojson for France's departments and regions.geojson for France's regions we can create a toggle which will make the geojson layer display either the regions or the departements.
```Python ```Python
geojson = dl.GeoJSON(url=base_path+'assets/regions.geojson', # url to geojson file geojson = dl.GeoJSON(url=base_path+'assets/regions.geojson', # url to geojson file
zoomToBounds=True, # when true, zooms to bounds when data changes (e.g. on load) zoomToBounds=True, # when true, zooms to bounds when data changes (e.g. on load)
zoomToBoundsOnClick=True, # when true, zooms to bounds of feature (e.g. polygon) on click zoomToBoundsOnClick=True, # when true, zooms to bounds of feature (e.g. polygon) on click
id="geojson-layer" id="geojson-layer"
) )
radioButtons = dcc.RadioItems( radioButtons = dcc.RadioItems(
options=[ options=[
{'label': 'Regions', 'value': 'region'}, {'label': 'Regions', 'value': 'region'},
{'label': 'Departements', 'value': 'departement'} {'label': 'Departements', 'value': 'departement'}
], ],
value='regions', value='regions',
id='radio-buttons', id='radio-buttons',
style ={'float':'center'} style ={'float':'center'}
) )
mapComponent = dl.Map(children = mapComponent = dl.Map(children =
[dl.TileLayer(),geojson], [dl.TileLayer(),geojson],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
@app.callback(Output('geojson-layer','url'),Input('radio-buttons','value')) @app.callback(Output('geojson-layer','url'),Input('radio-buttons','value'))
def switch_file(value): def switch_file(value):
return (base_path+'assets/' + value + 's.geojson') return (base_path+'assets/' + value + 's.geojson')
``` ```
So the GeoJSON layer works as a callback output, but it can also work as an input. So the GeoJSON layer works as a callback output, but it can also work as an input.
Each polygon drawn in the layer has different features such as the `click_feature` which updates when any of the polygon is clicked and allows us to get informations on it. Each polygon drawn in the layer has different features such as the `click_feature` which updates when any of the polygon is clicked and allows us to get informations on it.
```Python ```Python
txt_output = html.Div(id='result',style={'float': 'left','margin': 'auto','color':'red'}) txt_output = html.Div(id='result',style={'float': 'left','margin': 'auto','color':'red'})
@app.callback(Output('result','children'),Input('geojson-layer','click_feature'),State('radio-buttons','value')) @app.callback(Output('result','children'),Input('geojson-layer','click_feature'),State('radio-buttons','value'))
def get_name(feature,value): def get_name(feature,value):
return (value + ' : ' + feature['properties']['nom']) return (value + ' : ' + feature['properties']['nom'])
app.layout = html.Div(children = (radioButtons, mapComponent, txt_output)) app.layout = html.Div(children = (radioButtons, mapComponent, txt_output))
``` ```
%% Cell type:code id:72ade10f-9d80-4507-8e54-15d667ae15a9 tags: %% Cell type:code id:72ade10f-9d80-4507-8e54-15d667ae15a9 tags:
``` python ``` python
server_url, host, port, proxy, base_path = get_tl_config() server_url, host, port, proxy, base_path = get_tl_config()
app = Dash(prevent_initial_callbacks=True, server_url=server_url, requests_pathname_prefix=base_path) app = Dash(prevent_initial_callbacks=True, server_url=server_url, requests_pathname_prefix=base_path)
geojson = dl.GeoJSON(url=base_path+'assets/regions.geojson', # url to geojson file geojson = dl.GeoJSON(url=base_path+'assets/regions.geojson', # url to geojson file
zoomToBounds=True, # when true, zooms to bounds when data changes (e.g. on load) zoomToBounds=True, # when true, zooms to bounds when data changes (e.g. on load)
zoomToBoundsOnClick=True, # when true, zooms to bounds of feature (e.g. polygon) on click zoomToBoundsOnClick=True, # when true, zooms to bounds of feature (e.g. polygon) on click
id="geojson-layer" id="geojson-layer"
) )
radioButtons = dcc.RadioItems( radioButtons = dcc.RadioItems(
options=[ options=[
{'label': 'Regions', 'value': 'region'}, {'label': 'Regions', 'value': 'region'},
{'label': 'Departements', 'value': 'departement'} {'label': 'Departements', 'value': 'departement'}
], ],
value='region', value='region',
id='radio-buttons', id='radio-buttons',
style ={'float':'center'} style ={'float':'center'}
) )
txt_output = html.P(id='result',children='Click a polygon !', style={'text-align': 'center','margin': 'auto','color':'green',}) txt_output = html.P(id='result',children='Click a polygon !', style={'text-align': 'center','margin': 'auto','color':'green',})
mapComponent = dl.Map(children = mapComponent = dl.Map(children =
[dl.TileLayer(),geojson], [dl.TileLayer(),geojson],
style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'}, style={'width': '80%','height':'80vh','float': 'center','margin': 'auto'},
center = [46.5,2.25], center = [46.5,2.25],
zoom = 5, zoom = 5,
id = 'first_map') id = 'first_map')
@app.callback(Output('geojson-layer','url'),Input('radio-buttons','value')) @app.callback(Output('geojson-layer','url'),Input('radio-buttons','value'))
def switch_file(value): def switch_file(value):
return (base_path+'assets/' + value + 's.geojson') return (base_path+'assets/' + value + 's.geojson')
@app.callback(Output('result','children'),Input('geojson-layer','click_feature'),State('radio-buttons','value')) @app.callback(Output('result','children'),Input('geojson-layer','click_feature'),State('radio-buttons','value'))
def get_name(feature,value): def get_name(feature,value):
return (value + ' : ' + feature['properties']['nom']) return (value + ' : ' + feature['properties']['nom'])
app.layout = html.Div(children = (radioButtons,txt_output,mapComponent)) app.layout = html.Div(children = (radioButtons,txt_output,mapComponent))
app.run_server(mode='inline', host=host, port=port, proxy=proxy) app.run_server(mode='inline', host=host, port=port, proxy=proxy)
``` ```
%% Output %% Output
%% Cell type:code id:6ab4e665-61a5-4338-9251-d4a20439d426 tags: %% Cell type:code id:6ab4e665-61a5-4338-9251-d4a20439d426 tags:
``` python ``` python
``` ```
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment