Skip to content

Commit 2ba95c9

Browse files
authored
Multi-range sliders for DataFilterExtension (#340)
Closes #339 ### Change list - Add `MultiRangeSlider`, a subclass of `ipywidgets.VBox` to connect multiple float range sliders - Add to API docs
1 parent 949b272 commit 2ba95c9

File tree

6 files changed

+150
-2
lines changed

6 files changed

+150
-2
lines changed

docs/api/controls.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# lonboard.controls
2+
3+
::: lonboard.controls.MultiRangeSlider

lonboard/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Python library for fast, interactive geospatial vector data visualization in Jupyter.
22
"""
33

4-
from . import colormap, traits
4+
from . import colormap, controls, traits
55
from ._layer import (
66
BaseArrowLayer,
77
BaseLayer,

lonboard/controls.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
from functools import partial
2+
from typing import Sequence
3+
4+
import traitlets
5+
from ipywidgets import FloatRangeSlider
6+
from ipywidgets.widgets.trait_types import TypedTuple
7+
8+
# Import from source to allow mkdocstrings to link to base class
9+
from ipywidgets.widgets.widget_box import VBox
10+
11+
12+
class MultiRangeSlider(VBox):
13+
"""A widget for multiple ranged sliders.
14+
15+
This is designed to be used with the
16+
[DataFilterExtension][lonboard.experimental.DataFilterExtension] when you want to
17+
filter on 2 to 4 columns on the same time.
18+
19+
If you have only a single filter, use an ipywidgets
20+
[FloatRangeSlider][ipywidgets.widgets.widget_float.FloatRangeSlider] directly.
21+
22+
# Example
23+
24+
```py
25+
from ipywidgets import FloatRangeSlider
26+
27+
slider1 = FloatRangeSlider(
28+
value=(2, 5),
29+
min=0,
30+
max=10,
31+
step=0.1,
32+
description="First slider: "
33+
)
34+
slider2 = FloatRangeSlider(
35+
value=(30, 40),
36+
min=0,
37+
max=50,
38+
step=1,
39+
description="Second slider: "
40+
)
41+
multi_slider = MultiRangeSlider([slider1, slider2])
42+
multi_slider
43+
```
44+
45+
Then to propagate updates to a rendered layer, call
46+
[jsdlink][ipywidgets.widgets.widget_link.jsdlink] to connect the two widgets.
47+
48+
```py
49+
from ipywidgets import jsdlink
50+
51+
jsdlink(
52+
(multi_slider, "value"),
53+
(layer, "filter_range")
54+
)
55+
```
56+
57+
As you change the slider, the `filter_range` value on the layer class should be
58+
updated.
59+
"""
60+
61+
# We use a tuple to force reassignment to update the list
62+
# This is because list mutations do not get propagated as events
63+
# https://github.com/jupyter-widgets/ipywidgets/blob/b2531796d414b0970f18050d6819d932417b9953/python/ipywidgets/ipywidgets/widgets/widget_box.py#L52-L54
64+
value = TypedTuple(trait=TypedTuple(trait=traitlets.Float())).tag(sync=True)
65+
66+
def __init__(self, children: Sequence[FloatRangeSlider], **kwargs):
67+
# We manage a list of lists to match what deck.gl expects for the
68+
# DataFilterExtension
69+
def callback(change, *, i: int):
70+
value = list(self.value)
71+
value[i] = change["new"]
72+
self.set_trait("value", value)
73+
self.send_state("value")
74+
75+
initial_values = []
76+
for i, child in enumerate(children):
77+
func = partial(callback, i=i)
78+
child.observe(func, "value")
79+
initial_values.append(child.value)
80+
81+
super().__init__(children, value=initial_values, **kwargs)

lonboard/experimental/layer_extension.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,68 @@ class DataFilterExtension(BaseExtension):
148148
)
149149
```
150150
151+
The `DataFilterExtension` allows filtering on 1 to 4 attributes at the same time. So
152+
if you have four numeric columns of interest, you can filter on the intersection of
153+
all of them.
154+
155+
For easy visualization, we suggest connecting the `DataFilterExtension` to an
156+
interactive slider from `ipywidgets`.
157+
158+
```py
159+
from ipywidgets import FloatRangeSlider
160+
161+
slider = FloatRangeSlider(
162+
value=(2, 5),
163+
min=0,
164+
max=10,
165+
step=0.1,
166+
description="Slider: "
167+
)
168+
slider
169+
170+
jsdlink(
171+
(slider, "value"),
172+
(layer, "filter_range")
173+
)
174+
```
175+
176+
If you have 2 to 4 columns, use a
177+
[`MultiRangeSlider`][lonboard.controls.MultiRangeSlider], which combines multiple
178+
`FloatRangeSlider` objects in a form that the `DataFilterExtension` expects.
179+
180+
```py
181+
from ipywidgets import FloatRangeSlider, jsdlink
182+
183+
slider1 = FloatRangeSlider(
184+
value=(2, 5),
185+
min=0,
186+
max=10,
187+
step=0.1,
188+
description="First slider: "
189+
)
190+
slider2 = FloatRangeSlider(
191+
value=(30, 40),
192+
min=0,
193+
max=50,
194+
step=1,
195+
description="Second slider: "
196+
)
197+
multi_slider = MultiRangeSlider([slider1, slider2])
198+
multi_slider
199+
200+
jsdlink(
201+
(multi_slider, "value"),
202+
(layer, "filter_range")
203+
)
204+
```
205+
206+
# Important notes
207+
208+
- The DataFilterExtension only supports float32 data, so integer data will be casted
209+
to float32.
210+
- The DataFilterExtension copies all data referenced by `get_filter_value` to the
211+
GPU, so it will increase memory pressure on the GPU.
212+
151213
# Layer Properties
152214
153215
## `filter_enabled`

mkdocs.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ nav:
4444
- api/layers/base-layer.md
4545
- api/basemap.md
4646
- api/colormap.md
47+
- api/controls.md
4748
- api/traits.md
4849
- Experimental:
4950
- Layer Extensions:
@@ -144,6 +145,7 @@ plugins:
144145
- https://geoarrow.github.io/geoarrow-rs/python/latest/objects.inv
145146
- https://geopandas.org/en/stable/objects.inv
146147
- https://geopy.readthedocs.io/en/stable/objects.inv
148+
- https://ipywidgets.readthedocs.io/en/stable/objects.inv
147149
- https://matplotlib.org/stable/objects.inv
148150
- https://numpy.org/doc/stable/objects.inv
149151
- https://pandas.pydata.org/pandas-docs/stable/objects.inv

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "lonboard"
3-
version = "0.5.0"
3+
version = "0.6.0-beta.1"
44
description = "Python library for fast, interactive geospatial vector data visualization in Jupyter."
55
authors = ["Kyle Barron <[email protected]>"]
66
license = "MIT"

0 commit comments

Comments
 (0)