diff --git a/CHANGELOG.md b/CHANGELOG.md index 25ecd1e8..5e72a257 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,8 +19,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Support for `astropy.coordinates.SkyCoord` for assigning and reading the `target` property (#80) -- Support for `astropy.coordinates.Angle` for reading the `fov` property (#83) +- Support for `astropy.coordinates.Angle` for assigning and reading the `fov` property (#83) - Support for `regions.LineSkyRegion`, `regions.CircleSkyRegion`, `regions.EllipseSkyRegion`, `regions.PolygonSkyRegion`, `regions.RectangleSkyRegion`, `regions.Regions` with `add_graphic_overlay_from_region` (#88) +- Support for `wcs` property as an `astropy.WCS` that can be synchronized using the `synchronize_wcs` method (#89) ### Fixed diff --git a/README.md b/README.md index 1c4070db..2ce2f8c5 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,30 @@ aladin = Aladin() aladin ``` +## Widget limitations + +Because some limitations of the widget framework, some operations need specific behaviors. + +### Aladin Lite values + +Some values need to be synchronized in their own cell before being used in a next one, like: + +- wcs + +#### Example with the wcs value: + +First cell: + +```python +aladin.synchronize_wcs() +``` + +Second cell: + +```python +print(aladin.wcs) +``` + ## Development installation First, make sure you have installed jupyter in your python environnement: `pip install jupyter`. diff --git a/js/models/event_handler.js b/js/models/event_handler.js index 1bdb163c..7f6c15f7 100644 --- a/js/models/event_handler.js +++ b/js/models/event_handler.js @@ -12,7 +12,7 @@ export default class EventHandler { this.aladin = aladin; this.aladinDiv = aladinDiv; this.model = model; - this.messageHandler = new MessageHandler(aladin); + this.messageHandler = new MessageHandler(aladin, model); } /** @@ -171,6 +171,7 @@ export default class EventHandler { this.eventHandlers = { change_fov: this.messageHandler.handleChangeFoV, goto_ra_dec: this.messageHandler.handleGotoRaDec, + synchronize_wcs: this.messageHandler.handleSynchronizeWCS, add_catalog_from_URL: this.messageHandler.handleAddCatalogFromURL, add_MOC_from_URL: this.messageHandler.handleAddMOCFromURL, add_MOC_from_dict: this.messageHandler.handleAddMOCFromDict, diff --git a/js/models/message_handler.js b/js/models/message_handler.js index e4b54b55..e59efbd6 100644 --- a/js/models/message_handler.js +++ b/js/models/message_handler.js @@ -2,8 +2,9 @@ import { convertOptionNamesToCamelCase } from "../utils"; import A from "../aladin_lite"; export default class MessageHandler { - constructor(aladin) { + constructor(aladin, model) { this.aladin = aladin; + this.model = model; } handleChangeFoV(msg) { @@ -14,6 +15,12 @@ export default class MessageHandler { this.aladin.gotoRaDec(msg["ra"], msg["dec"]); } + handleSynchronizeWCS() { + const wcs = this.aladin.getViewWCS(); + this.model.set("_wcs", wcs); + this.model.save_changes(); + } + handleAddCatalogFromURL(msg) { const options = convertOptionNamesToCamelCase(msg["options"] || {}); this.aladin.addCatalog(A.catalogFromURL(msg["votable_URL"], options)); diff --git a/src/ipyaladin/aladin.py b/src/ipyaladin/aladin.py index 17beb478..882ea525 100644 --- a/src/ipyaladin/aladin.py +++ b/src/ipyaladin/aladin.py @@ -11,10 +11,10 @@ import warnings import anywidget +from astropy.coordinates import SkyCoord, Angle from astropy.table.table import QTable from astropy.table import Table -from astropy.coordinates import SkyCoord, Angle -import traitlets +from astropy.wcs import WCS try: from regions import ( @@ -34,6 +34,7 @@ RectangleSkyRegion = None Region = None Regions = None +import traitlets from traitlets import ( Float, Int, @@ -122,6 +123,9 @@ class Aladin(anywidget.AnyWidget): grid_opacity = Float(0.5).tag(sync=True, init_option=True) grid_options = traitlets.Dict().tag(sync=True, init_option=True) + # Synchronized traitlets + _wcs = traitlets.Dict().tag(sync=True) + # content of the last click clicked_object = traitlets.Dict().tag(sync=True) # listener callback is on the python side and contains functions to link to events @@ -220,6 +224,33 @@ def target(self, target: Union[str, SkyCoord]) -> None: } ) + @property + def wcs(self) -> WCS: + """The WCS of the Aladin Lite widget. + + You need to call synchronize_wcs in a previous + cell before accessing this attribute. + + Returns + ------- + WCS + An `~astropy.wcs.WCS` object representing the WCS of the widget. + + """ + if self._wcs == {}: + raise ValueError( + "You need to call synchronize_wcs before accessing the wcs attribute. " + "The WCS attribute need to be accessed from the next cell." + ) + if "RADECSYS" in self._wcs: # RADECSYS keyword is deprecated for astropy.WCS + self._wcs["RADESYS"] = self._wcs.pop("RADECSYS") + return WCS(self._wcs) + + def synchronize_wcs(self) -> None: + """Synchronize the WCS of the Aladin Lite widget with the given WCS.""" + self._wcs = {} + self.send({"event_name": "synchronize_wcs"}) + def add_catalog_from_URL( self, votable_URL: str, votable_options: Optional[dict] = None ) -> None: