Skip to content

Commit

Permalink
Small nits
Browse files Browse the repository at this point in the history
  • Loading branch information
kylebarron committed Oct 3, 2024
1 parent 71d9423 commit 5109393
Showing 1 changed file with 45 additions and 35 deletions.
80 changes: 45 additions & 35 deletions examples/linked-maps.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,31 @@
"source": [
"## Linked Maps\n",
"\n",
"This notebook demonstrates how you can link two different lonboard maps using the ipywidgets observe method, so panning/zooming one map will automatically pan/zoom the other map.\n",
"This notebook demonstrates how you can link two different Lonboard maps using the [`ipywidgets.observe`](https://ipywidgets.readthedocs.io/en/8.1.5/examples/Widget%20Events.html#traitlet-events) method, so panning/zooming one map will automatically pan/zoom the other map.\n",
"\n",
"Linked maps can be useful in a variety of situations:\n",
" * Before/After maps, where one map shows data before something happened and the other after the event \n",
" * To showcase results of different processing methodologies\n",
" * To simply present multiple maps with different data that doesn't easily fit on one map "
"\n",
"- Before/After maps, where one map shows data before something happened and the other after the event\n",
"- To showcase results of different processing methodologies\n",
"- To simply present multiple maps with different data that doesn't easily fit on one map\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from functools import partial\n",
"from typing import List\n",
"\n",
"import ipywidgets as widgets\n",
"import traitlets\n",
"\n",
"import lonboard\n",
"from lonboard import Map\n",
"import traitlets"
"from lonboard.basemap import CartoBasemap\n",
"from lonboard.models import ViewState"
]
},
{
Expand All @@ -35,11 +39,11 @@
"source": [
"## Create the maps\n",
"\n",
"Because layers don't matter for this example, we are going to create two maps without any layers, one map using the Positron basemap, and another using the Dark Matter basemap. \n",
"Because layers don't matter for this example, we are going to create two maps without any layers, one map using the Positron basemap, and another using the Dark Matter basemap.\n",
"\n",
"To start, the view state on the Positron map to be focused on the Gateway Arch in St. Louis Missouri, and the Dark Matter map will be centered on the Statue of Liberty in New York City, New York.\n",
"\n",
"We'll present the two maps side by side in an ipywidgets HBox to keep them tidy. Setting the layout of the maps to \"flex='1'\" will allow the maps to display inside the HBox."
"We'll present the two maps side by side in an ipywidgets HBox to keep them tidy. Setting the layout of the maps to \"flex='1'\" will allow the maps to display inside the HBox.\n"
]
},
{
Expand All @@ -51,29 +55,29 @@
"## Create postitron map focused on the arch\n",
"positron_map = Map(\n",
" layers=[],\n",
" basemap_style = lonboard.basemap.CartoBasemap.Positron,\n",
" basemap_style=CartoBasemap.Positron,\n",
" view_state={\n",
" \"longitude\":-90.1849, \n",
" \"latitude\":38.6245, \n",
" \"zoom\":16, \n",
" \"pitch\":0, \n",
" \"bearing\":0\n",
" \"longitude\": -90.1849,\n",
" \"latitude\": 38.6245,\n",
" \"zoom\": 16,\n",
" \"pitch\": 0,\n",
" \"bearing\": 0,\n",
" },\n",
" layout=widgets.Layout(flex=\"1\")\n",
" layout=widgets.Layout(flex=\"1\"),\n",
")\n",
"\n",
"## Create postitron map focused on the lady liberty\n",
"darkmatter_map = Map(\n",
" layers=[],\n",
" basemap_style = lonboard.basemap.CartoBasemap.DarkMatter,\n",
" basemap_style=CartoBasemap.DarkMatter,\n",
" view_state={\n",
" \"longitude\":-74.04454, \n",
" \"latitude\":40.6892, \n",
" \"zoom\":16, \n",
" \"pitch\":0, \n",
" \"bearing\":0\n",
" \"longitude\": -74.04454,\n",
" \"latitude\": 40.6892,\n",
" \"zoom\": 16,\n",
" \"pitch\": 0,\n",
" \"bearing\": 0,\n",
" },\n",
" layout=widgets.Layout(flex=\"1\")\n",
" layout=widgets.Layout(flex=\"1\"),\n",
")\n",
"\n",
"maps_box = widgets.HBox([positron_map, darkmatter_map])\n",
Expand All @@ -88,23 +92,28 @@
"\n",
"If you haven't yet run the cells below, you'll see that you can pan/zoom the two maps independent of one another.  Panning/zooming one map will not affect the other map.  After we run the code below though, the two maps will synchronize with each other, when we pan/zoom one map, the other map will automatically match the map that was modified.\n",
"\n",
"To achieve the view state synchronization, we'll write two simple callback function for each of the maps. The functions will receive events from the interaction with the maps, and if the interaction with the map changed the view_state, we'll set the view_state on the other map to match the view_state of the the map that we interacted with."
"To achieve the view state synchronization, we'll write two simple callback function for each of the maps. The functions will receive events from the interaction with the maps, and if the interaction with the map changed the view_state, we'll set the view_state on the other map to match the view_state of the the map that we interacted with.\n"
]
},
{
"cell_type": "code",
"execution_count": 14,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def sync_positron_to_darkmatter(x:traitlets.utils.bunch.Bunch=None) -> None:\n",
" if isinstance(x[\"new\"], lonboard.models.ViewState):\n",
"def sync_positron_to_darkmatter(event: traitlets.utils.bunch.Bunch) -> None:\n",
" if isinstance(event.get(\"new\"), ViewState):\n",
" darkmatter_map.view_state = positron_map.view_state\n",
"\n",
"\n",
"positron_map.observe(sync_positron_to_darkmatter)\n",
"\n",
"def sync_darkmatter_to_positron(x=None) -> None:\n",
" if isinstance(x[\"new\"], lonboard.models.ViewState):\n",
"\n",
"def sync_darkmatter_to_positron(event: traitlets.utils.bunch.Bunch) -> None:\n",
" if isinstance(event.get(\"new\"), ViewState):\n",
" positron_map.view_state = darkmatter_map.view_state\n",
"\n",
"\n",
"darkmatter_map.observe(sync_darkmatter_to_positron)"
]
},
Expand All @@ -114,21 +123,22 @@
"source": [
"## Linking the Maps (the more elegant/robust way)\n",
"\n",
"In the block above we are typing a lot of code, and the two functions are basically the same, just with hard coded maps to target in the functions, and we're explicitly calling the originating map's view_state even though the 'x[\"new\"]' actually is the view state.  Additionally if we had a lot of maps to sync, this would get out of hand quickly.  None of that is idea, but it makes the concept easy to understand.  Below is a better way to sync the maps, albeit a bit more abstract.\n",
"In the block above we are typing a lot of code, and the two functions are basically the same, just with hard coded maps to target in the functions, and we're explicitly calling the originating map's `view_state` even though the `event[\"new\"]` actually is the view state.  Additionally if we had a lot of maps to sync, this would get out of hand quickly. None of that is idea, but it makes the concept easy to understand. Below is a better way to sync the maps, albeit a bit more abstract.\n",
"\n",
"Luckily functools.patrtial can help us out.  Instead of writing a function per map, we can write one function that take the same events from the widget, but also another parameter which is a list of lonboard maps.  Then when we register the callback function with the map's observe() method, we pass partial as the function and tell partial to use the link_maps function and provide the list of the other maps to sync with this map.  This way we have one function that we wrote which can be used to sync any map with any number of other maps."
"Luckily `functools.partial` can help us out. Instead of writing a function per map, we can write one function that take the same events from the widget, but also another parameter which is a list of Lonboard maps. Then when we register the callback function with the map's `observe()` method, we pass partial as the function and tell partial to use the `link_maps` function and provide the list of the other maps to sync with this map. This way we have one function that we wrote which can be used to sync any map with any number of other maps.\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def link_maps(x:traitlets.utils.bunch.Bunch=None, other_maps:List[Map]=[]):\n",
" if isinstance(x[\"new\"], lonboard.models.ViewState):\n",
"def link_maps(event: traitlets.utils.bunch.Bunch, other_maps: List[Map] = []):\n",
" if isinstance(event.get(\"new\"), ViewState):\n",
" for lonboard_map in other_maps:\n",
" lonboard_map.view_state = x[\"new\"]\n",
" lonboard_map.view_state = event[\"new\"]\n",
"\n",
"\n",
"positron_map.observe(partial(link_maps, other_maps=[darkmatter_map]))\n",
"darkmatter_map.observe(partial(link_maps, other_maps=[positron_map]))"
Expand Down Expand Up @@ -158,7 +168,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.4"
"version": "3.11.8"
}
},
"nbformat": 4,
Expand Down

0 comments on commit 5109393

Please sign in to comment.