diff --git a/CHANGELOG.md b/CHANGELOG.md
index 446f044..9e24300 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,7 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add selected sources export as `astropy.Table` list with property `selected_objects` (#100)
- Add function `get_view_as_fits` to export the view as a `astropy.io.fits.HDUList` (#98)
- Add function `save_view_as_image` to save the view as an image file (#108)
-- Support for planetary objects for Aladin Lite target (#103)
+- Support planetary objects for ipyaladin targets (#103)
+- new method `add_marker` to add a marker to the view (#111)
### Deprecated
diff --git a/examples/02_Base_Commands.ipynb b/examples/02_Base_Commands.ipynb
index 6c2ae00..acbef60 100644
--- a/examples/02_Base_Commands.ipynb
+++ b/examples/02_Base_Commands.ipynb
@@ -14,7 +14,8 @@
"metadata": {},
"outputs": [],
"source": [
- "from ipyaladin import Aladin\n",
+ "from astropy.coordinates import Angle, SkyCoord\n",
+ "from ipyaladin import Aladin, Marker\n",
"from pathlib import Path"
]
},
@@ -23,24 +24,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "A list of all available commands can be displayed as such."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "print(dir(Aladin))"
- ]
- },
- {
- "attachments": {},
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "A few of them are illustrated in the next cells. Let's first, create the widget with a few initial parameters:"
+ "`ipyaladin`'s full list of methods can be found in the documentation [here](https://cds-astro.github.io/ipyaladin/autoapi/ipyaladin/widget/index.html). A few of them are illustrated in the next cells. Let's first, create the widget with a few initial parameters:"
]
},
{
@@ -64,9 +48,7 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {
- "tags": []
- },
+ "metadata": {},
"outputs": [],
"source": [
"aladin.target = \"sgr a*\""
@@ -74,12 +56,12 @@
},
{
"cell_type": "code",
+ "execution_count": null,
"metadata": {},
+ "outputs": [],
"source": [
"aladin.target"
- ],
- "outputs": [],
- "execution_count": null
+ ]
},
{
"cell_type": "markdown",
@@ -91,9 +73,7 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {
- "tags": []
- },
+ "metadata": {},
"outputs": [],
"source": [
"aladin.fov = 2"
@@ -118,9 +98,7 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {
- "tags": []
- },
+ "metadata": {},
"outputs": [],
"source": [
"aladin.overlay_survey = \"P/allWISE/color\"\n",
@@ -137,9 +115,7 @@
{
"cell_type": "code",
"execution_count": null,
- "metadata": {
- "tags": []
- },
+ "metadata": {},
"outputs": [],
"source": [
"aladin.coo_frame = \"ICRSd\" # ICRS, and angles expressed in degrees"
@@ -161,15 +137,6 @@
"The target and field of view can be set with astropy objects"
]
},
- {
- "cell_type": "code",
- "metadata": {},
- "source": [
- "from astropy.coordinates import Angle, SkyCoord"
- ],
- "outputs": [],
- "execution_count": null
- },
{
"cell_type": "code",
"execution_count": null,
@@ -204,32 +171,42 @@
"source": [
"aladin.add_fits(Path(\"images/m31.fits\"), name=\"M31\", opacity=0.5)"
]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "You can add markers to the view of the widget with custom popup title and description.\n",
+ "Here we will add markers for Messier objects M1 to M10."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "markers = []\n",
+ "for i in range(1, 11):\n",
+ " name = f\"M{i}\"\n",
+ " markers.append(\n",
+ " Marker(\n",
+ " position=name,\n",
+ " title=name,\n",
+ " description=(\n",
+ " ' '\n",
+ " \"Read more on SIMBAD\"\n",
+ " ),\n",
+ " )\n",
+ " )\n",
+ "aladin.add_markers(markers, name=\"M1-M10\", color=\"pink\", shape=\"cross\", source_size=15)\n",
+ "aladin.target = \"M1\"\n",
+ "aladin.fov = 0.2"
+ ]
}
],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.8"
- },
- "vscode": {
- "interpreter": {
- "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1"
- }
- }
- },
+ "metadata": {},
"nbformat": 4,
"nbformat_minor": 4
}
diff --git a/js/models/event_handler.js b/js/models/event_handler.js
index f36fbdb..fc90d51 100644
--- a/js/models/event_handler.js
+++ b/js/models/event_handler.js
@@ -268,6 +268,7 @@ export default class EventHandler {
});
this.eventHandlers = {
+ add_marker: this.messageHandler.handleAddMarker,
change_fov: this.messageHandler.handleChangeFoV,
goto_ra_dec: this.messageHandler.handleGotoRaDec,
save_view_as_image: this.messageHandler.handleSaveViewAsImage,
diff --git a/js/models/message_handler.js b/js/models/message_handler.js
index b3def3a..748b19d 100644
--- a/js/models/message_handler.js
+++ b/js/models/message_handler.js
@@ -9,6 +9,26 @@ export default class MessageHandler {
this.model = model;
}
+ handleAddMarker(msg) {
+ const options = convertOptionNamesToCamelCase(msg["options"] || {});
+ // default name
+ if (!options.name) options.name = "markers";
+ // create catalog
+ const catalog = A.catalog(options);
+ this.aladin.addCatalog(catalog);
+ const pythonMarkers = msg["markers"];
+ const markers = [];
+ for (const marker of pythonMarkers) {
+ markers.push(
+ A.marker(marker["lon"], marker["lat"], {
+ popupTitle: marker["title"],
+ popupDesc: marker["description"],
+ }),
+ );
+ }
+ catalog.addSources(markers);
+ }
+
handleChangeFoV(msg) {
this.aladin.setFoV(msg["fov"]);
}
diff --git a/src/ipyaladin/__init__.py b/src/ipyaladin/__init__.py
index 6161b84..eba7d08 100644
--- a/src/ipyaladin/__init__.py
+++ b/src/ipyaladin/__init__.py
@@ -1,4 +1,5 @@
"""Top-level package for ipyaladin."""
from .widget import Aladin # noqa: F401
+from .elements.marker import Marker # noqa: F401
from .__about__ import __version__, __aladin_lite_version__ # noqa: F401
diff --git a/src/test/__init__.py b/src/ipyaladin/elements/__init__.py
similarity index 100%
rename from src/test/__init__.py
rename to src/ipyaladin/elements/__init__.py
diff --git a/src/ipyaladin/elements/marker.py b/src/ipyaladin/elements/marker.py
new file mode 100644
index 0000000..789d88e
--- /dev/null
+++ b/src/ipyaladin/elements/marker.py
@@ -0,0 +1,31 @@
+from dataclasses import dataclass
+from typing import Tuple, Union
+
+from astropy.coordinates import SkyCoord, Longitude, Latitude
+from ipyaladin.utils._coordinate_parser import _parse_coordinate_string
+
+
+@dataclass
+class Marker:
+ """A class representing a marker in Aladin Lite."""
+
+ def __init__(
+ self,
+ position: Union[str, SkyCoord, Tuple[Longitude, Latitude]],
+ title: str,
+ description: str,
+ ) -> None:
+ self.title = title
+ self.description = description
+ if isinstance(position, SkyCoord):
+ self.lon = position.ra.deg
+ self.lat = position.dec.deg
+ elif isinstance(position, str):
+ self.lon, self.lat = _parse_coordinate_string(position)
+ elif (
+ isinstance(position, Tuple)
+ and isinstance(position[0], Longitude)
+ and isinstance(position[1], Latitude)
+ ):
+ self.lon = position[0].deg
+ self.lat = position[1].deg
diff --git a/src/ipyaladin/widget.py b/src/ipyaladin/widget.py
index a5e9d05..22494a4 100644
--- a/src/ipyaladin/widget.py
+++ b/src/ipyaladin/widget.py
@@ -6,6 +6,7 @@
"""
from collections.abc import Callable
+from dataclasses import asdict
import io
import pathlib
from json import JSONDecodeError
@@ -27,6 +28,7 @@
from .utils.exceptions import WidgetReducedError, WidgetNotReadyError
from .utils._coordinate_parser import _parse_coordinate_string
+from .elements.marker import Marker
try:
from regions import (
@@ -281,7 +283,7 @@ class Aladin(anywidget.AnyWidget):
"is reduced in size when hidden.",
).tag(sync=True)
- init_options = traitlets.List(trait=Any()).tag(sync=True)
+ init_options = traitlets.List(trait=traitlets.Any()).tag(sync=True)
@default("init_options")
def _init_options(self) -> List[str]:
@@ -507,6 +509,36 @@ def target(self, target: Union[str, SkyCoord, Tuple[float, float]]) -> None:
}
)
+ def add_markers(
+ self, markers: Union[Marker, List[Marker]], **catalog_options: any
+ ) -> None:
+ """Add markers to the Aladin Lite widget.
+
+ Markers have a popup window that appear when they're clicked on.
+
+ Parameters
+ ----------
+ markers : Marker or list[Marker]
+ The marker(s) to add to the widget. It can be given as a single `Marker`
+ object or as a list of `Marker` objects.
+ catalog_options : any
+ The options for the catalog. See the `Aladin Lite catalog options
+ `_
+
+ See Also
+ --------
+ add_table: also adds points, but without popup window.
+ """
+ if not isinstance(markers, list):
+ markers = [markers]
+ self.send(
+ {
+ "event_name": "add_marker",
+ "markers": [asdict(marker) for marker in markers],
+ "options": catalog_options,
+ }
+ )
+
def _save_file(self, path: str, buffer: bytes) -> None:
"""Save a file from a buffer.
@@ -776,6 +808,10 @@ def add_table(self, table: Union[QTable, Table], **table_options: any) -> None:
table options
`_
+ See Also
+ --------
+ add_markers: adds markers with a popup window when clicked
+
"""
table_bytes = io.BytesIO()
table.write(table_bytes, format="votable")
diff --git a/src/tests/__init__.py b/src/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/test_aladin.py b/src/tests/test_aladin.py
similarity index 100%
rename from src/test/test_aladin.py
rename to src/tests/test_aladin.py
diff --git a/src/test/test_coordinate_parser.py b/src/tests/test_coordinate_parser.py
similarity index 100%
rename from src/test/test_coordinate_parser.py
rename to src/tests/test_coordinate_parser.py