Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/graph analysis ipyleaflet visualisation #47

Merged
Merged
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
9dc3a17
exp: add notebook with prototype ipyleaflet viewer
rdnfn Feb 26, 2021
0b843a7
feature: add ipyleaflet based GeoGraphViewer class
rdnfn Mar 1, 2021
87897e6
Merge branch 'feature/graph-analysis' into feature/graph-analysis-ipy…
rdnfn Mar 1, 2021
ff2af92
feature: add habitats to GeoGraphViewer
rdnfn Mar 1, 2021
819f2f1
feature: add intermediate progress on layer_dict in geograph viewer
rdnfn Mar 2, 2021
ec75c28
feature: use choropleth for graph pgons
rdnfn Mar 3, 2021
cfd6fa3
rdnfn Mar 3, 2021
5670920
fix: update graph settings to new layer_dict
rdnfn Mar 3, 2021
95deef0
feature: add patch hover widget
rdnfn Mar 3, 2021
eeda4f4
feature: add demo notebook
rdnfn Mar 3, 2021
51a2ce3
feature: add geographviewer test notebook
rdnfn Mar 3, 2021
92ad6f3
Merge branch 'feature/graph-analysis' into feature/graph-analysis-ipy…
rdnfn Mar 3, 2021
d1e3e8d
feature: add habitats to demo
rdnfn Mar 3, 2021
ee97e47
feature: make hover widget universal for all graph pgons
rdnfn Mar 3, 2021
7260260
feature: add text to demo notebook
rdnfn Mar 3, 2021
5abab36
Merge remote-tracking branch 'origin/master' into feature/graph-analy…
rdnfn Mar 3, 2021
0704a69
feature: add scale to viewer
rdnfn Mar 3, 2021
710fa90
feature: update demo notebook with recent progress
rdnfn Mar 3, 2021
91f1d3a
feature: add new layer tab styling and area units
rdnfn Mar 3, 2021
2494fb0
feature: add toggle buttons to layer tab in viewer
rdnfn Mar 3, 2021
106905b
fix: remove unnecessary print statements
rdnfn Mar 3, 2021
7093936
Merge remote-tracking branch 'origin/feature/graph-analysis-metrics' …
rdnfn Mar 3, 2021
89b9025
feature: add metrics to viewer
rdnfn Mar 4, 2021
333d31b
feature: add video to demo notebook
rdnfn Mar 4, 2021
56ea428
tidy: small fixes to demo notebook
rdnfn Mar 4, 2021
29eaa5f
feature: add VisibilityWidget
rdnfn Mar 9, 2021
137f847
aux: update requirements to include ipyleaflet and jupyter lab
rdnfn Mar 9, 2021
2ea1756
aux: refactor visualisation.py into multiple modules
rdnfn Mar 9, 2021
f909609
aux: update comments and name of current logging method
rdnfn Mar 9, 2021
1dc27bd
feature: add current map and graph traits to GeoGraphViewer
rdnfn Mar 9, 2021
cbefec2
docs: add docstring to visibility widget
rdnfn Mar 9, 2021
064e00e
aux: remove unused method from GeoGraphViewer
rdnfn Mar 9, 2021
609919e
feature: refactor checkbox control into separate widget
rdnfn Mar 9, 2021
13a93a9
feature: factor out timeline widget
rdnfn Mar 9, 2021
74f10e6
docs: update comments in control widgets
rdnfn Mar 9, 2021
e482c46
feature: refactor metrics widget out of GeoGraphViewer
rdnfn Mar 9, 2021
a96a053
feature: refactor settings into separate widget
rdnfn Mar 9, 2021
b2228ae
feature: add logger to GeoGraphViewer
rdnfn Mar 12, 2021
ef08063
feature: add logger to control widgets
rdnfn Mar 12, 2021
fbae910
feature: create joint widget from control widgets
rdnfn Mar 12, 2021
41f0738
feature: add separate hover widget to control widgets
rdnfn Mar 12, 2021
815f7f5
Merge remote-tracking branch 'origin/feature/graph-analysis' into fea…
rdnfn Mar 12, 2021
1599070
feature: add new metric_list agument to viewer
rdnfn Mar 12, 2021
e891bc7
feature: add visualisation of components
rdnfn Mar 12, 2021
f01f78c
fix: remove unused variable in viewer
rdnfn Mar 12, 2021
02adc68
fix: update demo notebook to new visualisation module
rdnfn Mar 12, 2021
3a918cd
feature: create combined graph control widget
rdnfn Mar 13, 2021
b6a528b
feature: add styling widgets
rdnfn Mar 13, 2021
9ae1571
aux: update GeoGraphViewer to new control widget
rdnfn Mar 13, 2021
330764c
feature: add reimplementation of add_layer method
rdnfn Mar 13, 2021
a244d94
feature: add additional info to metrics widget
rdnfn Mar 13, 2021
563c9ca
feature: make metrics widget scrollable
rdnfn Mar 13, 2021
4763d5c
fix: make radio visibility buttons adapt to graph name length
rdnfn Mar 13, 2021
bbe1eb4
docs: add new visualisation folder to readme
rdnfn Mar 15, 2021
e0c10ff
feature: add LayerButtonWidget
rdnfn Mar 15, 2021
95b53f9
feature: add disconnected and poorly connected nodes
rdnfn Mar 15, 2021
f14ba09
aux: minor styling update
rdnfn Mar 15, 2021
5777c7a
feature: add style module to viewer
rdnfn Mar 15, 2021
8628b47
aux: add 10 squares test case
rdnfn Mar 15, 2021
245f319
feature: add satellite data to demo notebook
rdnfn Mar 15, 2021
9fc7a6c
aux: add test notebook for viewer
rdnfn Mar 15, 2021
981da2d
aux: add UI experiments notebook
rdnfn Mar 15, 2021
5672475
Merge remote-tracking branch 'origin/feature/graph-analysis' into fea…
rdnfn Mar 15, 2021
1aac0b5
docs: update docs and clean up CRS definition
rdnfn Mar 15, 2021
54ac8c9
docs: update docstrings of control widgets
rdnfn Mar 15, 2021
524bcca
fix: set correct CRS for graph geometries
rdnfn Mar 15, 2021
2ac8f4e
feature: add appropriate icons to buttons
rdnfn Mar 15, 2021
42eae9e
fix: add suggested performance improvements to viewer
rdnfn Mar 17, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
497 changes: 497 additions & 0 deletions notebooks/exploratory/rdnfn-11-widget-experiments-ui.ipynb

