Skip to content

Commit

Permalink
Basic categorical data filter (#609)
Browse files Browse the repository at this point in the history
  • Loading branch information
kylebarron authored Aug 22, 2024
1 parent 07187a0 commit f897fa5
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 7 deletions.
67 changes: 61 additions & 6 deletions lonboard/layer_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,29 @@ class DataFilterExtension(BaseExtension):
This extension dynamically enables the following properties onto the layer(s) where
it is included:
## `filter_categories`
The list of categories that should be rendered. If an object's filtered category is
in the list, the object will be rendered; otherwise it will be hidden. This prop can
be updated on user input or animation with very little cost.
Format:
- If category_size is 1: ['category1', 'category2']
- If category_size is 2 to 4:
[['category1', 'category2', ...], ['category3', ...], ...] for each filtered
property, respectively.
The maximum number of supported is determined by the category_size:
- If category_size is 1: 128 categories
- If category_size is 2: 64 categories per dimension
- If category_size is 3 or 4: 32 categories per dimension.
If this value is exceeded any categories beyond the limit will be ignored.
Default: `[0]`
## `filter_enabled`
Enable/disable the data filter. If the data filter is disabled, all objects are
Expand Down Expand Up @@ -275,16 +298,36 @@ class DataFilterExtension(BaseExtension):
Accessor to retrieve the value for each object that it will be filtered by.
- Type:
[FilterValueAccessor][lonboard.traits.FilterValueAccessor]
- Type: [FilterValueAccessor][lonboard.traits.FilterValueAccessor]
- If a scalar value is provided, it is used as the value for all objects.
- If an array is provided, each value in the array will be used as the value
for the object at the same row index.
- If an array is provided, each value in the array will be used as the value for
the object at the same row index.
## `get_filter_category`
Accessor to retrieve the category for each object that it will be filtered by.
- Type: [FilterValueAccessor][lonboard.traits.FilterValueAccessor]
- If a scalar value is provided, it is used as the value for all objects.
- If an array is provided, each value in the array will be used as the value for
the object at the same row index.
"""

_extension_type = traitlets.Unicode("data-filter").tag(sync=True)

_layer_traits = {
"filter_categories": traitlets.Union(
[
traitlets.List(traitlets.Any()),
traitlets.List(
traitlets.List(traitlets.Any()),
minlen=2,
maxlen=4,
),
],
default_value=None,
allow_none=True,
).tag(sync=True),
"filter_enabled": traitlets.Bool(True).tag(sync=True),
"filter_range": traitlets.Union(
[
Expand All @@ -294,14 +337,17 @@ class DataFilterExtension(BaseExtension):
minlen=2,
maxlen=4,
),
]
],
default_value=None,
allow_none=True,
).tag(sync=True),
"filter_soft_range": traitlets.Tuple(
traitlets.Float(), traitlets.Float(), default_value=None, allow_none=True
).tag(sync=True),
"filter_transform_size": traitlets.Bool(True).tag(sync=True),
"filter_transform_color": traitlets.Bool(True).tag(sync=True),
"get_filter_value": FilterValueAccessor(None, allow_none=False),
"get_filter_value": FilterValueAccessor(default_value=None, allow_none=True),
"get_filter_category": FilterValueAccessor(default_value=None, allow_none=True),
}

filter_size = traitlets.Int(1, min=1, max=4).tag(sync=True)
Expand All @@ -313,6 +359,15 @@ class DataFilterExtension(BaseExtension):
- Default 1.
"""

category_size = traitlets.Int(1, min=1, max=4).tag(sync=True)
"""The size of the category filter (number of columns to filter by).
The category filter can show/hide data based on 1-4 properties of each object.
- Type: `int`, optional
- Default 0.
"""


class PathStyleExtension(BaseExtension):
"""
Expand Down
13 changes: 12 additions & 1 deletion src/model/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,14 @@ export class DataFilterExtension extends BaseExtensionModel {
// TODO: set filterSize, fp64, countItems in constructor
// TODO: should filter_size automatically update from python?
const filterSize = this.model.get("filter_size");
this.extensionInstance = new _DataFilterExtension({ filterSize });
const categorySize = this.model.get("category_size");
this.extensionInstance = new _DataFilterExtension({
...(isDefined(filterSize) ? { filterSize } : {}),
...(isDefined(categorySize) ? { categorySize } : {}),
});

// Properties added by the extension onto the layer
layerModel.initRegularAttribute("filter_categories", "filterCategories");
layerModel.initRegularAttribute("filter_enabled", "filterEnabled");
layerModel.initRegularAttribute("filter_range", "filterRange");
layerModel.initRegularAttribute("filter_soft_range", "filterSoftRange");
Expand All @@ -121,17 +126,23 @@ export class DataFilterExtension extends BaseExtensionModel {
"filterTransformColor",
);

layerModel.initVectorizedAccessor(
"get_filter_category",
"getFilterCategory",
);
layerModel.initVectorizedAccessor("get_filter_value", "getFilterValue");

// Update the layer model with the list of the JS property names added by
// this extension
layerModel.extensionLayerPropertyNames = [
...layerModel.extensionLayerPropertyNames,
"filterCategories",
"filterEnabled",
"filterRange",
"filterSoftRange",
"filterTransformSize",
"filterTransformColor",
"getFilterCategory",
"getFilterValue",
];
}
Expand Down

0 comments on commit f897fa5

Please sign in to comment.