Skip to content

Commit 05c8d79

Browse files
committed
Improved validation
1 parent 295dd5e commit 05c8d79

File tree

4 files changed

+54
-59
lines changed

4 files changed

+54
-59
lines changed

lonboard/_map.py

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import warnings
4+
from dataclasses import replace
45
from pathlib import Path
56
from typing import IO, TYPE_CHECKING, Any, TextIO, overload
67

@@ -20,8 +21,8 @@
2021
ScaleControl,
2122
)
2223
from lonboard.layer import BaseLayer
24+
from lonboard.models import BaseViewState, MapViewState
2325
from lonboard.traits import HeightTrait, VariableLengthTuple, ViewStateTrait
24-
from lonboard.traits._map import DEFAULT_INITIAL_VIEW_STATE
2526
from lonboard.view import BaseView
2627

2728
if TYPE_CHECKING:
@@ -154,9 +155,7 @@ def on_click(self, callback: Callable, *, remove: bool = False) -> None:
154155
_esm = bundler_output_dir / "index.js"
155156
_css = bundler_output_dir / "index.css"
156157

157-
# TODO: change this view state to allow non-map view states if we have non-map views
158-
# Also allow a list/tuple of view states for multiple views
159-
view_state = ViewStateTrait()
158+
view_state: BaseViewState | None = ViewStateTrait() # type: ignore
160159
"""
161160
The view state of the map.
162161
@@ -492,8 +491,9 @@ def add_layer(
492491
elif reset_zoom:
493492
self.view_state = compute_view(self.layers) # type: ignore
494493

495-
def set_view_state(
494+
def set_view_state( # noqa: PLR0913
496495
self,
496+
view_state: BaseViewState | None = None,
497497
*,
498498
longitude: float | None = None,
499499
latitude: float | None = None,
@@ -505,6 +505,9 @@ def set_view_state(
505505
506506
Any parameters that are unset will not be changed.
507507
508+
Args:
509+
view_state: A complete view state object to set on the map.
510+
508511
Keyword Args:
509512
longitude: the new longitude to set on the map. Defaults to None.
510513
latitude: the new latitude to set on the map. Defaults to None.
@@ -513,24 +516,29 @@ def set_view_state(
513516
bearing: the new bearing to set on the map. Defaults to None.
514517
515518
"""
516-
view_state = (
517-
self.view_state._asdict() # type: ignore
518-
if self.view_state is not None
519-
else DEFAULT_INITIAL_VIEW_STATE
520-
)
521-
522-
if longitude is not None:
523-
view_state["longitude"] = longitude
524-
if latitude is not None:
525-
view_state["latitude"] = latitude
526-
if zoom is not None:
527-
view_state["zoom"] = zoom
528-
if pitch is not None:
529-
view_state["pitch"] = pitch
530-
if bearing is not None:
531-
view_state["bearing"] = bearing
532-
533-
self.view_state = view_state
519+
if view_state is not None:
520+
self.view_state = view_state
521+
return
522+
523+
current_view_state = self.view_state
524+
if isinstance(current_view_state, MapViewState):
525+
changes = {}
526+
if longitude is not None:
527+
changes["longitude"] = longitude
528+
if latitude is not None:
529+
changes["latitude"] = latitude
530+
if zoom is not None:
531+
changes["zoom"] = zoom
532+
if pitch is not None:
533+
changes["pitch"] = pitch
534+
if bearing is not None:
535+
changes["bearing"] = bearing
536+
537+
self.view_state = replace(current_view_state, **changes)
538+
else:
539+
raise TypeError(
540+
"Can only set MapViewState or GlobeViewState parameters individually via set_view_state.\nFor other view state types, pass a complete view_state object.",
541+
)
534542

535543
def fly_to( # noqa: PLR0913
536544
self,

lonboard/_viewport.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,10 @@ def compute_view(layers: Sequence[BaseLayer]) -> dict[str, Any]:
7474
"longitude": center.x or 0,
7575
"latitude": center.y or 0,
7676
"zoom": 0,
77-
"pitch": 0,
78-
"bearing": 0,
7977
}
8078
else:
8179
return {
8280
"longitude": center.x,
8381
"latitude": center.y,
8482
"zoom": zoom,
85-
"pitch": 0,
86-
"bearing": 0,
8783
}

lonboard/traits/_map.py

Lines changed: 3 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,8 @@
66
import traitlets
77

