Skip to main content

Examples

Example: Basic interactivity

A small extension of Example 1 that listens for clicks on nodes and edges. Each click updates a text panel below the map with the coordinates of the clicked element. This is the same Dash callback pattern you use with any other Dash component.

What this example shows

  • How to react to a node click by listening to the clicked_node callback property.
  • How to react to an edge click by listening to clicked_edge.
  • The exact shape of those payloads, so you know which keys to read.

Live demo

tip

A live deployment of this example is hosted on the Observatorio Metropolitano dashboards.

Open the live demo ↗

Walkthrough

1. The interactivity model

SylvereyeRoadNetwork doesn't take onClick callbacks the way a JS Leaflet handler would. Instead, it exposes callback properties that are updated by the component when the user clicks something:

  • clicked_node — populated when a node is clicked.
  • clicked_edge — populated when an edge is clicked.
  • clicked_marker — populated when a marker is clicked (see Example 4).

A standard Dash @app.callback then listens to changes on one of these properties and runs whatever code you want.

2. Listen for node clicks

Add an H3 to the layout so we have somewhere to write the result, then wire a callback that reads from clicked_node and writes into the H3:

from dash import Dash, html
from dash.dependencies import Input, Output
from dash_sylvereye import SylvereyeRoadNetwork

app = Dash()
app.layout = html.Div([
SylvereyeRoadNetwork(id='sylvereye-roadnet', ...),
html.H2("Clicked elements:"),
html.H3(id='h3-clicked-node-coords'),
])

@app.callback(
Output('h3-clicked-node-coords', 'children'),
[Input('sylvereye-roadnet', 'clicked_node')])
def update_node_data(clicked_node):
if clicked_node:
lat = clicked_node['data']['lat']
lon = clicked_node['data']['lon']
return f'Clicked node coords: {lat}, {lon}'

clicked_node is a small dict with this shape:

{
'index': <int>, # position in the nodes_data list
'data': <dict>, # a deep copy of the clicked node's dict from nodes_data
}

So clicked_node['data']['lat'] and clicked_node['data']['lon'] get you the node's geographic coordinates. Any OSM attributes the loader attached to the node live under clicked_node['data']['data'], see Callback properties for the full schema.

caution

The callback fires only when clicked_node changes. The very first time the user clicks any node, the value changes from None to the click payload, the callback runs. Clicking the same node a second time does not change the property, so the callback does not fire again. Click any other node first, then come back to the original.

3. Listen for edge clicks

Same pattern for edges. The payload follows the same {index, data} shape, with data carrying the original edge dict from edges_data. The most useful field there is coords, the polyline that draws the edge:

app.layout = html.Div([
SylvereyeRoadNetwork(id='sylvereye-roadnet', ...),
html.H2("Clicked elements:"),
html.H3(id='h3-clicked-node-coords'),
html.H3(id='h3-clicked-edge-coords'),
])

@app.callback(
Output('h3-clicked-edge-coords', 'children'),
[Input('sylvereye-roadnet', 'clicked_edge')])
def update_edge_data(clicked_edge):
if clicked_edge:
return f'Clicked edge coords: {clicked_edge["data"]["coords"]}'

Full example

import osmnx as ox
from dash import Dash, html
from dash.dependencies import Input, Output
from dash_sylvereye import SylvereyeRoadNetwork
from dash_sylvereye.utils import load_from_osmnx_graph

OSMNX_QUERY = 'Kamppi, Helsinki, Finland'
TILE_LAYER_URL = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png'
TILE_LAYER_SUBDOMAINS = 'abcde'
TILE_LAYER_ATTRIBUTION = '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>, &copy; <a href="https://carto.com/attributions">CARTO</a>'
MAP_CENTER = [60.1663, 24.9313]
MAP_ZOOM = 15
MAP_STYLE = {'width': '100%', 'height': '84vh'}
TILE_LAYER_OPACITY = 0.9

road_network = ox.graph_from_place(OSMNX_QUERY, network_type='drive')
nodes_data, edges_data = load_from_osmnx_graph(road_network)

app = Dash()
app.layout = html.Div([
SylvereyeRoadNetwork(
id='sylvereye-roadnet',
nodes_data=nodes_data,
edges_data=edges_data,
tile_layer_url=TILE_LAYER_URL,
tile_layer_subdomains=TILE_LAYER_SUBDOMAINS,
tile_layer_attribution=TILE_LAYER_ATTRIBUTION,
tile_layer_opacity=TILE_LAYER_OPACITY,
map_center=MAP_CENTER,
map_zoom=MAP_ZOOM,
map_style=MAP_STYLE,
),
html.H2("Clicked elements:"),
html.H3(id='h3-clicked-node-coords'),
html.H3(id='h3-clicked-edge-coords'),
])

@app.callback(
Output('h3-clicked-node-coords', 'children'),
[Input('sylvereye-roadnet', 'clicked_node')])
def update_node_data(clicked_node):
if clicked_node:
lat = clicked_node['data']['lat']
lon = clicked_node['data']['lon']
return f'Clicked node coords: {lat}, {lon}'

@app.callback(
Output('h3-clicked-edge-coords', 'children'),
[Input('sylvereye-roadnet', 'clicked_edge')])
def update_edge_data(clicked_edge):
if clicked_edge:
return f'Clicked edge coords: {clicked_edge["data"]["coords"]}'

if __name__ == '__main__':
app.run()

Next

Example 3 — Nodes/edges customization shows how to change the size, color, and transparency of nodes and edges based on data.