Source code for hscmap.regions

from dataclasses import dataclass
from typing import List, Optional, Tuple, cast

from .angle import Angle
from .models.actions.regions.newCircularRegionAdded import Model as NewCircularRegionAdded
from .models.actions.regions.newLinearRegionAdded import Model as NewLinearRegionAdded
from .models.actions.regions.newPathRegionAdded import Model as NewPathRegionAdded
from .models.actions.regions.newRectangularRegionAdded import Model as NewRectangularRegionAdded
from .models.actions.regions.newTextRegionAdded import Model as NewTextRegionAdded
from .models.actions.regions.regionDeleted import Model as regionDeleted
from .models.actions.regions.regionsReordered import Model as RegionsReordered
from .models.actions.regions.regionUpdated import Model as RegionUpdated
from .models.store import CircularRegion as CircularRegionState
from .models.store import LinearRegion as LinearRegionState
from .models.store import Path
from .models.store import PathRegion as PathRegionState
from .models.store import RectangularRegion as RectangularRegionState
from .models.store import TextRegion as TextRegionState
from .shape import ShapeBase
from .tinyid import tinyid
from .vec3 import SkyCoord
from .window import Window


[docs] @dataclass class RegionManager: _w: Window @property def members(self) -> List['RegionBase']: self._w.sync() return [type_map[cast(str, r['type'])](r['id'], self._w) for r in self._w._store_state['regions']['regions']]
[docs] def new_text(self, *, position: Tuple[float, float], text: str, color: Optional[List[float]] = None): w = self._w id = tinyid() positionCoord = SkyCoord(w._angle_input(position[0]), w._angle_input(position[1])) action = NewTextRegionAdded( type='regions/newTextRegionAdded', payload={ 'color': color, 'id': id, 'name': text, 'position': positionCoord.as_dict(), 'showLabel': True, 'type': 'Text', 'visible': True, }, ) w._dispatch(action) return TextRegion(id, w)
[docs] def new_circle(self, *, center: Tuple[float, float], radius: float, name='', color: Optional[List[float]] = None): w = self._w id = tinyid() centerCoord = SkyCoord(w._angle_input(center[0]), w._angle_input(center[1])) action = NewCircularRegionAdded( type='regions/newCircularRegionAdded', payload={ 'color': color, 'name': name, 'id': id, 'center': centerCoord.as_dict(), 'radius': w._angle_input(radius).radian, 'showLabel': True, 'type': 'Circular', 'visible': True, }, ) w._dispatch(action) return CircleRegion(id, w)
[docs] def new_line(self, *, start: Tuple[float, float], end: Tuple[float, float], name='', color: Optional[List[float]] = None): w = self._w id = tinyid() startCoord = SkyCoord(w._angle_input(start[0]), w._angle_input(start[1])) endCoord = SkyCoord(w._angle_input(end[0]), w._angle_input(end[1])) action = NewLinearRegionAdded( type='regions/newLinearRegionAdded', payload={ 'color': color, 'id': id, 'name': name, 'start': startCoord.as_dict(), 'end': endCoord.as_dict(), 'showLabel': True, 'type': 'Linear', 'visible': True, }, ) w._dispatch(action) return LinearRegion(id, w)
[docs] def new_rect(self, *, min_ra: float, max_ra: float, min_dec: float, max_dec: float, name='', color: Optional[List[float]] = None): w = self._w id = tinyid() action = NewRectangularRegionAdded( type='regions/newRectangularRegionAdded', payload={ 'color': color, 'id': id, 'name': name, 'minRa': w._angle_input(min_ra).radian, 'maxRa': w._angle_input(max_ra).radian, 'minDec': w._angle_input(min_dec).radian, 'maxDec': w._angle_input(max_dec).radian, 'showLabel': True, 'type': 'Rectangular', 'visible': True, }, ) w._dispatch(action) return RectangularRegion(id, w)
[docs] def from_shape(self, *, shape: ShapeBase, name=''): w = self._w id = tinyid() action = NewPathRegionAdded( type='regions/newPathRegionAdded', payload={ 'color': None, 'id': id, 'name': name, 'paths': [path for path in shape.paths()], 'showLabel': True, 'type': 'Path', 'visible': True, }, ) w._dispatch(action) return ShapeRegion(id, w)
[docs] def clear(self): for r in self.members: r.delete()
[docs] @dataclass class RegionBase: id: str _w: Window def _sync(self): self._w.sync() def _state(self): self._sync() for r in self._w._store_state['regions']['regions']: # pragma: no branch if r['id'] == self.id: # pragma: no branch return r raise ValueError(f"Region already deleted: {self.id}") # pragma: no cover def _update(self, **updates): self._w._dispatch( RegionUpdated( type='regions/regionUpdated', payload={ 'id': self.id, 'regionDef': {**self._state(), **updates}, # type: ignore }, ) )
[docs] def surface(self): w = self._w ids = [r.id for r in w.regions.members] ids.remove(self.id) ids.append(self.id) w._dispatch( RegionsReordered( type='regions/regionsReordered', payload={'ids': ids}, ) ) # TODO: Review react-stellar-globe # ↓Forced redraw. Without this, the display does not change even if the order of the regions changes. w.jump_to(*w.camera.center, duration=0.01)
[docs] def delete(self): self._w._dispatch( regionDeleted( type='regions/regionDeleted', payload={'id': self.id}, ) )
@property def visible(self) -> bool: return self._state()['visible'] @visible.setter def visible(self, value: bool): self._update(visible=value) @property def show_label(self) -> bool: self._sync() return self._state()['showLabel'] @show_label.setter def show_label(self, value: bool): self._update(showLabel=value) @property def name(self) -> str: self._sync() return self._state()['name'] @name.setter def name(self, value: str): self._update(name=value) @property def color(self) -> List[float]: self._sync() return self._state()['color'] @color.setter def color(self, value: List[float]): self._update(color=value)
[docs] class TextRegion(RegionBase): def _state(self) -> TextRegionState: return cast(TextRegionState, super()._state()) @property def text(self) -> str: self._sync() return self._state()['name'] @text.setter def text(self, value: str): self._update(name=value) @property def position(self) -> Tuple[float, float]: w = self._w self._sync() return ( w._angle_output(Angle.from_radian(self._state()['position']['ra'])), w._angle_output(Angle.from_radian(self._state()['position']['dec'])), ) @position.setter def position(self, value: Tuple[float, float]): w = self._w position = { 'ra': w._angle_input(value[0]).radian, 'dec': w._angle_input(value[1]).radian, } self._update(position=position)
[docs] class CircleRegion(RegionBase): def _state(self) -> CircularRegionState: return cast(CircularRegionState, super()._state()) @property def center(self) -> Tuple[float, float]: w = self._w self._sync() return ( w._angle_output(Angle.from_radian(self._state()['center']['ra'])), w._angle_output(Angle.from_radian(self._state()['center']['dec'])), ) @center.setter def center(self, value: Tuple[float, float]): w = self._w center = { 'ra': w._angle_input(value[0]).radian, 'dec': w._angle_input(value[1]).radian, } self._update(center=center) @property def radius(self) -> float: self._sync() return self._w._angle_output(Angle.from_radian(self._state()['radius'])) @radius.setter def radius(self, value: float): radius = self._w._angle_input(value).radian self._update(radius=radius)
[docs] class LinearRegion(RegionBase): def _state(self) -> LinearRegionState: return cast(LinearRegionState, super()._state()) @property def start(self) -> Tuple[float, float]: w = self._w self._sync() return ( w._angle_output(Angle.from_radian(self._state()['start']['ra'])), w._angle_output(Angle.from_radian(self._state()['start']['dec'])), ) @start.setter def start(self, value: Tuple[float, float]): w = self._w start = { 'ra': w._angle_input(value[0]).radian, 'dec': w._angle_input(value[1]).radian, } self._update(start=start) @property def end(self) -> Tuple[float, float]: w = self._w self._sync() return ( w._angle_output(Angle.from_radian(self._state()['end']['ra'])), w._angle_output(Angle.from_radian(self._state()['end']['dec'])), ) @end.setter def end(self, value: Tuple[float, float]): w = self._w end = { 'ra': w._angle_input(value[0]).radian, 'dec': w._angle_input(value[1]).radian, } self._update(end=end)
[docs] class RectangularRegion(RegionBase): def _state(self) -> RectangularRegionState: return cast(RectangularRegionState, super()._state()) @property def min_ra(self) -> float: return self._w._angle_output(Angle.from_radian(self._state()['minRa'])) @min_ra.setter def min_ra(self, value: float): self._update(minRa=self._w._angle_input(value).radian) @property def max_ra(self) -> float: return self._w._angle_output(Angle.from_radian(self._state()['maxRa'])) @max_ra.setter def max_ra(self, value: float): self._update(maxRa=self._w._angle_input(value).radian) @property def min_dec(self) -> float: return self._w._angle_output(Angle.from_radian(self._state()['minDec'])) @min_dec.setter def min_dec(self, value: float): self._update(minDec=self._w._angle_input(value).radian) @property def max_dec(self) -> float: return self._w._angle_output(Angle.from_radian(self._state()['maxDec'])) @max_dec.setter def max_dec(self, value: float): self._update(maxDec=self._w._angle_input(value).radian)
[docs] class ShapeRegion(RegionBase): def _state(self) -> PathRegionState: return cast(PathRegionState, super()._state()) @property def paths(self) -> List[Path]: self._sync() return self._state()['paths'] @paths.setter def paths(self, value: List[Path]): self._update(paths=value)
type_map = { 'Text': TextRegion, 'Circular': CircleRegion, 'Linear': LinearRegion, 'Rectangular': RectangularRegion, 'Path': ShapeRegion, }