88
from lonboard._environment import DEFAULT_HEIGHT
9-
from lonboard.models import (
10-
BaseViewState,
11-
FirstPersonViewState,
12-
GlobeViewState,
13-
MapViewState,
14-
OrbitViewState,
15-
OrthographicViewState,
16-
_serialize_view_state,
17-
)
9+
from lonboard.models import BaseViewState, _serialize_view_state
1810
from lonboard.traits._base import FixedErrorTraitType
19-
from lonboard.view import (
20-
BaseView,
21-
FirstPersonView,
22-
GlobeView,
23-
MapView,
24-
OrbitView,
25-
OrthographicView,
26-
)
2711

2812
if TYPE_CHECKING:
2913
from traitlets import HasTraits
@@ -82,15 +66,6 @@ def validate(self, obj: Any, value: Any) -> str:
8266
assert False
8367

8468

85-
VIEW_STATE_VALIDATORS: dict[type[BaseView], type[BaseViewState]] = {
86-
MapView: MapViewState,
87-
GlobeView: GlobeViewState,
88-
FirstPersonView: FirstPersonViewState,
89-
OrbitView: OrbitViewState,
90-
OrthographicView: OrthographicViewState,
91-
}
92-
93-
9469
class ViewStateTrait(FixedErrorTraitType):
9570
"""Trait to validate view state input."""
9671

@@ -109,13 +84,9 @@ def __init__(
10984
def validate(self, obj: Map, value: Any) -> None | BaseViewState:
11085
view = obj.views
11186
if view is None:
112-
return MapViewState() if value is None else value
87+
return None
11388
else: # noqa: RET505 (typing issue)
114-
validator = VIEW_STATE_VALIDATORS.get(type(view))
115-
if validator is None:
116-
self.error(obj, value, info="unsupported view type")
117-
assert False
118-
89+
validator = view._view_state_type # noqa: SLF001
11990
return validator(value) # type: ignore
12091
# view
12192

lonboard/view.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import traitlets as t
22

33
from lonboard._base import BaseWidget
4+
from lonboard.models import (
5+
BaseViewState,
6+
FirstPersonViewState,
7+
GlobeViewState,
8+
MapViewState,
9+
OrbitViewState,
10+
OrthographicViewState,
11+
)
412

513

614
class BaseView(BaseWidget):
@@ -10,6 +18,8 @@ class BaseView(BaseWidget):
1018
1119
"""
1220

21+
_view_state_type: type[BaseViewState] = BaseViewState
22+
1323
x = t.Union([t.Int(), t.Unicode()], allow_none=True, default_value=None).tag(
1424
sync=True,
1525
)
@@ -51,6 +61,8 @@ class FirstPersonView(BaseView):
5161

5262
_view_type = t.Unicode("first-person-view").tag(sync=True)
5363

64+
_view_state_type = FirstPersonViewState
65+
5466
projection_matrix = t.List(
5567
t.Float(),
5668
allow_none=True,
@@ -98,6 +110,8 @@ class GlobeView(BaseView):
98110

99111
_view_type = t.Unicode("globe-view").tag(sync=True)
100112

113+
_view_state_type = GlobeViewState
114+
101115
resolution = t.Float(allow_none=True, default_value=None).tag(sync=True)
102116
"""The resolution at which to turn flat features into 3D meshes, in degrees.
103117
@@ -125,6 +139,8 @@ class MapView(BaseView):
125139

126140
_view_type = t.Unicode("map-view").tag(sync=True)
127141

142+
_view_state_type = MapViewState
143+
128144
repeat = t.Bool(allow_none=True, default_value=None).tag(sync=True)
129145
"""
130146
Whether to render multiple copies of the map at low zoom levels. Default `false`.
@@ -183,6 +199,8 @@ class OrbitView(BaseView):
183199

184200
_view_type = t.Unicode("orbit-view").tag(sync=True)
185201

202+
_view_state_type = OrbitViewState
203+
186204
orbit_axis = t.Unicode(allow_none=True, default_value=None).tag(sync=True)
187205
"""Axis with 360 degrees rotating freedom, either `'Y'` or `'Z'`, default to `'Z'`."""
188206

@@ -233,6 +251,8 @@ class OrthographicView(BaseView):
233251

234252
_view_type = t.Unicode("orthographic-view").tag(sync=True)
235253

254+
_view_state_type = OrthographicViewState
255+
236256
flip_y = t.Bool(allow_none=True, default_value=None).tag(sync=True)
237257
"""
238258
Whether to use top-left coordinates (`true`) or bottom-left coordinates (`false`).

0 commit comments

Comments
 (0)