Last active
September 1, 2026 17:41
-
-
Save bmorris3/14e7b92113e4d03cacb011bfbd6b3711 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "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