Improve efficiency with in-place updates
Browse files
app.py
CHANGED
|
@@ -1,98 +1,124 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Checkout https://awesome-panel.org/resources/lonboard_dashboard/
|
| 3 |
-
"""
|
| 4 |
-
# pip install panel colorcet ipywidgets_bokeh geopandas palettable lonboard
|
| 5 |
import colorcet as cc
|
| 6 |
import geopandas as gpd
|
|
|
|
| 7 |
|
| 8 |
from lonboard import Map, PathLayer
|
| 9 |
from lonboard.colormap import apply_continuous_cmap
|
|
|
|
| 10 |
from palettable.palette import Palette
|
| 11 |
|
| 12 |
import panel as pn
|
| 13 |
|
|
|
|
|
|
|
| 14 |
url = "https://naciscdn.org/naturalearth/10m/cultural/ne_10m_roads_north_america.zip"
|
| 15 |
-
path = "ne_10m_roads_north_america.zip"
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
)
|
| 21 |
-
except:
|
| 22 |
-
gdf = pn.state.as_cached(
|
| 23 |
-
"ne_10m_roads_north_america", gpd.read_file, filename=url, engine="pyogrio"
|
| 24 |
-
)
|
| 25 |
|
|
|
|
| 26 |
state_options = sorted(state for state in gdf["state"].unique() if state)
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
def to_rgb(hex: str) -> list:
|
| 30 |
h = hex.strip("#")
|
| 31 |
return list(int(h[i : i + 2], 16) for i in (0, 2, 4))
|
| 32 |
|
| 33 |
-
|
| 34 |
def to_palette(cmap) -> Palette:
|
| 35 |
"""Returns the ColorCet colormap as a palettable Palette"""
|
| 36 |
colors = [to_rgb(item) for item in cmap]
|
| 37 |
return Palette(name="colorcet", map_type="colorcet", colors=colors)
|
| 38 |
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
)
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
def
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
pn.template.FastListTemplate(
|
| 94 |
logo="https://panel.holoviz.org/_static/logo_horizontal_dark_theme.png",
|
| 95 |
-
title="Works with
|
| 96 |
-
|
| 97 |
-
|
|
|
|
| 98 |
).servable()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import colorcet as cc
|
| 2 |
import geopandas as gpd
|
| 3 |
+
import param
|
| 4 |
|
| 5 |
from lonboard import Map, PathLayer
|
| 6 |
from lonboard.colormap import apply_continuous_cmap
|
| 7 |
+
from lonboard._viewport import compute_view
|
| 8 |
from palettable.palette import Palette
|
| 9 |
|
| 10 |
import panel as pn
|
| 11 |
|
| 12 |
+
pn.extension("ipywidgets")
|
| 13 |
+
|
| 14 |
url = "https://naciscdn.org/naturalearth/10m/cultural/ne_10m_roads_north_america.zip"
|
|
|
|
| 15 |
|
| 16 |
+
@pn.cache
|
| 17 |
+
def get_data():
|
| 18 |
+
return gpd.read_file(filename=url, engine="pyogrio")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
+
gdf = get_data()
|
| 21 |
state_options = sorted(state for state in gdf["state"].unique() if state)
|
| 22 |
|
| 23 |
+
description = """# Lonboard
|
| 24 |
+
|
| 25 |
+
A Python library for **fast, interactive geospatial vector data visualization** in Jupyter (and Panel).
|
| 26 |
+
|
| 27 |
+
By utilizing new technologies like `GeoArrow` and `GeoParquet` in conjunction with GPU-based map rendering, Lonboard aims to enable visualizing large geospatial datasets interactively through a simple interface."""
|
| 28 |
+
|
| 29 |
+
logo = pn.pane.Image(
|
| 30 |
+
"https://github.com/developmentseed/lonboard/raw/main/assets/dalle-lonboard.jpg"
|
| 31 |
+
)
|
| 32 |
|
| 33 |
def to_rgb(hex: str) -> list:
|
| 34 |
h = hex.strip("#")
|
| 35 |
return list(int(h[i : i + 2], 16) for i in (0, 2, 4))
|
| 36 |
|
|
|
|
| 37 |
def to_palette(cmap) -> Palette:
|
| 38 |
"""Returns the ColorCet colormap as a palettable Palette"""
|
| 39 |
colors = [to_rgb(item) for item in cmap]
|
| 40 |
return Palette(name="colorcet", map_type="colorcet", colors=colors)
|
| 41 |
|
| 42 |
+
class StateViewer(pn.viewable.Viewer):
|
| 43 |
+
value: Map = param.ClassSelector(class_=Map, doc="The map object", constant=True)
|
| 44 |
+
state: str = param.Selector(default="California", objects=state_options)
|
| 45 |
+
cmap: str = param.Selector(default=cc.fire, objects=cc.palette, label="cmap by Colorcet")
|
| 46 |
+
alpha: float = param.Number(default=0.8, bounds=(0, 1))
|
| 47 |
+
|
| 48 |
+
data = param.DataFrame()
|
| 49 |
+
|
| 50 |
+
def __init__(self, **params):
|
| 51 |
+
params["value"] = params.get("value", Map(layers=[], view_state={"longitude": -119.81446785010868, "latitude": 36.08305565437565, "zoom": 5}))
|
| 52 |
+
|
| 53 |
+
super().__init__(**params)
|
| 54 |
+
|
| 55 |
+
self.value.layout.width=self.value.layout.height="100%"
|
| 56 |
+
|
| 57 |
+
self.description = pn.Column(pn.pane.Markdown(description, margin=5), logo)
|
| 58 |
+
self.settings = pn.Column(
|
| 59 |
+
pn.widgets.Select.from_param(self.param.state, sizing_mode="stretch_width"),
|
| 60 |
+
pn.widgets.ColorMap.from_param(
|
| 61 |
+
self.param.cmap,
|
| 62 |
+
ncols=3,
|
| 63 |
+
swatch_width=100,
|
| 64 |
+
name="cmap by Colorcet",
|
| 65 |
+
sizing_mode="stretch_width",
|
| 66 |
+
),
|
| 67 |
+
pn.widgets.FloatSlider.from_param(
|
| 68 |
+
self.param.alpha, sizing_mode="stretch_width"
|
| 69 |
+
),
|
| 70 |
+
margin=5,
|
| 71 |
+
sizing_mode="fixed",
|
| 72 |
+
width=300,
|
| 73 |
+
)
|
| 74 |
+
self.view = pn.Column(
|
| 75 |
+
self._title, pn.pane.IPyWidget(self.value, sizing_mode="stretch_both")
|
| 76 |
+
)
|
| 77 |
+
self._layout = pn.Row(
|
| 78 |
+
pn.Column(self.settings, sizing_mode="fixed", width=300),
|
| 79 |
+
self.view,
|
| 80 |
+
sizing_mode="stretch_both",
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
def __panel__(self):
|
| 84 |
+
return self._layout
|
| 85 |
+
|
| 86 |
+
@param.depends("state", watch=True, on_init=True)
|
| 87 |
+
def _update_data(self):
|
| 88 |
+
self.data = gdf[gdf["state"] == self.state]
|
| 89 |
+
|
| 90 |
+
def _get_color(self):
|
| 91 |
+
palette = to_palette(self.cmap)
|
| 92 |
+
normalized_scale_rank = (self.data["scalerank"] - 3) / 9
|
| 93 |
+
return apply_continuous_cmap(normalized_scale_rank, palette, alpha=self.alpha)
|
| 94 |
+
|
| 95 |
+
@param.depends("data", watch=True)
|
| 96 |
+
def _update_value(self):
|
| 97 |
+
layer = PathLayer.from_geopandas(self.data, width_min_pixels=0.8)
|
| 98 |
+
layer.get_color = self._get_color()
|
| 99 |
+
self.value.layers = [layer]
|
| 100 |
+
self._fly_to_center()
|
| 101 |
+
|
| 102 |
+
def _fly_to_center(self):
|
| 103 |
+
computed_view_state = compute_view(self.value.layers)
|
| 104 |
+
self.value.fly_to(
|
| 105 |
+
**computed_view_state,
|
| 106 |
+
duration=1000,
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
@param.depends("cmap", "alpha", watch=True)
|
| 110 |
+
def _update_layer_get_color(self):
|
| 111 |
+
self.value.layers[0].get_color = self._get_color()
|
| 112 |
+
|
| 113 |
+
@param.depends("state")
|
| 114 |
+
def _title(self):
|
| 115 |
+
return f"# North America Roads: {self.state}"
|
| 116 |
+
|
| 117 |
+
viewer = StateViewer()
|
| 118 |
pn.template.FastListTemplate(
|
| 119 |
logo="https://panel.holoviz.org/_static/logo_horizontal_dark_theme.png",
|
| 120 |
+
title="Works with Lonboard",
|
| 121 |
+
sidebar=[viewer.description, viewer.settings],
|
| 122 |
+
main=[viewer.view],
|
| 123 |
+
main_layout=None,
|
| 124 |
).servable()
|