Skip to content

Instantly share code, notes, and snippets.

@bmorris3
Last active September 1, 2026 17:41
Show Gist options
  • Select an option

  • Save bmorris3/14e7b92113e4d03cacb011bfbd6b3711 to your computer and use it in GitHub Desktop.

Select an option

Save bmorris3/14e7b92113e4d03cacb011bfbd6b3711 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"id": "20e15b27-609b-429c-a5fb-a0811a14c47c",
"metadata": {},
"source": [
"First, download the 100k source catalog from vizier. Only do this one time!"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "087ca1ac-50d5-406c-b2d5-c233a7fdfbe7",
"metadata": {},
"outputs": [],
"source": [
"from astroquery.vizier import Vizier\n",
"\n",
"catalog = Vizier(\n",
" catalog='IV/39/tic82',\n",
" row_limit=100_000\n",
")\n",
"\n",
"result = catalog.query_object(\"omega Cen\", radius=0.2 * u.deg)[0]\n",
"result.write('omega_Cen_TIC.ecsv')"
]
},
{
"cell_type": "markdown",
"id": "226374ab-d142-4e8b-abed-54493b8c741e",
"metadata": {},
"source": [
"Now the demo:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "04c98ed6-8ed7-4190-86dd-f27faf2ec8c8",
"metadata": {},
"outputs": [],
"source": [
"from mast_aladin import MastAladin\n",
"from astropy.table import Table\n",
"from astropy.coordinates import SkyCoord\n",
"from sidecar import Sidecar\n",
"import astropy.units as u\n",
"import numpy as np\n",
"import asyncio\n",
"from time import time\n",
"\n",
"class Timer:\n",
" def __init__(self, timeout, callback):\n",
" self._timeout = timeout\n",
" self._callback = callback\n",
"\n",
" async def _job(self):\n",
" await asyncio.sleep(self._timeout)\n",
" self._callback()\n",
"\n",
" def start(self):\n",
" self._task = asyncio.ensure_future(self._job())\n",
"\n",
" def cancel(self):\n",
" self._task.cancel()\n",
"\n",
"\n",
"def throttle(wait):\n",
" \"\"\" Decorator that prevents a function from being called\n",
" more than once every wait period.\n",
" \n",
" Source: https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Events.html#throttling \n",
" \n",
" \"\"\"\n",
" def decorator(fn):\n",
" time_of_last_call = 0\n",
" scheduled, timer = False, None\n",
" new_args, new_kwargs = None, None\n",
" def throttled(*args, **kwargs):\n",
" nonlocal new_args, new_kwargs, time_of_last_call, scheduled, timer\n",
" def call_it():\n",
" nonlocal new_args, new_kwargs, time_of_last_call, scheduled, timer\n",
" time_of_last_call = time()\n",
" fn(*new_args, **new_kwargs)\n",
" scheduled = False\n",
" time_since_last_call = time() - time_of_last_call\n",
" new_args, new_kwargs = args, kwargs\n",
" if not scheduled:\n",
" scheduled = True\n",
" new_wait = max(0, wait - time_since_last_call)\n",
" timer = Timer(new_wait, call_it)\n",
" timer.start()\n",
" return throttled\n",
" return decorator\n",
"\n",
"catalog = Table.read('omega_Cen_TIC.ecsv')\n",
"ra_column = 'RAJ2000'\n",
"dec_column = 'DEJ2000'\n",
"\n",
"source_coords = SkyCoord(ra=catalog[ra_column], dec=catalog[dec_column], unit=u.deg)\n",
"xmin, xmax = source_coords.ra.deg.min(), source_coords.ra.deg.max()\n",
"ymin, ymax = source_coords.dec.deg.min(), source_coords.dec.deg.max()\n",
"\n",
"corners = np.array([\n",
" [xmin, ymin],\n",
" [xmax, ymin],\n",
" [xmax, ymax],\n",
" [xmin, ymax]\n",
"])\n",
"\n",
"ma = MastAladin(full_screen=True)\n",
"\n",
"ma.target = SkyCoord(ra=catalog['RAJ2000'].mean(), dec=catalog['DEJ2000'].mean(), unit=u.deg)\n",
"ma.fov = 0.01 * u.deg\n",
"\n",
"with Sidecar(anchor='split-right'):\n",
" display(ma)\n",
"\n",
"catalog_options = dict(\n",
" shape='cross',\n",
" size=10,\n",
" color='#ff0000',\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "28ef2816-b9b7-4294-ba8f-9a1805efb2a7",
"metadata": {},
"outputs": [],
"source": [
"@throttle(1)\n",
"def on_fov_update(*args):\n",
" viewport = ma.get_viewport_region()\n",
" in_viewport = viewport.contains(source_coords, ma.wcs)\n",
" n_sources_max = 5000\n",
" n_sources = in_viewport.sum()\n",
"\n",
" if 0 < n_sources <= n_sources_max:\n",
"\n",
" # if the viewport has changed and few enough sources\n",
" # will be visible, find and remove any catalog or stcs_region\n",
" # overlays, then add back the table of visible sources\n",
" for overlay in ma.overlays:\n",
" if overlay.startswith(('catalog', 'stcs_region')):\n",
" ma.remove_overlay(overlay)\n",
"\n",
" ma.add_table(\n",
" catalog[in_viewport],\n",
" name=f'catalog ({n_sources} of {source_coords.size})',\n",
" **catalog_options\n",
" )\n",
" elif n_sources > n_sources_max:\n",
"\n",
" # if the viewport contains too many sources \n",
" for overlay in ma.overlays:\n",
" if overlay.startswith('catalog'):\n",
" ma.remove_overlay(overlay)\n",
"\n",
" if not any(overlay.startswith('stcs_region') for overlay in ma.overlays):\n",
" corner_string = ' '.join(map(lambda x: \" \".join(map(\"{0:f}\".format, x)), corners))\n",
" stcs = f\"POLYGON ICRS {corner_string}\"\n",
" overlay_options = dict(\n",
" fill=True,\n",
" opacity=0.3,\n",
" color=catalog_options['color'],\n",
" fillColor=catalog_options['color'],\n",
" lineDash=[15, 30]\n",
" )\n",
" ma.add_graphic_overlay_from_stcs(stcs, **overlay_options)\n",
"\n",
" # if n_sources == 0, do nothing. this case happens when\n",
" # the user zooms in so far that no sources are shown, or pans\n",
" # away so the catalog is totally outside the viewport\n",
" return\n",
"\n",
"ma.unobserve_all() # this line is here so you can tweak the callback above and run this cell again\n",
"ma.observe(on_fov_update, ['_target', '_fov', '_rotation', '_fov_xy'])\n",
"\n",
"\n",
"on_fov_update()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.13.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment