Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/api/controls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# lonboard.controls

::: lonboard.controls.MultiRangeSlider
2 changes: 1 addition & 1 deletion lonboard/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Python library for fast, interactive geospatial vector data visualization in Jupyter.
"""

from . import colormap, traits
from . import colormap, controls, traits
from ._layer import (
BaseArrowLayer,
BaseLayer,
Expand Down
81 changes: 81 additions & 0 deletions lonboard/controls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
from functools import partial
from typing import Sequence

import traitlets
from ipywidgets import FloatRangeSlider
from ipywidgets.widgets.trait_types import TypedTuple

# Import from source to allow mkdocstrings to link to base class
from ipywidgets.widgets.widget_box import VBox


class MultiRangeSlider(VBox):
"""A widget for multiple ranged sliders.

This is designed to be used with the
[DataFilterExtension][lonboard.experimental.DataFilterExtension] when you want to
filter on 2 to 4 columns on the same time.

If you have only a single filter, use an ipywidgets
[FloatRangeSlider][ipywidgets.widgets.widget_float.FloatRangeSlider] directly.

# Example

```py
from ipywidgets import FloatRangeSlider

slider1 = FloatRangeSlider(
value=(2, 5),
min=0,
max=10,
step=0.1,
description="First slider: "
)
slider2 = FloatRangeSlider(
value=(30, 40),
min=0,
max=50,
step=1,
description="Second slider: "
)
multi_slider = MultiRangeSlider([slider1, slider2])
multi_slider
```

Then to propagate updates to a rendered layer, call
[jsdlink][ipywidgets.widgets.widget_link.jsdlink] to connect the two widgets.

```py
from ipywidgets import jsdlink

jsdlink(
(multi_slider, "value"),
(layer, "filter_range")
)
```

As you change the slider, the `filter_range` value on the layer class should be
updated.
"""

# We use a tuple to force reassignment to update the list
# This is because list mutations do not get propagated as events
# https://github.com/jupyter-widgets/ipywidgets/blob/b2531796d414b0970f18050d6819d932417b9953/python/ipywidgets/ipywidgets/widgets/widget_box.py#L52-L54
value = TypedTuple(trait=TypedTuple(trait=traitlets.Float())).tag(sync=True)

def __init__(self, children: Sequence[FloatRangeSlider], **kwargs):
# We manage a list of lists to match what deck.gl expects for the
# DataFilterExtension
def callback(change, *, i: int):
value = list(self.value)
value[i] = change["new"]
self.set_trait("value", value)
self.send_state("value")

initial_values = []
for i, child in enumerate(children):
func = partial(callback, i=i)
child.observe(func, "value")
initial_values.append(child.value)

super().__init__(children, value=initial_values, **kwargs)
62 changes: 62 additions & 0 deletions lonboard/experimental/layer_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,68 @@ class DataFilterExtension(BaseExtension):
)
```

The `DataFilterExtension` allows filtering on 1 to 4 attributes at the same time. So
if you have four numeric columns of interest, you can filter on the intersection of
all of them.

For easy visualization, we suggest connecting the `DataFilterExtension` to an
interactive slider from `ipywidgets`.

```py
from ipywidgets import FloatRangeSlider

slider = FloatRangeSlider(
value=(2, 5),
min=0,
max=10,
step=0.1,
description="Slider: "
)
slider

jsdlink(
(slider, "value"),
(layer, "filter_range")
)
```

If you have 2 to 4 columns, use a
[`MultiRangeSlider`][lonboard.controls.MultiRangeSlider], which combines multiple
`FloatRangeSlider` objects in a form that the `DataFilterExtension` expects.

```py
from ipywidgets import FloatRangeSlider, jsdlink

slider1 = FloatRangeSlider(
value=(2, 5),
min=0,
max=10,
step=0.1,
description="First slider: "
)
slider2 = FloatRangeSlider(
value=(30, 40),
min=0,
max=50,
step=1,
description="Second slider: "
)
multi_slider = MultiRangeSlider([slider1, slider2])
multi_slider

jsdlink(
(multi_slider, "value"),
(layer, "filter_range")
)
```

# Important notes

- The DataFilterExtension only supports float32 data, so integer data will be casted
to float32.
- The DataFilterExtension copies all data referenced by `get_filter_value` to the
GPU, so it will increase memory pressure on the GPU.

# Layer Properties

## `filter_enabled`
Expand Down
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ nav:
- api/layers/base-layer.md
- api/basemap.md
- api/colormap.md
- api/controls.md
- api/traits.md
- Experimental:
- Layer Extensions:
Expand Down Expand Up @@ -144,6 +145,7 @@ plugins:
- https://geoarrow.github.io/geoarrow-rs/python/latest/objects.inv
- https://geopandas.org/en/stable/objects.inv
- https://geopy.readthedocs.io/en/stable/objects.inv
- https://ipywidgets.readthedocs.io/en/stable/objects.inv
- https://matplotlib.org/stable/objects.inv
- https://numpy.org/doc/stable/objects.inv
- https://pandas.pydata.org/pandas-docs/stable/objects.inv
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "lonboard"
version = "0.5.0"
version = "0.6.0-beta.1"
description = "Python library for fast, interactive geospatial vector data visualization in Jupyter."
authors = ["Kyle Barron <kyle@developmentseed.org>"]
license = "MIT"
Expand Down