I have a geospatial app and would like to add Cytoscape over it to show network relationships in my dataset, similar to this. I’m wondering if anyone knows a MVE that shows how to accomplish this task.
well … isn’t this timely @lorenzo
check out this
- #200 Add
CyLeaflet
all-in-one component for network graphs overlaid on Leaflet maps. Requires thedash-leaflet
package, which can be installed as an extra with this package:pip install dash-cytoscape[leaflet]
.
And thank Hydro-Quebec who underwrote the development of it
oh … and please report back here w what you get up to … I’m keen to see practical use cases from such!
Hi @DaveG,
Thank you very much for sharing the link; it looks promising. However, when I attempted to run the example from dash-cytoscape , I encountered the following error:
TypeError: Cannot read properties of undefined (reading 'updateLeafBounds')
at _callee2$ (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_13_0m1702540705.dev.js:580:56)
at tryCatch (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_13_0m1702540705.dev.js:406:1357)
at Generator.<anonymous> (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_13_0m1702540705.dev.js:406:4187)
at Generator.next (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_13_0m1702540705.dev.js:406:2208)
at asyncGeneratorStep (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_13_0m1702540705.dev.js:412:103)
at _next (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_13_0m1702540705.dev.js:413:194)
at http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_13_0m1702540705.dev.js:413:364
at new Promise (<anonymous>)
at http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_13_0m1702540705.dev.js:413:97
at handleClientside (http://127.0.0.1:8050/_dash-component-suites/dash/dash-renderer/build/dash_renderer.v2_13_0m1702540705.dev.js:533:28)
I’m unsure if this issue is a bug or if it’s related to the versions of dash-cytoscape and dash-leaflet I have installed, which are:
dash-cytoscape==1.0.0
dash-leaflet==1.0.15
Any insights or assistance would be greatly appreciated.
requirements.txt:
blinker==1.7.0
certifi==2024.2.2
charset-normalizer==3.3.2
click==8.1.7
colorama==0.4.6
dash==2.15.0
dash-core-components==2.0.0
dash-cytoscape==1.0.0
dash-html-components==2.0.0
dash-leaflet==1.0.15
dash-table==5.0.0
Flask==3.0.2
idna==3.6
importlib-metadata==7.0.1
itsdangerous==2.1.2
Jinja2==3.1.3
MarkupSafe==2.1.5
nest-asyncio==1.6.0
packaging==23.2
plotly==5.18.0
requests==2.31.0
retrying==1.3.4
six==1.16.0
tenacity==8.2.3
typing_extensions==4.9.0
urllib3==2.2.0
Werkzeug==3.0.1
zipp==3.17.0
@PipInstallPython, thanks, i’m able to reproduce it.
I now have two other questions. I have this code which plot a network of nodes over a Leaflet map:
import dash
from dash import html
import dash_cytoscape as cyto
import dash_leaflet as dl
import json
# Initialize Dash app
app = dash.Dash(__name__)
master_node_id = '10002423'
# Read from the JSON file
with open('data/elements_' + str(master_node_id) +'.json', 'r') as file:
elements = json.load(file)
cy_stylesheet = [
{"selector": "node", "style": {"width": "20px", "height": "20px"}},
{"selector": "edge", "style": {"width": "5px"}},
{"selector": "node", "style": {"label": "data(label)"}},
]
# Read from the JSON file
with open('data/elements_' + str(master_node_id) +'.json', 'r') as file:
elements = json.load(file)
# `elements` is now a list of elements that can be used in Dash Cytoscape
node_ids = [59149772, 59149765, 59149769, 59149767, 59149771, 59149774, 61934176, 59149766, 59149768, 59149770, 59149773, 59149771]
# Extract unique labels
unique_labels = list(set(node['data']['label'] for node in elements))
total_labels = len(unique_labels)
# Create a selector string that matches any of the specified node IDs
selector = ', '.join([f'node[id = "{node_id}"]' for node_id in node_ids])
cy_stylesheet = [
{"selector": "node",
"style": {
"width": "5px",
"height": "5px"
}},
{"selector": "node", # Targets all nodes
"style": {
"label": "data(label)", # Use the 'label' field from each node's data for the label text
'font-size': '0px' # Decrease font size to 10px
}},
# Style for all nodes
{
'selector': 'node',
'style': {
'width': '5px', # Reduce the width of the node
'height': '5px',
'content': 'data(label)',
'font-size': '0px', # Example font size
'background-color': '#0074D9', # Example default node color
'text-valign': 'center',
'text-halign': 'center',
'color': '#000000', # Example text color
}
},
# Special style for the master unit node
{
'selector': f'node[id = "{master_node_id}"]', # Targeting master node by its ID
'style': {
'background-color': '#FF4136', # Different color for the master node
'border-color': 'black', # Optional: add a border to make it stand out
'border-width': 1,
'width': '20px', # Reduce the width of the node
'height': '20px',
}
},
{"selector": "edge",
"style": {
"width": "1px"
}},
]
# Dash layout with Cytoscape component
app.layout = html.Div([
cyto.CyLeaflet(
id="network",
cytoscape_props={
"elements": elements,
"stylesheet": cy_stylesheet,
"autoungrabify": True,
},
leaflet_props={
"children": [dl.TileLayer(
url='https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',
)],
"maxZoom": 22,
"minZoom": 12
},
width=600,
height=800,
)
])
if __name__ == '__main__':
app.run_server(debug=True)
which results in this preview:
the dataset to reproduce the code is available here and represents a master unit (in red) with connected children nodes. My questions now are:
- I would like to limit the zoom to 12 (minZoom) and 22 (maxZoom) of the Leaflet map. It seems that the configuration in the code is not taken into account.
- I have multiple children nodes at the same location. I would like to plot them in a way that can be well represented. For example, draw markers in a spider formation. Does someone have any suggestions to achieve that?