diff --git a/README.md b/README.md index 8c6b137..71e5e19 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,12 @@ You can find the full documentation [here](https://yworks.github.io/yfiles-jupyt element color mapping Make Data Dependent Property Changes
Open In Colab + + heat mapping + Define a Heatmap Background
Open In Colab + leaflet mapping + Use a Map Background
Open In Colab + For example code look [here](https://github.com/yWorks/yfiles-jupyter-graphs/tree/master/examples). diff --git a/examples/00_toc.ipynb b/examples/00_toc.ipynb index 173c370..869a6c2 100644 --- a/examples/00_toc.ipynb +++ b/examples/00_toc.ipynb @@ -58,7 +58,12 @@ "- **[Advanced example using Neo4j graph data science](./27_neo4j-sample-gds_example.ipynb)**
\n", " This notebook contains a more in-depth example of the yFiles Graphs for Jupyter widget with Neo4j Graph Data Science package.\n", "- **[Example using Little Alchemy 2 database](./28_little-alchemy_example.ipynb)**
\n", - " This notebook uses some Game recipes of Little Alchemy 2" + " This notebook uses some Game recipes of Little Alchemy 2\n", + "- **Backgrounds**\n", + " - [Heat Mapping](./29_heat_mapping.ipynb)\n", + " - [Leaflet Integration](./30_leaflet_mapping.ipynb)\n", + "- **[Nested Graphs](./31_nested_graphs.ipynb)**
\n", + " This notebook shows how to create group nodes" ] }, { @@ -78,7 +83,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -92,7 +97,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.15" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/examples/02_label_mapping.ipynb b/examples/02_label_mapping.ipynb index 03cd9d6..6793ef0 100644 --- a/examples/02_label_mapping.ipynb +++ b/examples/02_label_mapping.ipynb @@ -33,9 +33,12 @@ "from yfiles_jupyter_graphs import GraphWidget\n", "%pip install networkx --quiet\n", "from typing import Dict\n", - "from networkx import erdos_renyi_graph\n", + "from networkx import erdos_renyi_graph, set_node_attributes, set_edge_attributes\n", "\n", "g = erdos_renyi_graph(10, 0.3, 2)\n", + "# We will use this additional attribute as a label later on\n", + "set_node_attributes(g, {node: {\"NodeName\": f\"Node {node}\"} for node in g.nodes})\n", + "set_edge_attributes(g, {edge: {\"EdgeName\": f\"Edge {edge}\"} for edge in g.edges})\n", "w = GraphWidget(graph=g)\n" ] }, @@ -94,6 +97,32 @@ "metadata": {}, "source": [ "## Node Label Mapping\n", + "### Property key mapping\n", + "\n", + "To only reflect the data of a property on the node, you can easily assign this using the respective key. Check out the [mapping overloads notebook](./12_mapping_overloads.ipynb) for a more detailed explanation.\n", + "\n", + "This is a shorter alternative to a local lambda function, e.g., `lambda node: node['properties']['NodeName']`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0057e94d-fc4a-417a-ad94-a81689ec6f16", + "metadata": {}, + "outputs": [], + "source": [ + "w.node_label_mapping = 'NodeName'\n", + "display(w)" + ] + }, + { + "cell_type": "markdown", + "id": "12bd0dd5-77c8-4aa3-bd0d-46c80f7ec83b", + "metadata": {}, + "source": [ + "### Function mapping\n", + "\n", + "For a more versatile node label computation you can define a mapping function:\n", "\n", "The node label mapping is a function that is supposed to return a label string for each given node object which is then displayed in the widget.\n", "\n", @@ -196,8 +225,31 @@ "metadata": {}, "source": [ "## Edge Label Mapping\n", + "### Property key mapping\n", "\n", - "The edge label mapping is a function that is supposed to return a label string for each given edge object which is then displayed in the widget.\n", + "Similar to node mappings, edges also allow for a short version assigning properties by key:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d320734c-8a53-4bbe-8468-d0de06423bcd", + "metadata": {}, + "outputs": [], + "source": [ + "w2 = GraphWidget(graph=g)\n", + "w2.edge_label_mapping = 'EdgeName'\n", + "display(w2)" + ] + }, + { + "cell_type": "markdown", + "id": "4aeec1ca-34e6-4cb5-9e0a-a361606046fa", + "metadata": {}, + "source": [ + "### Function mapping\n", + "\n", + "Similar to node labels, you can define an edge label mapping function for more control over the visualized label. The edge label mapping is a function that is supposed to return a label string for each given edge object which is then displayed in the widget.\n", "\n", "We will use a similar mapping function as for the node labels. We negate every edge index and use this as our new edge label. \\\n", "For this, we first a define a new mapping function and then set this function as our current edge label mapping. \\\n", @@ -211,8 +263,6 @@ "metadata": {}, "outputs": [], "source": [ - "w2 = GraphWidget(graph=g)\n", - "\n", "edges = w2.get_edges()\n", "def custom_edge_label_mapping(edge: Dict):\n", " \"\"\"let the label be the negated index\"\"\"\n", @@ -301,7 +351,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.3" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/examples/02a_label_styles_mapping.ipynb b/examples/02a_label_styles_mapping.ipynb index b21b9b1..929ade3 100644 --- a/examples/02a_label_styles_mapping.ipynb +++ b/examples/02a_label_styles_mapping.ipynb @@ -24,19 +24,10 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "3ce520a0-0c33-48ae-ab02-54d7aba460a8", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n", - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], + "outputs": [], "source": [ "%pip install yfiles_jupyter_graphs --quiet\n", "from yfiles_jupyter_graphs import GraphWidget\n", @@ -58,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "id": "d99602b7-b89b-46bb-a477-00172006ac05", "metadata": {}, "outputs": [], @@ -89,25 +80,10 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "1a87056a-a345-4221-ad19-d1440a63f08e", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "e7c4d9f1bab64fe383b57d1829a39f7d", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "GraphWidget(layout=Layout(height='500px', width='100%'))" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "display(w)" ] @@ -136,7 +112,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "id": "371c5053-6019-4a20-af67-067f38fcbf39", "metadata": {}, "outputs": [], @@ -167,21 +143,10 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "938719a0-d739-4ddf-8f7f-da00c1d05012", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "w.set_node_label_mapping(custom_label_styles_mapping)\n", "w.get_node_label_mapping()" @@ -189,25 +154,10 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "a45c9447-74ba-48a1-bdcb-acb6e3bec468", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "e7c4d9f1bab64fe383b57d1829a39f7d", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "GraphWidget(layout=Layout(height='500px', width='100%'))" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "display(w)" ] @@ -222,21 +172,10 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "5f64587f-1035-4649-a751-32022baac797", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "w.del_node_label_mapping()\n", "w.get_node_label_mapping()" @@ -261,21 +200,10 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "3cb41d68-d190-4e48-b91d-58a8973463c7", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "w2 = GraphWidget(graph=g)\n", "w2.set_edge_label_mapping(custom_label_styles_mapping)\n", @@ -284,25 +212,10 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "70a11fd8-e8ff-40ce-ba01-94762d3fd78d", "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "20d33b73e3a6463dacc6f2ae6ed7a685", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "GraphWidget(layout=Layout(height='500px', width='100%'))" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "display(w2)" ] @@ -317,21 +230,10 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "1b31186d-0cbb-4e2b-903e-83a9a3659640", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "w2.del_edge_label_mapping()\n", "w2.get_edge_label_mapping()" diff --git a/examples/16_neo4j_import.ipynb b/examples/16_neo4j_import.ipynb index ac191b2..d50f60d 100644 --- a/examples/16_neo4j_import.ipynb +++ b/examples/16_neo4j_import.ipynb @@ -85,7 +85,7 @@ "metadata": {}, "outputs": [], "source": [ - "NEO4J_URI = \"neo4j+s://demo.neo4jlabs.com\" \n", + "NEO4J_URI = \"neo4j+ssc://demo.neo4jlabs.com\" \n", "NEO4J_USERNAME = \"movies\"\n", "NEO4J_PASSWORD = \"movies\"\n", "\n", @@ -203,6 +203,9 @@ "w3.node_label_mapping = lambda node: node['properties']['name'] if 'name' in node['properties'] else node['properties']['title']\n", "\n", "w3.hierarchic_layout()\n", + "\n", + "w3.set_heat_mapping(lambda element: element['properties']['votes']/ 7000 if 'votes' in element['properties'] else 0)\n", + "\n", "display(w3)" ] } diff --git a/examples/22_layouts.ipynb b/examples/22_layouts.ipynb index 76898fa..f12cddf 100644 --- a/examples/22_layouts.ipynb +++ b/examples/22_layouts.ipynb @@ -250,6 +250,36 @@ "w9.organic_edge_router()\n", "display(w9)" ] + }, + { + "cell_type": "markdown", + "id": "cc731bba-3fb9-416b-8ea8-2c2b8d8567bb", + "metadata": {}, + "source": [ + "## Interactive Organic Layout\n", + "\n", + "The interactive organic layout allows node dragging" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d66c4239-3b93-4a0a-a678-596e73b833f9", + "metadata": {}, + "outputs": [], + "source": [ + "w10 = GraphWidget(graph=florentine_families_graph())\n", + "w10.interactive_organic_layout()\n", + "display(w10)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8abec923-5234-4654-9817-821582d9562b", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/examples/29_heat_mapping.ipynb b/examples/29_heat_mapping.ipynb new file mode 100644 index 0000000..3798d49 --- /dev/null +++ b/examples/29_heat_mapping.ipynb @@ -0,0 +1,238 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "675ab9b2-219d-490d-8efd-45dbd77c26b9", + "metadata": {}, + "source": [ + "# Heat Mapping \"Open\n", + "\n", + "Before using the graph widget, install all necessary packages and initialize your widget." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "acb80d9e-112d-4e03-9915-c074c03a871a", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install yfiles_jupyter_graphs --quiet\n", + "from yfiles_jupyter_graphs import GraphWidget\n", + "w = GraphWidget()" + ] + }, + { + "cell_type": "markdown", + "id": "26a2bbb9-7f41-485c-82d5-541b21539996", + "metadata": {}, + "source": [ + "You can also open this notebook in Google Colab when Google Colab's custom widget manager is enabled:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bb9670f9-698b-4a8d-a961-3263dc709e44", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " import google.colab\n", + " from google.colab import output\n", + " output.enable_custom_widget_manager()\n", + "except:\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "id": "8a1cc311-e290-47fe-8b8f-df0c91c4d14b", + "metadata": {}, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "id": "e9b0ac00-6de6-47c3-b599-f14389831c06", + "metadata": {}, + "source": [ + "## Mapping Function\n", + "\n", + "The heatmapping is slightly different than other mappings, as there is one combined mapping for edges and nodes\n", + "\n", + "There are get, set and delete methods for the heatmap.\n", + "- you can set a new heat mapping with ```w.set_heat_mapping```\n", + "- you can get the current heat mapping with ```w.get_heat_mapping```\n", + "- you can delete a custom heat mapping with ```w.del_heat_mapping```\n", + "\n", + "If no custom mapping is set the default mapping is used." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "69c45788-84d3-4b1b-8e8b-05dbd74bd8ab", + "metadata": {}, + "outputs": [], + "source": [ + "print(w.default_heat_mapping.__doc__)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7a90608-4c1e-486a-8789-04ac80f0d395", + "metadata": {}, + "outputs": [], + "source": [ + "def heatmap(element):\n", + " load = 0.5\n", + " if 'start' in element:\n", + " #edge case\n", + " return 0\n", + " else:\n", + " # Calculate load based on number of edges\n", + " load += sum(0.1 for edge in w.edges if edge['end'] == element['id'])\n", + " load += sum(-0.1 for edge in w.edges if edge['start'] == element['id'])\n", + " # Calculate load based on capacity (if available)\n", + " if 'properties' in element and 'capacity' in element['properties']:\n", + " load += 1 - (element['properties']['capacity'] / 100)\n", + " # Calculate load based on duration (if available)\n", + " if 'properties' in element and 'duration' in element['properties']:\n", + " load += element['properties']['duration'] / 10\n", + " load = min(1, max(0, load))\n", + " return load\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "4cc6bc9c-b259-46cc-9604-9c9cb250a375", + "metadata": {}, + "source": [ + "We use a graph representing a production chain and add our heatmap according to the expected step load:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6732964b-649c-4ebc-a580-005464bf681d", + "metadata": {}, + "outputs": [], + "source": [ + "w.set_heat_mapping(heatmap)\n", + "w.nodes = [\n", + " { \"id\": 1, \"properties\": {\"label\": \"Start\", \"capacity\": 50 }},\n", + " { \"id\": 3, \"properties\": {\"label\": \"Evaluation\", \"capacity\": 20 }},\n", + " { \"id\": 4, \"properties\": {\"label\": \"Move to Backlog\"}},\n", + " { \"id\": 5, \"properties\": {\"label\": \"Prepare\", \"capacity\": 20 }},\n", + " { \"id\": 6, \"properties\": {\"label\": \"Prepare Shortcut\"}},\n", + " { \"id\": 7, \"properties\": {\"label\": \"Step A-1\"}},\n", + " { \"id\": 9, \"properties\": {\"label\": \"Advance\", \"duration\": 3 }},\n", + " { \"id\": 10, \"properties\": {\"label\": \"Early Discard\"}},\n", + " { \"id\": 11, \"properties\": {\"label\": \"Step A-2\"}},\n", + " { \"id\": 12, \"properties\": {\"label\": \"Quick Preparation\"}},\n", + " { \"id\": 13, \"properties\": {\"label\": \"Backlog\", \"capacity\": 100 }},\n", + " { \"id\": 14, \"properties\": {\"label\": \"Step B\"}},\n", + " { \"id\": 16, \"properties\": {\"label\": \"End of Preparation\"}},\n", + " { \"id\": 17, \"properties\": {\"label\": \"Buffer\", \"capacity\": 30, \"duration\": 10 }},\n", + " { \"id\": 18, \"properties\": {\"label\": \"Main Processing\", \"capacity\": 70, \"duration\": 2 }},\n", + " { \"id\": 19, \"properties\": {\"label\": \"Refinement 1\", \"capacity\": 20, \"duration\": 1 }},\n", + " { \"id\": 20, \"properties\": {\"label\": \"Refinement 2\", \"capacity\": 60, \"duration\": 4 }},\n", + " { \"id\": 21, \"properties\": {\"label\": \"Testing\", \"capacity\": 70, \"duration\": 1 }},\n", + " { \"id\": 22, \"properties\": {\"label\": \"Delivery\", \"capacity\": 20 }},\n", + " { \"id\": 23, \"properties\": {\"label\": \"Rejection\", \"capacity\": 100 }},\n", + " { \"id\": 24, \"properties\": {\"label\": \"Store\", \"capacity\": 50, \"duration\": 5 }}\n", + "]\n", + "\n", + "w.edges = [\n", + " { \"start\": 1, \"end\": 5, \"properties\": {} },\n", + " { \"start\": 1, \"end\": 2, \"properties\": {} },\n", + " { \"start\": 1, \"end\": 3 , \"properties\": {} },\n", + " { \"start\": 1, \"end\": 4, \"properties\": { \"probability\": 0.1 }},\n", + " { \"start\": 16, \"end\": 17, \"properties\": {} },\n", + " { \"start\": 16, \"end\": 18, \"properties\": {} },\n", + " { \"start\": 18, \"end\": 19, \"properties\": {} },\n", + " { \"start\": 5, \"end\": 11, \"properties\": {} },\n", + " { \"start\": 9, \"end\": 6, \"properties\": { \"probability\": 0.1 }},\n", + " { \"start\": 5, \"end\": 7, \"properties\": { \"probability\": 3 }},\n", + " { \"start\": 2, \"end\": 8 , \"properties\": {} },\n", + " { \"start\": 6, \"end\": 12 , \"properties\": {} },\n", + " { \"start\": 3, \"end\": 9 , \"properties\": {} },\n", + " { \"start\": 3, \"end\": 10, \"properties\": { \"probability\": 0.1 }},\n", + " { \"start\": 4, \"end\": 13 , \"properties\": {} },\n", + " { \"start\": 11, \"end\": 14 , \"properties\": {} },\n", + " { \"start\": 7, \"end\": 14, \"properties\": {} },\n", + " { \"start\": 5, \"end\": 16, \"properties\": { \"probability\": 0.1 }},\n", + " { \"start\": 14, \"end\": 16 , \"properties\": {} },\n", + " { \"start\": 18, \"end\": 21 , \"properties\": {} },\n", + " { \"start\": 21, \"end\": 23, \"properties\": { \"probability\": 0.1 }},\n", + " { \"start\": 21, \"end\": 24 , \"properties\": {} },\n", + " { \"start\": 21, \"end\": 22 , \"properties\": {} },\n", + " { \"start\": 13, \"end\": 15 , \"properties\": {} },\n", + " { \"start\": 12, \"end\": 17 , \"properties\": {} },\n", + " { \"start\": 18, \"end\": 22 , \"properties\": {} },\n", + " { \"start\": 10, \"end\": 15 , \"properties\": {} },\n", + " { \"start\": 9, \"end\": 20, \"properties\": {} },\n", + " { \"start\": 19, \"end\": 21 , \"properties\": {} },\n", + " { \"start\": 17, \"end\": 18 , \"properties\": {} },\n", + " { \"start\": 20, \"end\": 21 , \"properties\": {} }\n", + "]\n", + "w.hierarchic_layout()\n", + "w.get_heat_mapping()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "773513de-4b4e-492d-959d-e88116b4d4bb", + "metadata": {}, + "outputs": [], + "source": [ + "display(w)" + ] + }, + { + "cell_type": "markdown", + "id": "9390607f-9f13-49bf-b77e-b9f4939df12e", + "metadata": {}, + "source": [ + "If the heat mapping is deleted, the heatmap mapping reverts back to the default mapping." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45a96e73-f5e2-474e-b1c1-4092f857bdd1", + "metadata": {}, + "outputs": [], + "source": [ + "w.del_heat_mapping()\n", + "w.get_heat_mapping()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/30_leaflet_mapping.ipynb b/examples/30_leaflet_mapping.ipynb new file mode 100644 index 0000000..7bb4147 --- /dev/null +++ b/examples/30_leaflet_mapping.ipynb @@ -0,0 +1,250 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2dc9a13a-8eda-41a2-b82e-0fa6dcdba165", + "metadata": {}, + "source": [ + "# Leaflet Integration \"Open\n", + "\n", + "Before using the graph widget, install all necessary packages and initialize your widget." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e2c1065-4f28-4ae1-b743-9166aa647e4e", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install yfiles_jupyter_graphs --quiet\n", + "from yfiles_jupyter_graphs import GraphWidget\n", + "w = GraphWidget()" + ] + }, + { + "cell_type": "markdown", + "id": "8730a384-1ece-4615-a86e-68f289cef3a6", + "metadata": {}, + "source": [ + "You can also open this notebook in Google Colab when Google Colab's custom widget manager is enabled:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "aa6b295b-3b11-4fce-ba09-81f8c70b2bb5", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " import google.colab\n", + " from google.colab import output\n", + " output.enable_custom_widget_manager()\n", + "except:\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "id": "26eead6d-66c5-4f14-9a5c-cc8a633dcb95", + "metadata": {}, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "id": "516f8b11-6f66-46b7-bfea-4d4f9f4e548f", + "metadata": {}, + "source": [ + "## Mapping Function\n", + "\n", + "The node coordinate mapping is a function that is supposed to return a 2-tuple of numbers `(lat, lng)` for each given node object which is then used in the widget.\n", + "\n", + "Optionally, the index can be used as the first function parameter." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "899cb5a5-c81a-4598-b534-2375c1857774", + "metadata": {}, + "outputs": [], + "source": [ + "print(w.default_node_coordinate_mapping.__doc__)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "642533bd-2922-4641-b71b-314de376ac87", + "metadata": {}, + "outputs": [], + "source": [ + "flightData = {\n", + " 'airports': [\n", + " { 'name': 'Los Angeles', 'iata': 'LAX','lat': 33.942536,'lng': -118.408075, 'passengers': 65000000 }, { 'name': 'Rio de Janeiro', 'iata': 'GIG','lat': -22.808903,'lng': -43.243647, 'passengers': 5000000 }, {'name': 'Lima', 'iata': 'LIM','lat': -12.021889,'lng': -77.114319, 'passengers': 18000000 }, { 'name': 'London', 'iata': 'LHR','lat': 51.4775,'lng': -0.461389, 'passengers': 61000000 },\n", + " {'name': 'Frankfurt', 'iata': 'FRA','lat': 50.033333,'lng': 8.570556, 'passengers': 48000000 }, {'name': 'Moscow', 'iata': 'SVO','lat': 55.972642,'lng': 37.414589, 'passengers': 49000000 }, {'name': 'New Delhi', 'iata': 'DEL','lat': 28.5665,'lng': 77.103089, 'passengers': 39000000 }, {'name': 'Shanghai', 'iata': 'PVG','lat': 31.143378,'lng': 121.805214, 'passengers': 32000000 },\n", + " {'name': 'Hongkong', 'iata': 'HKG','lat': 22.308919,'lng': 113.914603, 'passengers': 1000000 }, {'name': 'Tokio', 'iata': 'NRT','lat': 35.764722,'lng': 140.386389, 'passengers': 15000000 }, {'name': 'Dubai', 'iata': 'DXB','lat': 25.252778,'lng': 55.364444, 'passengers': 29000000 }, {'name': 'Dakar', 'iata': 'DKR','lat': 14.670833,'lng': -17.072778, 'passengers': 2000000 },\n", + " {'name': 'Johannesburg', 'iata': 'JNB','lat': -26.133694,'lng': 28.242317, 'passengers': 9000000 }, {'name': 'Sydney', 'iata': 'SYD','lat': -33.946111,'lng': 151.177222, 'passengers': 44000000 }, {'name': 'Nairobi', 'iata': 'NBO','lat': -1.319167,'lng': 36.927778, 'passengers': 900000 }, {'name': 'Atlanta', 'iata': 'ATL','lat': 33.639167,'lng': -84.427778, 'passengers': 93000000 },\n", + " {'name': 'New York City', 'iata': 'JFK','lat': 40.63975,'lng': -73.778925, 'passengers': 55000000 }, {'name': 'Cairo', 'iata': 'CAI','lat': 30.121944,'lng': 31.405556, 'passengers': 14000000 }, {'name': 'Casablanca', 'iata': 'CMN','lat': 33.367467,'lng': -7.589967, 'passengers': 7000000 }, {'name': 'Lagos', 'iata': 'LOS','lat': 6.577222,'lng': 3.321111, 'passengers': 5000000 },\n", + " {'name': 'Cape Town', 'iata': 'CPT','lat': -33.969444,'lng': 18.597222, 'passengers': 5000000 }, {'name': 'Chengdu', 'iata': 'CTU','lat': 30.578333,'lng': 103.946944, 'passengers': 40000000 }, {'name': 'Jakarta', 'iata': 'CGK','lat': -6.125567,'lng': 106.655897, 'passengers': 54000000 }, {'name': 'Teheran', 'iata': 'IKA','lat': 35.416111,'lng': 51.152222, 'passengers': 8000000 },\n", + " {'name': 'Tel Aviv', 'iata': 'TLV','lat': 32.011389,'lng': 34.886667, 'passengers': 20000000 }, {'name': 'Kuala Lumpur', 'iata': 'KUL','lat': 2.745578,'lng': 101.709917, 'passengers': 25000000 }, {'name': 'Manila', 'iata': 'MNL','lat': 14.508647,'lng': 121.019581, 'passengers': 8000000 }, {'name': 'Singapur', 'iata': 'SIN','lat': 1.350189,'lng': 103.994433, 'passengers': 32000000 },\n", + " {'name': 'Taipeh', 'iata': 'TPE','lat': 25.077732,'lng': 121.232822, 'passengers': 800000 }, {'name': 'Bangkok', 'iata': 'BKK','lat': 13.681108,'lng': 100.747283, 'passengers': 65000000 }, {'name': 'Istanbul', 'iata': 'IST','lat': 40.976922,'lng': 28.814606, 'passengers': 64000000 }, {'name': 'Ulaanbaatar', 'iata': 'ULN','lat': 47.843056,'lng': 106.766639, 'passengers': 1000000 },\n", + " {'name': 'Melbourne', 'iata': 'MEL','lat': -37.673333,'lng': 144.843333, 'passengers': 12000000 }, {'name': 'Brisbane', 'iata': 'BNE','lat': -27.383333,'lng': 153.118056, 'passengers': 23000000 }, {'name': 'Nadi', 'iata': 'NAN','lat': -17.755392,'lng': 177.443378, 'passengers': 2000000 }, {'name': 'Auckland', 'iata': 'AKL','lat': -37.008056,'lng': 174.791667, 'passengers': 21000000 },\n", + " {'name': 'Paris', 'iata': 'CDG','lat': 49.009722,'lng': 2.547778, 'passengers': 57000000 }, {'name': 'Madrid', 'iata': 'MAD','lat': 40.4675,'lng': -3.551944, 'passengers': 50000000 }, {'name': 'Barcelona', 'iata': 'BCN','lat': 41.297078,'lng': 2.078464, 'passengers': 41000000 }, {'name': 'Rome', 'iata': 'FCO','lat': 41.804444,'lng': 12.250833, 'passengers': 29000000 },\n", + " {'name': 'Copenhagen', 'iata': 'CPH','lat': 55.617917,'lng': 12.655972, 'passengers': 30000000 }, {'name': 'Helsinki', 'iata': 'HEL','lat': 60.317222,'lng': 24.963333, 'passengers': 5000000 }, {'name': 'Athens', 'iata': 'ATH','lat': 37.936358,'lng': 23.944467, 'passengers': 22000000 }, {'name': 'Dublin', 'iata': 'DUB','lat': 53.421333,'lng': -6.270075, 'passengers': 32000000 },\n", + " {'name': 'Reykjavik', 'iata': 'RKV','lat': 64.13,'lng': -21.940556, 'passengers': 400000 }, {'name': 'Oslo', 'iata': 'OSL','lat': 60.193917,'lng': 11.100361, 'passengers': 9000000 }, {'name': 'Vienna', 'iata': 'VIE','lat': 48.110833,'lng': 16.570833, 'passengers': 10000000 }, {'name': 'Lisbon', 'iata': 'LIS','lat': 38.774167,'lng': -9.134167, 'passengers': 28000000 },\n", + " {'name': 'Stockholm', 'iata': 'ARN','lat': 59.651944,'lng': 17.918611, 'passengers': 7000000 }, {'name': 'Edinburgh', 'iata': 'EDI','lat': 55.95,'lng': -3.3725, 'passengers': 14000000 }, {'name': 'Chicago', 'iata': 'ORD','lat': 41.978603,'lng': -87.904842, 'passengers': 54000000 }, {'name': 'Dallas', 'iata': 'DFW','lat': 32.896828,'lng': -97.037997, 'passengers': 73000000 },\n", + " {'name': 'San Francisco', 'iata': 'SFO','lat': 37.618972,'lng': -122.374889, 'passengers': 42000000 }, {'name': 'Las Vegas', 'iata': 'LAS','lat': 36.080056,'lng': -115.15225, 'passengers': 52000000 }, {'name': 'Miami', 'iata': 'MIA','lat': 25.79325,'lng': -80.290556, 'passengers': 50000000 }, {'name': 'Toronto', 'iata': 'YYZ','lat': 43.677222,'lng': -79.630556, 'passengers': 12000000 },\n", + " {'name': 'Vancouver', 'iata': 'YVR','lat': 49.193889,'lng': -123.184444, 'passengers': 19000000 }, {'name': 'Montreal', 'iata': 'YUL','lat': 45.47175,'lng': -73.736569, 'passengers': 15000000 }, {'name': 'Mexico-City', 'iata': 'MEX','lat': 19.436303,'lng': -99.072097, 'passengers': 46000000 }, {'name': 'Guatemala-City', 'iata': 'GUA','lat': 14.583272,'lng': -90.527475, 'passengers': 2000000 },\n", + " {'name': 'Buenos Aires', 'iata': 'EZE','lat': -34.822222,'lng': -58.535833, 'passengers': 5000000 }, {'name': 'Sao Paulo', 'iata': 'GRU','lat': -23.432075,'lng': -46.469511, 'passengers': 34000000 }, {'name': 'Santiago de Chile', 'iata': 'SCL','lat': -33.392975,'lng': -70.785803, 'passengers': 20000000 }, {'name': 'Brasilia', 'iata': 'BSB','lat': -15.871111,'lng': -47.918611, 'passengers': 13000000 },\n", + " {'name': 'Bogota', 'iata': 'BOG','lat': 4.701594,'lng': -74.146947, 'passengers': 36000000 }, {'name': 'Caracas', 'iata': 'CCS','lat': 10.601194,'lng': -66.991222, 'passengers': 8000000 } ],\n", + " 'connections': [\n", + " {'from': 'LAX','to': 'JFK' }, {'from': 'JFK','to': 'GIG' }, {'from': 'JFK','to': 'LIM' }, {'from': 'JFK','to': 'LHR' }, {'from': 'GIG','to': 'FRA' }, {'from': 'LIM','to': 'GIG' }, {'from': 'FRA','to': 'JFK' }, {'from': 'LHR','to': 'FRA' }, {'from': 'FRA','to': 'SVO' }, {'from': 'FRA','to': 'DXB' }, {'from': 'SVO','to': 'DEL' },\n", + " {'from': 'SVO','to': 'PVG' }, {'from': 'DEL','to': 'HKG' }, {'from': 'PVG','to': 'HKG' }, {'from': 'PVG','to': 'NRT' }, {'from': 'HKG','to': 'SYD' }, {'from': 'NRT','to': 'SYD' }, {'from': 'DXB','to': 'SVO' }, {'from': 'DXB','to': 'DEL' }, {'from': 'DXB','to': 'DKR' }, {'from': 'DXB','to': 'JNB' }, {'from': 'JNB','to': 'LHR' },\n", + " {'from': 'JNB','to': 'DKR' }, {'from': 'SYD','to': 'DXB' }, {'from': 'NBO','to': 'JNB' }, {'from': 'NBO','to': 'DXB' }, {'from': 'ATL','to': 'JFK' }, {'from': 'LAX','to': 'ATL' }, {'from': 'ATL','to': 'LHR' }, {'from': 'ATL','to': 'LIM' }, {'from': 'SCL','to': 'LIM' }, {'from': 'EZE','to': 'SCL' }, {'from': 'SCL','to': 'GRU' },\n", + " {'from': 'GIG','to': 'EZE' }, {'from': 'GIG','to': 'GRU' }, {'from': 'BSB','to': 'GIG' }, {'from': 'SCL','to': 'BSB' }, {'from': 'LIM','to': 'BSB' }, {'from': 'BOG','to': 'BSB' }, {'from': 'CCS','to': 'BSB' }, {'from': 'BOG','to': 'GUA' }, {'from': 'CCS','to': 'MIA' }, {'from': 'GUA','to': 'MIA' }, {'from': 'GUA','to': 'MEX' },\n", + " {'from': 'MEX','to': 'LAX' }, {'from': 'MEX','to': 'LAX' }, {'from': 'LAX','to': 'SFO' }, {'from': 'SFO','to': 'YVR' }, {'from': 'LAX','to': 'LAS' }, {'from': 'LAX','to': 'DFW' }, {'from': 'LAX','to': 'ORD' }, {'from': 'SFO','to': 'LAS' }, {'from': 'DFW','to': 'ATL' }, {'from': 'ATL','to': 'YYZ' }, {'from': 'ORD','to': 'YYZ' }, \n", + " {'from': 'YYZ','to': 'YUL' }, {'from': 'YYZ','to': 'JFK' }, {'from': 'YUL','to': 'JFK' }, {'from': 'JNB','to': 'CPT' }, {'from': 'LOS','to': 'DKR' }, {'from': 'NBO','to': 'LOS' }, {'from': 'DKR','to': 'CMN' }, {'from': 'DKR','to': 'CAI' }, {'from': 'NBO','to': 'CAI' }, {'from': 'DXB','to': 'CAI' }, {'from': 'IKA','to': 'DXB' }, \n", + " {'from': 'IST','to': 'IKA' }, {'from': 'TLV','to': 'ATH' }, {'from': 'CAI','to': 'TLV' }, {'from': 'ATH','to': 'IST' }, {'from': 'FCO','to': 'ATH' }, {'from': 'LIS','to': 'LHR' }, {'from': 'LIS','to': 'MAD' }, {'from': 'MAD','to': 'BCN' }, {'from': 'CDG','to': 'LHR' }, {'from': 'DUB','to': 'LHR' }, {'from': 'EDI','to': 'LHR' }, \n", + " {'from': 'CDG','to': 'BCN' }, {'from': 'BCN','to': 'FCO' }, {'from': 'VIE','to': 'IST' }, {'from': 'VIE','to': 'SVO' }, {'from': 'CMN','to': 'LIS' }, {'from': 'MAD','to': 'CMN' }, {'from': 'FRA','to': 'CPH' }, {'from': 'LHR','to': 'CPH' }, {'from': 'CPH','to': 'OSL' }, {'from': 'CPH','to': 'ARN' }, {'from': 'ARN','to': 'HEL' },\n", + " {'from': 'HEL','to': 'SVO' }, {'from': 'ULN','to': 'PVG' }, {'from': 'CTU','to': 'PVG' }, {'from': 'PVG','to': 'TPE' }, {'from': 'CTU','to': 'HKG' }, {'from': 'TPE','to': 'HKG' }, {'from': 'HKG','to': 'MNL' }, {'from': 'BKK','to': 'HKG' }, {'from': 'SIN','to': 'KUL' }, {'from': 'SIN','to': 'BKK' }, {'from': 'CGK','to': 'SIN' },\n", + " {'from': 'MNL','to': 'SIN' }, {'from': 'SIN','to': 'SYD' }, {'from': 'BNE','to': 'SYD' }, {'from': 'SYD','to': 'MEL' }, {'from': 'NAN','to': 'SYD' }, {'from': 'AKL','to': 'SYD' }, {'from': 'NAN','to': 'AKL' }, {'from': 'RKV','to': 'LHR' }, {'from': 'BCN','to': 'CDG' }, {'from': 'BCN','to': 'FRA' }, {'from': 'FCO','to': 'FRA' }, \n", + " {'from': 'BOG','to': 'MEX' }, {'from': 'BOG','to': 'GRU' }, {'from': 'ATL','to': 'MIA' }, {'from': 'FRA','to': 'IST' }, {'from': 'IST','to': 'DEL' }, {'from': 'PVG','to': 'BKK' }, {'from': 'DEL','to': 'BKK' },\n", + " ],\n", + "}\n", + "\n", + "w.edges = edges = [\n", + " {\"start\": connection['from'], \"end\": connection['to'], \"label\": \"\", \"properties\": {}, \"directed\": False}\n", + " for connection in flightData['connections']\n", + "]\n", + "\n", + "w.nodes = nodes = [\n", + " {\"id\": airport['iata'], \"properties\": {\"label\": airport['name'], 'passengers': airport['passengers']}, \"coordinates\": [airport['lat'], airport['lng']]}\n", + " for airport in flightData['airports']\n", + "]\n" + ] + }, + { + "cell_type": "markdown", + "id": "4e296c92-22b3-4765-9c5e-0d2bf9e98742", + "metadata": {}, + "source": [ + "We'll use the given latitude and longitute for our coordinate mapping." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7448ef35-a12d-4d4d-a6a8-e2f96b97dd99", + "metadata": {}, + "outputs": [], + "source": [ + "w.node_coordinate_mapping = 'coordinates' \n", + "w.get_node_coordinate_mapping()" + ] + }, + { + "cell_type": "markdown", + "id": "515b21f4-5a9d-42c2-a463-1594f6a727d4", + "metadata": {}, + "source": [ + "To show the map-view, either pick the `Show on Map` layout in the toolbar, or pre-configure the layout with `w.map_layout()`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20a5689d-30dc-4a40-a8c4-b64f5e3ae630", + "metadata": {}, + "outputs": [], + "source": [ + "w.map_layout()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0acf19fd-a301-4ece-9354-cd8474427d1e", + "metadata": {}, + "outputs": [], + "source": [ + "display(w)" + ] + }, + { + "cell_type": "markdown", + "id": "e64bce42-f3d8-4153-bb46-a349ebb3f10c", + "metadata": {}, + "source": [ + "If the coordinate mapping is deleted, the heatmap mapping reverts back to the default mapping." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8d4b5fda-947d-4eed-b624-70c19726bde4", + "metadata": {}, + "outputs": [], + "source": [ + "w.del_node_coordinate_mapping()\n", + "w.get_node_coordinate_mapping()" + ] + }, + { + "cell_type": "markdown", + "id": "c087955a-d94d-4a5b-bb38-d366bfb5f36b", + "metadata": {}, + "source": [ + "Add an additional heat mapping:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fc71eb02-da8f-4d9f-8b68-52e82f1d76c8", + "metadata": {}, + "outputs": [], + "source": [ + "w2 = GraphWidget()\n", + "w2.nodes = w.nodes\n", + "w2.edges = w.edges\n", + "\n", + "w2.node_coordinate_mapping = 'coordinates' \n", + "\n", + "def heat(element):\n", + " if 'start' in element:\n", + " #edge case\n", + " return 0\n", + " else: return (element['properties']['passengers'] - 800000)/92200000\n", + "\n", + "w2.set_heat_mapping(heat)\n", + "w2.map_layout()\n", + "display(w2)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/31_nested_graphs.ipynb b/examples/31_nested_graphs.ipynb new file mode 100644 index 0000000..d5fd8c2 --- /dev/null +++ b/examples/31_nested_graphs.ipynb @@ -0,0 +1,305 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "95c7a78b-917a-42b6-a1a9-b3255c0667ce", + "metadata": {}, + "source": [ + "# Nested Graphs \"Open\n", + "\n", + "Before using the graph widget, install all necessary packages and initialize your widget." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "86ccab84-0881-4e14-b33d-51313ec2b85e", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install yfiles_jupyter_graphs --quiet\n", + "from yfiles_jupyter_graphs import GraphWidget" + ] + }, + { + "cell_type": "markdown", + "id": "99c91625-6682-4dcc-86ed-ff80a85d435d", + "metadata": {}, + "source": [ + "You can also open this notebook in Google Colab when Google Colab's custom widget manager is enabled:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "837b3dc2-a656-4bd1-856d-b2e4ed12b7a0", + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " import google.colab\n", + " from google.colab import output\n", + " output.enable_custom_widget_manager()\n", + "except:\n", + " pass" + ] + }, + { + "cell_type": "markdown", + "id": "e8af92d1-a099-4f27-b42b-7ffebff1954d", + "metadata": {}, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "id": "8cea83a9-2373-4dce-a9a2-353022fb1676", + "metadata": {}, + "source": [ + "## Sample Data\n", + "\n", + "The dataset for this sample is the following airport and flight routes data:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0eb47bdd-d943-4820-bbc3-29d8396204ac", + "metadata": {}, + "outputs": [], + "source": [ + "airports = [\n", + " {\"name\": \"Los Angeles\", \"iata\": \"LAX\", \"lat\": 33.942536, \"lng\": -118.408075, \"passengers\": 65000000, \"country\": \"USA\", \"id\": \"LAX\"}, {\"name\": \"Rio de Janeiro\", \"iata\": \"GIG\", \"lat\": -22.808903, \"lng\": -43.243647, \"passengers\": 5000000, \"country\": \"Brazil\", \"id\": \"GIG\"}, {\"name\": \"Lima\", \"iata\": \"LIM\", \"lat\": -12.021889, \"lng\": -77.114319, \"passengers\": 18000000, \"country\": \"Peru\", \"id\": \"LIM\"}, {\"name\": \"London\", \"iata\": \"LHR\", \"lat\": 51.4775, \"lng\": -0.461389, \"passengers\": 61000000, \"country\": \"UK\", \"id\": \"LHR\"},\n", + " {\"name\": \"Frankfurt\", \"iata\": \"FRA\", \"lat\": 50.033333, \"lng\": 8.570556, \"passengers\": 48000000, \"country\": \"Germany\", \"id\": \"FRA\"}, {\"name\": \"Moscow\", \"iata\": \"SVO\", \"lat\": 55.972642, \"lng\": 37.414589, \"passengers\": 49000000, \"country\": \"Russia\", \"id\": \"SVO\"}, {\"name\": \"New Delhi\", \"iata\": \"DEL\", \"lat\": 28.5665, \"lng\": 77.103089, \"passengers\": 39000000, \"country\": \"India\", \"id\": \"DEL\"}, {\"name\": \"Shanghai\", \"iata\": \"PVG\", \"lat\": 31.143378, \"lng\": 121.805214, \"passengers\": 32000000, \"country\": \"China\", \"id\": \"PVG\"},\n", + " {\"name\": \"Hongkong\", \"iata\": \"HKG\", \"lat\": 22.308919, \"lng\": 113.914603, \"passengers\": 1000000, \"country\": \"China\", \"id\": \"HKG\"}, {\"name\": \"Tokio\", \"iata\": \"NRT\", \"lat\": 35.764722, \"lng\": 140.386389, \"passengers\": 15000000, \"country\": \"Japan\", \"id\": \"NRT\"}, {\"name\": \"Dubai\", \"iata\": \"DXB\", \"lat\": 25.252778, \"lng\": 55.364444, \"passengers\": 29000000, \"country\": \"UAE\", \"id\": \"DXB\"}, {\"name\": \"Dakar\", \"iata\": \"DKR\", \"lat\": 14.670833, \"lng\": -17.072778, \"passengers\": 2000000, \"country\": \"Senegal\", \"id\": \"DKR\"},\n", + " {\"name\": \"Johannesburg\", \"iata\": \"JNB\", \"lat\": -26.133694, \"lng\": 28.242317, \"passengers\": 9000000, \"country\": \"South Africa\", \"id\": \"JNB\"}, {\"name\": \"Sydney\", \"iata\": \"SYD\", \"lat\": -33.946111, \"lng\": 151.177222, \"passengers\": 44000000, \"country\": \"Australia\", \"id\": \"SYD\"}, {\"name\": \"Nairobi\", \"iata\": \"NBO\", \"lat\": -1.319167, \"lng\": 36.927778, \"passengers\": 900000, \"country\": \"Kenya\", \"id\": \"NBO\"}, {\"name\": \"Atlanta\", \"iata\": \"ATL\", \"lat\": 33.639167, \"lng\": -84.427778, \"passengers\": 93000000, \"country\": \"USA\", \"id\": \"ATL\"},\n", + " {\"name\": \"New York City\", \"iata\": \"JFK\", \"lat\": 40.63975, \"lng\": -73.778925, \"passengers\": 55000000, \"country\": \"USA\", \"id\": \"JFK\"}, {\"name\": \"Cairo\", \"iata\": \"CAI\", \"lat\": 30.121944, \"lng\": 31.405556, \"passengers\": 14000000, \"country\": \"Egypt\", \"id\": \"CAI\"}, {\"name\": \"Casablanca\", \"iata\": \"CMN\", \"lat\": 33.367467, \"lng\": -7.589967, \"passengers\": 7000000, \"country\": \"Morocco\", \"id\": \"CMN\"}, {\"name\": \"Lagos\", \"iata\": \"LOS\", \"lat\": 6.577222, \"lng\": 3.321111, \"passengers\": 5000000, \"country\": \"Nigeria\", \"id\": \"LOS\"},\n", + " {\"name\": \"Cape Town\", \"iata\": \"CPT\", \"lat\": -33.969444, \"lng\": 18.597222, \"passengers\": 5000000, \"country\": \"South Africa\", \"id\": \"CPT\"}, {\"name\": \"Chengdu\", \"iata\": \"CTU\", \"lat\": 30.578333, \"lng\": 103.946944, \"passengers\": 40000000, \"country\": \"China\", \"id\": \"CTU\"}, {\"name\": \"Jakarta\", \"iata\": \"CGK\", \"lat\": -6.125567, \"lng\": 106.655897, \"passengers\": 54000000, \"country\": \"Indonesia\", \"id\": \"CGK\"}, {\"name\": \"Teheran\", \"iata\": \"IKA\", \"lat\": 35.416111, \"lng\": 51.152222, \"passengers\": 8000000, \"country\": \"Iran\", \"id\": \"IKA\"},\n", + " {\"name\": \"Tel Aviv\", \"iata\": \"TLV\", \"lat\": 32.011389, \"lng\": 34.886667, \"passengers\": 20000000, \"country\": \"Israel\", \"id\": \"TLV\"}, {\"name\": \"Kuala Lumpur\", \"iata\": \"KUL\", \"lat\": 2.745578, \"lng\": 101.709917, \"passengers\": 25000000, \"country\": \"Malaysia\", \"id\": \"KUL\"}, {\"name\": \"Manila\", \"iata\": \"MNL\", \"lat\": 14.508647, \"lng\": 121.019581, \"passengers\": 8000000, \"country\": \"Philippines\", \"id\": \"MNL\"}, {\"name\": \"Singapur\", \"iata\": \"SIN\", \"lat\": 1.350189, \"lng\": 103.994433, \"passengers\": 32000000, \"country\": \"Singapore\", \"id\": \"SIN\"},\n", + " {\"name\": \"Taipeh\", \"iata\": \"TPE\", \"lat\": 25.077732, \"lng\": 121.232822, \"passengers\": 800000, \"country\": \"Taiwan\", \"id\": \"TPE\"}, {\"name\": \"Bangkok\", \"iata\": \"BKK\", \"lat\": 13.681108, \"lng\": 100.747283, \"passengers\": 65000000, \"country\": \"Thailand\", \"id\": \"BKK\"}, {\"name\": \"Istanbul\", \"iata\": \"IST\", \"lat\": 40.976922, \"lng\": 28.814606, \"passengers\": 64000000, \"country\": \"Turkey\", \"id\": \"IST\"}, {\"name\": \"Ulaanbaatar\", \"iata\": \"ULN\", \"lat\": 47.843056, \"lng\": 106.766639, \"passengers\": 1000000, \"country\": \"Mongolia\", \"id\": \"ULN\"},\n", + " {\"name\": \"Melbourne\", \"iata\": \"MEL\", \"lat\": -37.673333, \"lng\": 144.843333, \"passengers\": 12000000, \"country\": \"Australia\", \"id\": \"MEL\"}, {\"name\": \"Brisbane\", \"iata\": \"BNE\", \"lat\": -27.383333, \"lng\": 153.118056, \"passengers\": 23000000, \"country\": \"Australia\", \"id\": \"BNE\"}, {\"name\": \"Nadi\", \"iata\": \"NAN\", \"lat\": -17.755392, \"lng\": 177.443378, \"passengers\": 2000000, \"country\": \"Fiji\", \"id\": \"NAN\"}, {\"name\": \"Auckland\", \"iata\": \"AKL\", \"lat\": -37.008056, \"lng\": 174.791667, \"passengers\": 21000000, \"country\": \"New Zealand\", \"id\": \"AKL\"},\n", + " {\"name\": \"Paris\", \"iata\": \"CDG\", \"lat\": 49.009722, \"lng\": 2.547778, \"passengers\": 57000000, \"country\": \"France\", \"id\": \"CDG\"}, {\"name\": \"Madrid\", \"iata\": \"MAD\", \"lat\": 40.4675, \"lng\": -3.551944, \"passengers\": 50000000, \"country\": \"Spain\", \"id\": \"MAD\"}, {\"name\": \"Barcelona\", \"iata\": \"BCN\", \"lat\": 41.297078, \"lng\": 2.078464, \"passengers\": 41000000, \"country\": \"Spain\", \"id\": \"BCN\"}, {\"name\": \"Rome\", \"iata\": \"FCO\", \"lat\": 41.804444, \"lng\": 12.250833, \"passengers\": 29000000, \"country\": \"Italy\", \"id\": \"FCO\"},\n", + " {\"name\": \"Copenhagen\", \"iata\": \"CPH\", \"lat\": 55.617917, \"lng\": 12.655972, \"passengers\": 30000000, \"country\": \"Denmark\", \"id\": \"CPH\"}, {\"name\": \"Helsinki\", \"iata\": \"HEL\", \"lat\": 60.317222, \"lng\": 24.963333, \"passengers\": 5000000, \"country\": \"Finland\", \"id\": \"HEL\"}, {\"name\": \"Athens\", \"iata\": \"ATH\", \"lat\": 37.936358, \"lng\": 23.944467, \"passengers\": 22000000, \"country\": \"Greece\", \"id\": \"ATH\"}, {\"name\": \"Dublin\", \"iata\": \"DUB\", \"lat\": 53.421333, \"lng\": -6.270075, \"passengers\": 32000000, \"country\": \"Ireland\", \"id\": \"DUB\"},\n", + " {\"name\": \"Reykjavik\", \"iata\": \"RKV\", \"lat\": 64.13, \"lng\": -21.940556, \"passengers\": 400000, \"country\": \"Iceland\", \"id\": \"RKV\"}, {\"name\": \"Oslo\", \"iata\": \"OSL\", \"lat\": 60.193917, \"lng\": 11.100361, \"passengers\": 9000000, \"country\": \"Norway\", \"id\": \"OSL\"}, {\"name\": \"Vienna\", \"iata\": \"VIE\", \"lat\": 48.110833, \"lng\": 16.570833, \"passengers\": 10000000, \"country\": \"Austria\", \"id\": \"VIE\"}, {\"name\": \"Lisbon\", \"iata\": \"LIS\", \"lat\": 38.774167, \"lng\": -9.134167, \"passengers\": 28000000, \"country\": \"Portugal\", \"id\": \"LIS\"},\n", + " {\"name\": \"Stockholm\", \"iata\": \"ARN\", \"lat\": 59.651944, \"lng\": 17.918611, \"passengers\": 7000000, \"country\": \"Sweden\", \"id\": \"ARN\"}, {\"name\": \"Edinburgh\", \"iata\": \"EDI\", \"lat\": 55.95, \"lng\": -3.3725, \"passengers\": 14000000, \"country\": \"UK\", \"id\": \"EDI\"}, {\"name\": \"Chicago\", \"iata\": \"ORD\", \"lat\": 41.978603, \"lng\": -87.904842, \"passengers\": 54000000, \"country\": \"USA\", \"id\": \"ORD\"}, {\"name\": \"Dallas\", \"iata\": \"DFW\", \"lat\": 32.896828, \"lng\": -97.037997, \"passengers\": 73000000, \"country\": \"USA\", \"id\": \"DFW\"},\n", + " {\"name\": \"San Francisco\", \"iata\": \"SFO\", \"lat\": 37.618972, \"lng\": -122.374889, \"passengers\": 42000000, \"country\": \"USA\", \"id\": \"SFO\"}, {\"name\": \"Las Vegas\", \"iata\": \"LAS\", \"lat\": 36.080056, \"lng\": -115.15225, \"passengers\": 52000000, \"country\": \"USA\", \"id\": \"LAS\"}, {\"name\": \"Miami\", \"iata\": \"MIA\", \"lat\": 25.79325, \"lng\": -80.290556, \"passengers\": 50000000, \"country\": \"USA\", \"id\": \"MIA\"}, {\"name\": \"Toronto\", \"iata\": \"YYZ\", \"lat\": 43.677222, \"lng\": -79.630556, \"passengers\": 12000000, \"country\": \"Canada\", \"id\": \"YYZ\"},\n", + " {\"name\": \"Vancouver\", \"iata\": \"YVR\", \"lat\": 49.193889, \"lng\": -123.184444, \"passengers\": 19000000, \"country\": \"Canada\", \"id\": \"YVR\"}, {\"name\": \"Montreal\", \"iata\": \"YUL\", \"lat\": 45.47175, \"lng\": -73.736569, \"passengers\": 15000000, \"country\": \"Canada\", \"id\": \"YUL\"}, {\"name\": \"Mexico-City\", \"iata\": \"MEX\", \"lat\": 19.436303, \"lng\": -99.072097, \"passengers\": 46000000, \"country\": \"Mexico\", \"id\": \"MEX\"}, {\"name\": \"Guatemala-City\", \"iata\": \"GUA\", \"lat\": 14.583272, \"lng\": -90.527475, \"passengers\": 2000000, \"country\": \"Guatemala\", \"id\": \"GUA\"},\n", + " {\"name\": \"Buenos Aires\", \"iata\": \"EZE\", \"lat\": -34.822222, \"lng\": -58.535833, \"passengers\": 5000000, \"country\": \"Argentina\", \"id\": \"EZE\"}, {\"name\": \"Sao Paulo\", \"iata\": \"GRU\", \"lat\": -23.432075, \"lng\": -46.469511, \"passengers\": 34000000, \"country\": \"Brazil\", \"id\": \"GRU\"}, {\"name\": \"Santiago de Chile\", \"iata\": \"SCL\", \"lat\": -33.392975, \"lng\": -70.785803, \"passengers\": 20000000, \"country\": \"Chile\", \"id\": \"SCL\"}, {\"name\": \"Brasilia\", \"iata\": \"BSB\", \"lat\": -15.871111, \"lng\": -47.918611, \"passengers\": 13000000, \"country\": \"Brazil\", \"id\": \"BSB\"},\n", + " {\"name\": \"Bogota\", \"iata\": \"BOG\", \"lat\": 4.701594, \"lng\": -74.146947, \"passengers\": 36000000, \"country\": \"Colombia\", \"id\": \"BOG\"}, {\"name\": \"Caracas\", \"iata\": \"CCS\", \"lat\": 10.601194, \"lng\": -66.991222, \"passengers\": 8000000, \"country\": \"Venezuela\", \"id\": \"CCS\"}\n", + " ]\n", + "connections = [\n", + " {'from': 'LAX','to': 'JFK' }, {'from': 'JFK','to': 'GIG' }, {'from': 'JFK','to': 'LIM' }, {'from': 'JFK','to': 'LHR' }, {'from': 'GIG','to': 'FRA' }, {'from': 'LIM','to': 'GIG' }, {'from': 'FRA','to': 'JFK' }, {'from': 'LHR','to': 'FRA' }, {'from': 'FRA','to': 'SVO' }, {'from': 'FRA','to': 'DXB' }, {'from': 'SVO','to': 'DEL' },\n", + " {'from': 'SVO','to': 'PVG' }, {'from': 'DEL','to': 'HKG' }, {'from': 'PVG','to': 'HKG' }, {'from': 'PVG','to': 'NRT' }, {'from': 'HKG','to': 'SYD' }, {'from': 'NRT','to': 'SYD' }, {'from': 'DXB','to': 'SVO' }, {'from': 'DXB','to': 'DEL' }, {'from': 'DXB','to': 'DKR' }, {'from': 'DXB','to': 'JNB' }, {'from': 'JNB','to': 'LHR' },\n", + " {'from': 'JNB','to': 'DKR' }, {'from': 'SYD','to': 'DXB' }, {'from': 'NBO','to': 'JNB' }, {'from': 'NBO','to': 'DXB' }, {'from': 'ATL','to': 'JFK' }, {'from': 'LAX','to': 'ATL' }, {'from': 'ATL','to': 'LHR' }, {'from': 'ATL','to': 'LIM' }, {'from': 'SCL','to': 'LIM' }, {'from': 'EZE','to': 'SCL' }, {'from': 'SCL','to': 'GRU' },\n", + " {'from': 'GIG','to': 'EZE' }, {'from': 'GIG','to': 'GRU' }, {'from': 'BSB','to': 'GIG' }, {'from': 'SCL','to': 'BSB' }, {'from': 'LIM','to': 'BSB' }, {'from': 'BOG','to': 'BSB' }, {'from': 'CCS','to': 'BSB' }, {'from': 'BOG','to': 'GUA' }, {'from': 'CCS','to': 'MIA' }, {'from': 'GUA','to': 'MIA' }, {'from': 'GUA','to': 'MEX' },\n", + " {'from': 'MEX','to': 'LAX' }, {'from': 'MEX','to': 'LAX' }, {'from': 'LAX','to': 'SFO' }, {'from': 'SFO','to': 'YVR' }, {'from': 'LAX','to': 'LAS' }, {'from': 'LAX','to': 'DFW' }, {'from': 'LAX','to': 'ORD' }, {'from': 'SFO','to': 'LAS' }, {'from': 'DFW','to': 'ATL' }, {'from': 'ATL','to': 'YYZ' }, {'from': 'ORD','to': 'YYZ' }, \n", + " {'from': 'YYZ','to': 'YUL' }, {'from': 'YYZ','to': 'JFK' }, {'from': 'YUL','to': 'JFK' }, {'from': 'JNB','to': 'CPT' }, {'from': 'LOS','to': 'DKR' }, {'from': 'NBO','to': 'LOS' }, {'from': 'DKR','to': 'CMN' }, {'from': 'DKR','to': 'CAI' }, {'from': 'NBO','to': 'CAI' }, {'from': 'DXB','to': 'CAI' }, {'from': 'IKA','to': 'DXB' }, \n", + " {'from': 'IST','to': 'IKA' }, {'from': 'TLV','to': 'ATH' }, {'from': 'CAI','to': 'TLV' }, {'from': 'ATH','to': 'IST' }, {'from': 'FCO','to': 'ATH' }, {'from': 'LIS','to': 'LHR' }, {'from': 'LIS','to': 'MAD' }, {'from': 'MAD','to': 'BCN' }, {'from': 'CDG','to': 'LHR' }, {'from': 'DUB','to': 'LHR' }, {'from': 'EDI','to': 'LHR' }, \n", + " {'from': 'CDG','to': 'BCN' }, {'from': 'BCN','to': 'FCO' }, {'from': 'VIE','to': 'IST' }, {'from': 'VIE','to': 'SVO' }, {'from': 'CMN','to': 'LIS' }, {'from': 'MAD','to': 'CMN' }, {'from': 'FRA','to': 'CPH' }, {'from': 'LHR','to': 'CPH' }, {'from': 'CPH','to': 'OSL' }, {'from': 'CPH','to': 'ARN' }, {'from': 'ARN','to': 'HEL' },\n", + " {'from': 'HEL','to': 'SVO' }, {'from': 'ULN','to': 'PVG' }, {'from': 'CTU','to': 'PVG' }, {'from': 'PVG','to': 'TPE' }, {'from': 'CTU','to': 'HKG' }, {'from': 'TPE','to': 'HKG' }, {'from': 'HKG','to': 'MNL' }, {'from': 'BKK','to': 'HKG' }, {'from': 'SIN','to': 'KUL' }, {'from': 'SIN','to': 'BKK' }, {'from': 'CGK','to': 'SIN' },\n", + " {'from': 'MNL','to': 'SIN' }, {'from': 'SIN','to': 'SYD' }, {'from': 'BNE','to': 'SYD' }, {'from': 'SYD','to': 'MEL' }, {'from': 'NAN','to': 'SYD' }, {'from': 'AKL','to': 'SYD' }, {'from': 'NAN','to': 'AKL' }, {'from': 'RKV','to': 'LHR' }, {'from': 'BCN','to': 'CDG' }, {'from': 'BCN','to': 'FRA' }, {'from': 'FCO','to': 'FRA' }, \n", + " {'from': 'BOG','to': 'MEX' }, {'from': 'BOG','to': 'GRU' }, {'from': 'ATL','to': 'MIA' }, {'from': 'FRA','to': 'IST' }, {'from': 'IST','to': 'DEL' }, {'from': 'PVG','to': 'BKK' }, {'from': 'DEL','to': 'BKK' },\n", + " ]\n" + ] + }, + { + "cell_type": "markdown", + "id": "1970927b-c195-4135-8b10-c0ad7902abab", + "metadata": {}, + "source": [ + "Provided as data to yFiles Jupyter Graphs results in this graph:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cd760fb5-88da-4343-9fc0-4dbaccae7202", + "metadata": {}, + "outputs": [], + "source": [ + "w = GraphWidget()\n", + "w.edges = [\n", + " {\"start\": connection['from'], \"end\": connection['to'], \"label\": \"\", \"properties\": {}, \"directed\": False}\n", + " for connection in connections\n", + "]\n", + "\n", + "w.nodes = [\n", + " {\"id\": airport['iata'], \"properties\": {\"label\": airport['name'], 'passengers': airport['passengers'], 'country': airport['country']}, \"coordinates\": [airport['lat'], airport['lng']]}\n", + " for airport in airports\n", + "]\n", + "w.node_coordinate_mapping = 'coordinates' # also map the geo-coordinate data because they are available in the dataset\n", + "display(w)" + ] + }, + { + "cell_type": "markdown", + "id": "66223c0a-fcfa-468e-a04b-6f29ff15055a", + "metadata": {}, + "source": [ + "Among other properties, each airport entry contains a `country` property which we can use as grouping key, such that the airports are grouped by country." + ] + }, + { + "cell_type": "markdown", + "id": "ff915adc-d9ae-4581-81c9-df4c9389d243", + "metadata": {}, + "source": [ + "## Adding parent nodes to the dataset\n", + "\n", + "To use the `country` property as grouping hierarchy, we need to make sure that the source data also countains (node) entries for the countries, such that they can be used as parent nodes. This step is only necessay, if the original data does not contain entries for the parent nodes already." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c3615c0-6f70-4525-94e2-0d674f1e24d9", + "metadata": {}, + "outputs": [], + "source": [ + "airport_nodes = [\n", + " {\"id\": airport['iata'], \"properties\": {\"label\": airport['name'], 'passengers': airport['passengers'], 'country': airport['country']}, \"coordinates\": [airport['lat'], airport['lng']]}\n", + " for airport in airports\n", + "]\n", + "\n", + "countries = []\n", + "for airport_node in airport_nodes:\n", + " # collect unique countries among airports\n", + " airport_country = airport_node[\"properties\"][\"country\"]\n", + " if not any(country[\"id\"] == airport_country for country in countries):\n", + " countries.append({\"id\": airport_country, \"properties\": {\"label\": airport_country}})\n", + "\n", + "# create a nodes source with both, the unique countries (reprsenting our group nodes) and airports\n", + "nodes = airport_nodes + countries" + ] + }, + { + "cell_type": "markdown", + "id": "21d70393-a928-47d5-adb4-cdfc25c1bedd", + "metadata": {}, + "source": [ + "## Parent Mapping function\n", + "\n", + "To create a nested graph, the parent-mapping function must be specified. This function should return an id for each given node object which is then used as group node in the widget for the respective child." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "45a57df5-955e-40b4-9862-f8fb24b3f526", + "metadata": {}, + "outputs": [], + "source": [ + "print(w.set_node_parent_mapping.__doc__) " + ] + }, + { + "cell_type": "markdown", + "id": "f6ebe245-24b2-4e2f-bc48-e07eb215cbb3", + "metadata": {}, + "source": [ + "For this we define a new mapping function that provides the `country` property as parent node id.\n", + "\n", + "Because we already enriched our datasets with respective node objects, these country nodes are created as group nodes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "359f5269-2f57-4458-96b6-802c93e3fa87", + "metadata": {}, + "outputs": [], + "source": [ + "w2 = GraphWidget()\n", + "\n", + "# use the country property as parent-id for each node\n", + "w2.node_parent_mapping = \"country\"\n", + "\n", + "# assign the data\n", + "w2.edges = [\n", + " {\"start\": connection['from'], \"end\": connection['to'], \"label\": \"\", \"properties\": {}, \"directed\": False}\n", + " for connection in connections\n", + "]\n", + "w2.nodes = nodes\n", + "\n", + "# also map the geo-coordinate data because they are available in the dataset\n", + "w2.node_coordinate_mapping = 'coordinates' \n", + "\n", + "display(w2)" + ] + }, + { + "cell_type": "markdown", + "id": "2006b777-0167-44eb-b496-5b66a55a9c04", + "metadata": {}, + "source": [ + "## Group node styling\n", + "\n", + "Similar to leaf nodes, group nodes may also be styled through the node style mappings, see also [Color Mapping](./03_color_mapping.ipynb) or [Styles Mapping](./08_styles_mapping.ipynb).\n", + "\n", + "For example, make the group nodes gray-ish:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c2d3a3d7-40dc-425c-944e-a33382c67a0e", + "metadata": {}, + "outputs": [], + "source": [ + "w3 = GraphWidget()\n", + "w3.node_parent_mapping = \"country\"\n", + "w3.edges = [\n", + " {\"start\": connection['from'], \"end\": connection['to'], \"label\": \"\", \"properties\": {}, \"directed\": False}\n", + " for connection in connections\n", + "]\n", + "w3.nodes = nodes\n", + "w3.node_coordinate_mapping = 'coordinates' \n", + "\n", + "# assign a color mapping to style country nodes differently, otherwise use the widget's default color\n", + "w3.node_color_mapping = lambda node : None if \"passengers\" in node[\"properties\"] else \"gray\"\n", + "\n", + "display(w3)" + ] + }, + { + "cell_type": "markdown", + "id": "3041b183-2d9e-4bfa-a0f6-ea5e26445acf", + "metadata": {}, + "source": [ + "If a node parent mapping is deleted, the parent mapping reverts back to the default mapping." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4b3d4816-d0a9-4415-b29a-02b7732ca8be", + "metadata": {}, + "outputs": [], + "source": [ + "w.del_node_parent_mapping()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/mkdocs/02_graph_widget.md b/mkdocs/02_graph_widget.md index 8add47f..9636c68 100644 --- a/mkdocs/02_graph_widget.md +++ b/mkdocs/02_graph_widget.md @@ -310,6 +310,12 @@ If no mapping is explicitly set, [`default_node_label_mapping`](#default_node_la | `node_label_mapping` | `Union[callable, str]` | A function that produces node labels or the name of the property to use for binding. The function should have the same signature as `default_node_label_mapping` e.g. take in a node dictionary and return a string. | **Example** +```Python +In [1]: from yfiles_jupyter_graphs import GraphWidget +In [2]: w = GraphWidget() +In [3]: w.node_label_mapping = 'id' +``` + ```Python In [1]: from yfiles_jupyter_graphs import GraphWidget In [2]: w = GraphWidget() @@ -351,6 +357,12 @@ If no mapping is explicitly set, [`default_edge_label_mapping`](#default_edge_la | `edge_label_mapping` | `Union[callable, str]` | A function that produces edge labels or the name of the property to use for binding. The funtion should have the same signature as `default_edge_label_mapping` e.g. take in an edge dictionary and return a string. | **Example** +```Python +In [1]: from yfiles_jupyter_graphs import GraphWidget +In [2]: w = GraphWidget() +In [3]: w.edge_label_mapping = 'id' +``` + ```Python In [1]: from yfiles_jupyter_graphs import GraphWidget In [2]: w = GraphWidget() @@ -806,6 +818,52 @@ Remove a custom node type mapping.   +### node_parent_mapping: Union[callable, str]
+          Data dependent change of node parent on a per node basis. + +**`def get_node_parent_mapping()`**
+          Getter for the node parent mapping property. + +**Notes** + +If no mapping is explicitly set, [`default_node_parent_mapping`](#default_node_parent_mapping) is returned. + +**Returns** + +| Name | parent | Description | +| ----------- | ----------- | ----------- | +| `node_parent_mapping` | `Union[callable, str]` | A function that produces node parents or the name of the property to use for binding. | + +**`def set_node_parent_mapping(node_parent_mapping)`**
+          Setter for the node parent mapping property. + +**Parameters** + +| Name | parent | Description | +| ----------- | ----------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `node_parent_mapping` | `Union[callable, str]` | A function that produces node parent ids or the name of the property to use for binding. The function should have the same signature as `default_node_parent_mapping` e.g. take in a node dictionary and return a bool/int/float or str value. | + +**Notes** + +Given node parent ids create group nodes instead of regular nodes. + +**Example** +```Python +In [1]: from yfiles_jupyter_graphs import GraphWidget +In [2]: w = GraphWidget() +In [3]: def custom_node_parent_mapping(node: dict): + ... +In [4]: w.set_node_parent_mapping(custom_node_parent_mapping) +``` + + +**`def del_node_parent_mapping()`**
+          Deleter for the node parent mapping property. + +Remove a custom node parent mapping. + +  + ### node_position_mapping: Union[callable, str]
          Data dependent change of node position on a per node basis. @@ -856,6 +914,97 @@ Remove a custom node position mapping.   +### heat_mapping: Union[callable, str]
+          Data dependent change of heat value on a per node and edge basis. + +**`def get_heat_mapping()`**
+          Getter for the heat mapping property. + +**Notes** + +If no mapping is explicitly set, [`default_heat_mapping`](#default_heat_mapping) is returned. + +**Returns** + +| Name | Type | Description | +|---------------------| ----------- |--------------------------------------------------------------------------------------| +| `heat_mapping` | `Union[callable, str]` | A function that produces heat values or the name of the property to use for binding. | + +**`def set_heat_mapping(heat_mapping)`**
+          Setter for the heat mapping property. + +**Parameters** + +| Name | Type | Description | +|----------------| ----------- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `heat_mapping` | `Union[callable, str]` | A function that produces heat values or the name of the property to use for binding. The function should have the same signature as `default_heat_mapping` e.g. take in a element dictionary and return a float. | + +**Notes** + +This mapping is used for both edges and nodes + +**Example** +```Python +In [1]: from yfiles_jupyter_graphs import GraphWidget +In [2]: w = GraphWidget() +In [3]: def custom_heat_mapping(element: dict): + ... +In [4]: w.set_heat_mapping(custom_heat_mapping) +``` + +**`def del_heat_mapping()`**
+          Deleter for the heat mapping property. + +Remove a custom heat mapping. + +  + +### node_coordinate_mapping: Union[callable, str]
+          Data dependent change of node coordinate on a per node basis. + +**`def get_node_coordinate_mapping()`**
+          Getter for the node coordinate mapping property. + +**Notes** + +If no mapping is explicitly set, [`default_node_coordinate_mapping`](#default_node_coordinate_mapping) is returned. + +**Returns** + +| Name | Type | Description | +| ----------- | ----------- | ----------- | +| `node_coordinate_mapping` | `Union[callable, str]` | A function that produces node coordinates or the name of the property to use for binding. | + +**`def set_node_coordinate_mapping(node_coordinate_mapping)`**
+          Setter for the node coordinate mapping property. + +**Parameters** + +| Name | Type | Description | +| ----------- | ----------- | ----------- | +| `node_coordinate_mapping` | `Union[callable, str]` | A function that produces node coordinates or the name of the property to use for binding. The function should have the same signature as `default_node_coordinate_mapping` e.g. take in a node dictionary and return a float 2-tuple. | + +**Notes** + +Only the map layout consider node coordinates, +all other algorithms ignore node coordinates. + +**Example** +```Python +In [1]: from yfiles_jupyter_graphs import GraphWidget +In [2]: w = GraphWidget() +In [3]: def custom_node_coordinate_mapping(node: dict): + ... +In [4]: w.set_node_coordinate_mapping(custom_node_coordinate_mapping) +``` + +**`def del_node_coordinate_mapping()`**
+          Deleter for the node coordinate mapping property. + +Remove a custom node coordinate mapping. + +  + ### directed_mapping: Union[callable, str]
          Data dependent change if an edge is directed or not. @@ -1010,6 +1159,14 @@ for more details about this specific algorithm.   +### def interactive_organic_layout()
+          Alias for GraphWidget.graph_layout= "interactive_organic_layout". + +See [yFiles interactive organic layout guide](https://docs.yworks.com/yfileshtml/#/dguide/organic_layout#interactive_organic_layout) +for more details about this specific algorithm. + +  + ### def organic_edge_router()
          Alias for GraphWidget.graph_layout= "organic_edge_router". @@ -1049,6 +1206,12 @@ then it will be called with the element (typing.Dict) as first parameter. **Example** +```Python +In [1]: from yfiles_jupyter_graphs import GraphWidget +In [2]: w = GraphWidget() +In [3]: w.{node|edge}_label_mapping = 'id' +``` + ```Python In [1]: from yfiles_jupyter_graphs import GraphWidget In [2]: w = GraphWidget() @@ -1124,6 +1287,9 @@ Can be 'overwritten' by setting the property with a function of the same signatu If the given mapping function has only one parameter (that is not typed as int), then it will be called with the element (typing.Dict) as first parameter. +When a string is provided as the function argument, the key will be searched for in both the properties +dictionary and the element keys. + **Example** ```Python @@ -1413,6 +1579,44 @@ In [4]: w.set_node_layout_mapping(custom_node_layout_mapping)   +### def default_heat_mapping(index, node)
+          The default heat mapping for nodes and edges. + +Provides constant value of None for all nodes and edges. + +**Parameters** + +| Name | Type | Description | +|-----------| ----------- |---------------------------| +| `index` | `int` | Position in element list. | +| `element` | `typing.Dict` | | + +**Notes** + +This is the default value for the [`heat_mapping`](#heat_mapping_property) property. +Can be 'overwritten' by setting the property with a function of the same signature. + +If the given mapping function has only one parameter (that is not typed as int), +then it will be called with the element (typing.Dict) as first parameter. + +**Example** + +```Python +In [1]: from yfiles_jupyter_graphs import GraphWidget +In [2]: w = GraphWidget() +In [3]: def custom_heat_mapping(node: typing.Dict): + ... +In [4]: w.set_heat_mapping(custom_heat_mapping) +``` + +**Returns** + +| Name | Type | Description | +|--------|-----------------|---------------------------------------------| +| `heat` | `float` | `The heat can be a number between 0 and 1.` | + +  + ### def default_edge_thickness_factor_mapping(index, edge)
          The default thickness factor mapping for edges. @@ -1489,6 +1693,44 @@ In [4]: w.set_node_type_mapping(custom_node_type_mapping)   +### def default_node_parent_mapping(index, node)
+          The default parent mapping for nodes. + +Provides constant value of `None` for all nodes. + +**Parameters** + +| Name | parent | Description | +| ----------- | ----------- |------------------------------------| +| `index` | `int` | (optional) Position in nodes list. | +| `node` | `typing.Dict` | | + +**Notes** + +This is the default value for the [`node_parent_mapping`](#node_parent_mapping_property) property. +Can be 'overwritten' by setting the property with a function of the same signature. + +If the given mapping function has only one parameter (that is not typed as int), +then it will be called with the element (typing.Dict) as first parameter. + +**Example** + +```Python +In [1]: from yfiles_jupyter_graphs import GraphWidget +In [2]: w = GraphWidget() +In [3]: def custom_node_parent_mapping(node: typing.Dict): + ... +In [4]: w.set_node_parent_mapping(custom_node_parent_mapping) +``` + +**Returns** + +| Name | parent | Description | +| ----------- | ----------- |-----------------| +| `node_parent` | `None` | Node parent Id. | + +  + ### def default_node_position_mapping(index, node)
          The default position mapping for nodes. @@ -1527,6 +1769,45 @@ In [4]: w.set_node_position_mapping(custom_node_position_mapping)   +### def default_node_coordinate_mapping(index, node)
+          The default coordinate mapping for nodes. + +Provides constant value of `None` for all nodes. +There is no coordinate mapping unless explicitly set. + +**Parameters** + +| Name | Type | Description | +| ----------- | ----------- |------------------------------------| +| `index` | `int` | (optional) position in nodes list. | +| `node` | `typing.Dict` | | + +**Notes** + +This is the default value for the [`node_coordinate_mapping`](#node_coordinate_mapping_property) property. +Can be 'overwritten' by setting the property with a function of the same signature. + +If the given mapping function has only one parameter (that is not typed as int), +then it will be called with the element (typing.Dict) as first parameter. + +**Example** + +```Python +In [1]: from yfiles_jupyter_graphs import GraphWidget +In [2]: w = GraphWidget() +In [3]: def custom_node_coordinate_mapping(node: typing.Dict): + ... +In [4]: w.set_node_coordinate_mapping(custom_node_coordinate_mapping) +``` + +**Returns** + +| Name | Type | Description | +| ----------- | ----------- |---------------------------------------------| +| `node_coordinate` | `float 2-tuple` | geo coordinates in latitude and longitude . | + +  + ### def default_directed_mapping(index, edge)
          The default directed mapping for edges. diff --git a/mkdocs/index.md b/mkdocs/index.md index cd9cc99..3463186 100644 --- a/mkdocs/index.md +++ b/mkdocs/index.md @@ -46,6 +46,14 @@ A graph diagram visualization widget for Jupyter Notebooks and Labs powered by [ element color mapping
Make Data Dependent Property Changes + + heat mapping
+ Define a Heatmap Background + + + leaflet mapping
+ Use a Map Background + For example code look [here](https://github.com/yWorks/yfiles-jupyter-graphs/tree/master/examples). diff --git a/screenshots/heat_mapping.png b/screenshots/heat_mapping.png new file mode 100644 index 0000000..eb5c511 Binary files /dev/null and b/screenshots/heat_mapping.png differ diff --git a/screenshots/leaflet_map.png b/screenshots/leaflet_map.png new file mode 100644 index 0000000..28726a9 Binary files /dev/null and b/screenshots/leaflet_map.png differ