Large diffs are not rendered by default.

89 changes: 89 additions & 0 deletions notebooks/exploratory/rdnfn-13-geographviewer-test.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "polyphonic-gateway",
"metadata": {},
"outputs": [],
"source": [
"%load_ext autoreload\n",
"%autoreload 2\n",
"%config IPCompleter.greedy=True"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "educational-texture",
"metadata": {},
"outputs": [],
"source": [
"from src.models import geograph\n",
"from src.data_loading import test_data\n",
"from src.constants import PREFERRED_CRS\n",
"\n",
"test_gdf = test_data.get_polygon_gdf(\"chernobyl_10_squares_touching\")\n",
"test_gdf['class_label'] = list(range(10))\n",
"graph = geograph.GeoGraph(test_gdf, crs=PREFERRED_CRS)\n",
"graph.add_habitat(name=\"eagle_habitat\", valid_classes=[0], max_travel_distance=10000)\n",
"\n",
"from src.visualisation import geoviewer, control_widgets"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "distinct-kenya",
"metadata": {},
"outputs": [],
"source": [
"import ipyleaflet\n",
"\n",
"viewer = geoviewer.GeoGraphViewer()\n",
"viewer.add_graph(graph, name='main_graph')\n",
"viewer.add_layer(ipyleaflet.basemaps.Esri.WorldImagery)\n",
"viewer.enable_graph_controls()\n",
"#viewer.log_handler.show_logs()\n",
"viewer"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "unsigned-butterfly",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "touched-beaver",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "mental-garden",
"metadata": {},
"outputs": [],
"source": [
"%load_ext autoreload\n",
"%autoreload 2\n",
"%config IPCompleter.greedy=True"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "turned-dispatch",
"metadata": {},
"outputs": [],
"source": [
"from src.models import geograph, visualisation\n",
"from src.data_loading import test_data\n",
"\n",
"test_gdf = test_data.get_polygon_gdf(\"chernobyl_squares_touching\")\n",
"test_gdf['class_label'] = 0\n",
"test_gdf = test_gdf[['geometry', 'class_label']]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "diagnostic-motor",
"metadata": {},
"outputs": [],
"source": [
"test_gdf2 = test_data.get_polygon_gdf(\"chernobyl_squares_apart\")\n",
"test_gdf2['class_label'] = 0\n",
"test_gdf2 = test_gdf2[['geometry', 'class_label']]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "thirty-pulse",
"metadata": {},
"outputs": [],
"source": [
"graph = geograph.GeoGraph(test_gdf)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "identified-right",
"metadata": {},
"outputs": [],
"source": [
"graph2 = geograph.GeoGraph(test_gdf2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "hungarian-clear",
"metadata": {},
"outputs": [],
"source": [
"#widgets.link((m, 'layers'), (viewer, 'layers'))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fourth-injury",
"metadata": {},
"outputs": [],
"source": [
"from traitlets import HasTraits, Integer, observe, Dict, Tuple\n",
"import ipywidgets as widgets\n",
"from ipywidgets import HTML\n",
"import ipyleaflet\n",
"from ipyleaflet import TileLayer, GeoData, basemaps\n",
"import copy\n",
"import markdown \n",
"import pandas as pd\n",
"\n",
"from src.constants import CHERNOBYL_COORDS_WGS84, WGS84\n",
"\n",
"class GeoGraphViewer(ipyleaflet.Map):\n",
" #layers = Tuple()\n",
" log_tab = widgets.Output(layout={'border': '1px solid black'})\n",
" \n",
" def __init__(self):\n",
" super().__init__(center=CHERNOBYL_COORDS_WGS84, zoom = 7, scroll_wheel_zoom=True)\n",
" self.map = ipyleaflet.Map(center=CHERNOBYL_COORDS_WGS84, zoom = 7, scroll_wheel_zoom=True)\n",
" self.layer_dict = dict(Basemap=dict(layer=TileLayer(base=True, max_zoom=19, min_zoom=4),active=True, layer_type='map'))\n",
" #self.create_traitlets_links()\n",
" self.custom_style = dict(style={'color': 'black', 'fillColor': '#3366cc'},\n",
" hover_style={'fillColor': 'red' , 'fillOpacity': 0.2},\n",
" point_style={'radius': 10, 'color': 'red', 'fillOpacity': 0.8, 'fillColor': 'blue', 'weight': 3})\n",
" \n",
" @log_tab.capture() \n",
" def add_graph(self,graph, name = 'Graph'):\n",
" nodes, edges = visualisation.create_node_edge_geometries(graph.graph)\n",
" graph_geometries = pd.concat([nodes,edges]).reset_index()\n",
" graph_geo_data = ipyleaflet.GeoData(geo_dataframe = graph_geometries.to_crs(WGS84),\n",
" style ={'color': 'black', 'fillColor': '#3366cc'},\n",
" hover_style={'fillColor': 'red' , 'fillOpacity': 0.2},\n",
" point_style={'radius': 10, 'color': 'red', 'fillOpacity': 0.8, 'fillColor': 'blue', 'weight': 3},\n",
" name = name)\n",
" self.layer_dict[name] = dict(layer=graph_geo_data, active=True, layer_type='graph')\n",
" self.layer_update()\n",
" \n",
" @log_tab.capture() \n",
" def layer_update(self):\n",
" self.layers = tuple([entry['layer'] for entry in self.layer_dict.values() if entry['active']])\n",
" \n",
" @log_tab.capture() \n",
" def set_graph_style(self, radius):\n",
" for name, entry in self.layer_dict.items():\n",
" if entry['layer_type'] == \"graph\":\n",
" layer = entry['layer']\n",
" #layer.point_style['radius'] = radius #doesn't work because traitlet change not observed\n",
" self.custom_style['point_style']['radius'] = radius\n",
" layer = ipyleaflet.GeoData(geo_dataframe = layer.geo_dataframe, name = layer.name, **self.custom_style)\n",
" self.layer_dict[name]['layer'] = layer\n",
" self.layer_update()\n",
" \n",
" \n",
" @log_tab.capture() \n",
" def remove_graphs(self, button):\n",
" for idx, layer in enumerate(self.layers):\n",
" if layer.name[0:5] == \"Graph\":\n",
" self.remove_layer(layer)\n",
" \n",
" @log_tab.capture() \n",
" def checkbox_layer_switch(self, change_dict):\n",
" if change_dict['name'] == 'value':\n",
" layer_name = change_dict['owner'].description\n",
" self.layer_dict[layer_name]['active'] = change_dict['new']\n",
" \n",
" self.layer_update()\n",
" \n",
" \n",
" @log_tab.capture() \n",
" def create_habitat_tab(self):\n",
" checkboxes = []\n",
" for name in self.layer_dict.keys():\n",
" checkbox = widgets.Checkbox(\n",
" value=True,\n",
" description=name,\n",
" disabled=False,\n",
" indent=False\n",
" )\n",
" checkbox.observe(self.checkbox_layer_switch)\n",
" checkboxes.append(checkbox)\n",
" \n",
" self.add_habitat_button = widgets.Button(\n",
" description='Remove graph',\n",
" disabled=False,\n",
" button_style='', # 'success', 'info', 'warning', 'danger' or ''\n",
" tooltip='Click me',\n",
" icon='plus' # (FontAwesome names without the `fa-` prefix)\n",
" )\n",
" self.add_habitat_button.on_click(self.remove_graphs)\n",
" \n",
" habitat_accordion = widgets.Accordion(children=[self.add_habitat_button])\n",
" habitat_accordion.set_title(0, 'Habitats')\n",
" \n",
" checkboxes.append(habitat_accordion)\n",
" \n",
" habitat_tab = widgets.VBox(checkboxes)\n",
" \n",
" return habitat_tab\n",
" \n",
" @log_tab.capture() \n",
" def create_diff_tab(self):\n",
" time_slider1 = widgets.IntSlider(min=1960, max=2021, step=1, value=1990, description=\"Start time:\")\n",
" time_slider2 = widgets.IntSlider(min=1960, max=2021, step=1, value=2010, description=\"End time:\")\n",
" \n",
" compute_node_button = widgets.Button(\n",
" description='Compute node diff',\n",
" disabled=False,\n",
" button_style='', # 'success', 'info', 'warning', 'danger' or ''\n",
" tooltip='This computes the differences between of the nodes in the graph at start time and the graph at end time.',\n",
" icon='' # (FontAwesome names without the `fa-` prefix)\n",
" )\n",
" \n",
" compute_pgon_button = widgets.Button(\n",
" description='Compute polygon diff',\n",
" disabled=False,\n",
" button_style='', # 'success', 'info', 'warning', 'danger' or ''\n",
" tooltip='This computes the differences between of the polygons in the graph at start time and the graph at end time.',\n",
" icon='' # (FontAwesome names without the `fa-` prefix)\n",
" )\n",
" \n",
" diff_tab = widgets.VBox([time_slider1, time_slider2, compute_node_button, compute_pgon_button])\n",
" \n",
" return diff_tab\n",
" \n",
" @log_tab.capture() \n",
" def create_settings_tab(self):\n",
" radius_slider = widgets.FloatSlider(min=0.01, max=100.0, step=0.005, value=5.0, description=\"Node radius:\")\n",
" w = widgets.interactive(self.set_graph_style, radius=radius_slider)\n",
" \n",
" zoom_slider = widgets.FloatSlider(description='Zoom level:', min=0, max=15, value=7)\n",
" widgets.jslink((zoom_slider, 'value'), (self, 'zoom'))\n",
" \n",
" settings_tab = widgets.VBox([zoom_slider, radius_slider, radius_slider, radius_slider, radius_slider, radius_slider])\n",
" \n",
" return settings_tab\n",
" \n",
" @log_tab.capture() \n",
" def add_widgets(self):\n",
" self.add_settings_widget()\n",
" self.add_control(ipyleaflet.FullScreenControl())\n",
" \n",
" @log_tab.capture()\n",
" def add_settings_widget(self):\n",
" habitats_tab = self.create_habitat_tab()\n",
" diff_tab = self.create_diff_tab()\n",
" settings_tab = self.create_settings_tab()\n",
" \n",
" tab_nest = widgets.Tab()\n",
" tab_nest.children = [habitats_tab, settings_tab, diff_tab, settings_tab, self.log_tab]\n",
" for i, title in enumerate([\"Habitats\",\"Metrics\",\"Diff\",\"Settings\",\"Log\"]):\n",
" tab_nest.set_title(i, title)\n",
" \n",
" header = widgets.HTML(markdown.markdown(\"\"\"  GeoGraph  \"\"\"))\n",
" widget_control2 = ipyleaflet.WidgetControl(widget=header, position='bottomright')\n",
" self.add_control(widget_control2)\n",
" \n",
" self.control = ipyleaflet.LayersControl(position='topleft')\n",
" self.add_control(self.control)\n",
" \n",
" widget_panel = widgets.VBox([tab_nest])\n",
" widget_control = ipyleaflet.WidgetControl(widget=widget_panel, position='topright')\n",
" self.add_control(widget_control)\n",
" \n",
"\n",
"\n",
" \n",
"viewer = GeoGraphViewer()\n",
"viewer.add_graph(graph)\n",
"viewer.add_graph(graph2, name = 'Graph 2')\n",
"viewer.create_habitat_tab()\n",
"viewer.add_widgets()\n",
"viewer"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "copyrighted-denmark",
"metadata": {},
"outputs": [],
"source": [
"viewer.log_tab"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "extraordinary-excess",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.8.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}