4545 ColorAccessor ,
4646 FloatAccessor ,
4747 NormalAccessor ,
48+ VariableLengthTuple ,
4849)
4950
5051if TYPE_CHECKING :
@@ -111,24 +112,14 @@ def __init__(self, *, extensions: Sequence[BaseExtension] = (), **kwargs):
111112
112113 # TODO: validate that only one extension per type is included. E.g. you can't have
113114 # two data filter extensions.
114- extensions = traitlets . List ( trait = traitlets .Instance (BaseExtension )).tag (
115+ extensions = VariableLengthTuple ( traitlets .Instance (BaseExtension )).tag (
115116 sync = True , ** ipywidgets .widget_serialization
116117 )
117118 """
118119 A list of [layer extension](https://developmentseed.org/lonboard/latest/api/layer-extensions/)
119120 objects to add additional features to a layer.
120121 """
121122
122- # TODO: the extensions list is not observed; separately, the list object itself does
123- # not propagate events, so an append wouldn't work.
124-
125- # @traitlets.observe("extensions")
126- # def _observe_extensions(self, change):
127- # """When a new extension is assigned, add its layer props to this layer."""
128- # new_extensions: List[BaseExtension] = change["new"]
129- # for extension in new_extensions:
130- # self.add_traits(**extension._layer_traits)
131-
132123 def _add_extension_traits (self , extensions : Sequence [BaseExtension ]):
133124 """Assign selected traits from the extension onto this Layer."""
134125 for extension in extensions :
@@ -154,14 +145,52 @@ def _add_extension_traits(self, extensions: Sequence[BaseExtension]):
154145 if trait .get_metadata ("sync" ):
155146 self .keys .append (name )
156147
157- def add_extension (self , extension : BaseExtension ):
158- if any (isinstance (ext , extension .__class__ ) for ext in self .extensions ):
159- raise ValueError ("Cannot handle multiple of the same extension" )
148+ def add_extension (self , extension : BaseExtension , ** props ):
149+ """Add a new layer extension to an existing layer instance.
150+
151+ Any properties for the added extension should also be passed as keyword
152+ arguments to this function.
153+
154+ Examples:
155+
156+ ```py
157+ from lonboard import ScatterplotLayer
158+ from lonboard.layer_extension import DataFilterExtension
159+
160+ gdf = geopandas.GeoDataFrame(...)
161+ layer = ScatterplotLayer.from_geopandas(gdf)
162+
163+ extension = DataFilterExtension(filter_size=1)
164+ filter_values = gdf["filter_column"]
165+
166+ layer.add_extension(
167+ extension,
168+ get_filter_value=filter_values,
169+ filter_range=[0, 1]
170+ )
171+ ```
172+
173+ Args:
174+ extension: The new extension to add.
175+
176+ Raises:
177+ ValueError: if another extension of the same type already exists on the
178+ layer.
179+ """
180+ if any (isinstance (extension , type (ext )) for ext in self .extensions ):
181+ raise ValueError ("Only one extension of each type permitted" )
182+
183+ with self .hold_trait_notifications ():
184+ self ._add_extension_traits ([extension ])
185+ self .extensions += (extension ,)
160186
161- # Maybe keep a registry of which extensions have already been added?
162- self ._add_extension_traits ([extension ])
187+ # Assign any extension properties
188+ added_names : List [str ] = []
189+ for prop_name , prop_value in props .items ():
190+ self .set_trait (prop_name , prop_value )
191+ added_names .append (prop_name )
163192
164- self .extensions . append ( extension )
193+ self .send_state ( added_names + [ "extensions" ] )
165194
166195 pickable = traitlets .Bool (True ).tag (sync = True )
167196 """
@@ -492,9 +521,9 @@ def __init__(self, **kwargs: BitmapLayerKwargs):
492521
493522 bounds = traitlets .Union (
494523 [
495- traitlets . List (traitlets .Float (), minlen = 4 , maxlen = 4 ),
496- traitlets . List (
497- traitlets . List (traitlets .Float (), minlen = 2 , maxlen = 2 ),
524+ VariableLengthTuple (traitlets .Float (), minlen = 4 , maxlen = 4 ),
525+ VariableLengthTuple (
526+ VariableLengthTuple (traitlets .Float (), minlen = 2 , maxlen = 2 ),
498527 minlen = 4 ,
499528 maxlen = 4 ,
500529 ),
@@ -516,7 +545,7 @@ def __init__(self, **kwargs: BitmapLayerKwargs):
516545 - Default: `0`
517546 """
518547
519- transparent_color = traitlets . List (
548+ transparent_color = VariableLengthTuple (
520549 traitlets .Float (), default_value = None , allow_none = True , minlen = 3 , maxlen = 4
521550 )
522551 """The color to use for transparent pixels, in `[r, g, b, a]`.
@@ -525,7 +554,7 @@ def __init__(self, **kwargs: BitmapLayerKwargs):
525554 - Default: `[0, 0, 0, 0]`
526555 """
527556
528- tint_color = traitlets . List (
557+ tint_color = VariableLengthTuple (
529558 traitlets .Float (), default_value = None , allow_none = True , minlen = 3 , maxlen = 4
530559 )
531560 """The color to tint the bitmap by, in `[r, g, b]`.
@@ -588,7 +617,7 @@ def __init__(self, **kwargs: BitmapTileLayerKwargs):
588617 _layer_type = traitlets .Unicode ("bitmap-tile" ).tag (sync = True )
589618
590619 data = traitlets .Union (
591- [traitlets .Unicode (), traitlets . List (traitlets .Unicode (), minlen = 1 )]
620+ [traitlets .Unicode (), VariableLengthTuple (traitlets .Unicode (), minlen = 1 )]
592621 ).tag (sync = True )
593622 """
594623 Either a URL template or an array of URL templates from which the tile data should
@@ -643,7 +672,7 @@ def __init__(self, **kwargs: BitmapTileLayerKwargs):
643672 - Default: `None`
644673 """
645674
646- extent = traitlets . List (
675+ extent = VariableLengthTuple (
647676 traitlets .Float (), minlen = 4 , maxlen = 4 , allow_none = True , default_value = None
648677 ).tag (sync = True )
649678 """
@@ -726,7 +755,7 @@ def __init__(self, **kwargs: BitmapTileLayerKwargs):
726755 - Default: `0`
727756 """
728757
729- transparent_color = traitlets . List (
758+ transparent_color = VariableLengthTuple (
730759 traitlets .Float (), default_value = None , allow_none = True , minlen = 3 , maxlen = 4
731760 )
732761 """The color to use for transparent pixels, in `[r, g, b, a]`.
@@ -735,7 +764,7 @@ def __init__(self, **kwargs: BitmapTileLayerKwargs):
735764 - Default: `[0, 0, 0, 0]`
736765 """
737766
738- tint_color = traitlets . List (
767+ tint_color = VariableLengthTuple (
739768 traitlets .Float (), default_value = None , allow_none = True , minlen = 3 , maxlen = 4
740769 )
741770 """The color to tint the bitmap by, in `[r, g, b]`.
@@ -1999,7 +2028,7 @@ class HeatmapLayer(BaseArrowLayer):
19992028 def __init__ (
20002029 self , * , table : ArrowStreamExportable , ** kwargs : Unpack [HeatmapLayerKwargs ]
20012030 ):
2002- err_msg = """\
2031+ err_msg = """
20032032 The `HeatmapLayer` is not currently working.
20042033
20052034 As of Lonboard v0.10, Lonboard upgraded to version 9.0 of the underlying
@@ -2085,7 +2114,7 @@ def from_duckdb(
20852114 - Default: `0.05`
20862115 """
20872116
2088- color_domain = traitlets . List (
2117+ color_domain = VariableLengthTuple (
20892118 traitlets .Float (), default_value = None , allow_none = True , minlen = 2 , maxlen = 2
20902119 ).tag (sync = True )
20912120 # """
0 commit comments