Visualization¶
torchsky.viz has two surfaces:
- Figures for talks, papers and notebooks: full-sky maps from HEALPix maps
or scattered samples, and spectral-cube quick-looks. Everything is drawn with
matplotlib, which is imported lazily, so importing
torchsky.vizstays cheap. - A local browser viewer for decoded images, catalogs and MOC/HiPS-style products.
FITS decoding at the application boundary is provided by torchfits.
Install¶
pip install "torchsky[viz]"
The viz extra brings fastapi/uvicorn/pillow for the server plus
matplotlib for figures. The server runs without matplotlib; figures run
without the server dependencies.
All-sky figures¶
# fragment: needs matplotlib
import torch
from torchsky.sphere import healpix
from torchsky.viz import plot_healpix_sky
nside = 256
values = torch.rand(healpix.nside2npix(nside))
plot_healpix_sky(
values,
nside=nside,
title="Surface brightness",
subtitle="Synthetic band · nside 256",
unit="counts s$^{-1}$ deg$^{-2}$",
projection="mollweide",
save="sky.png",
)
The dense map is nearest-pixel resampled onto a longitude/latitude grid whose
size follows nside, so the figure costs O(grid) rather than O(npix) and has no
empty cells. Pass resolution=(width, height) to change it, or
resample=False to box-average a map that is much finer than the grid.
Sparse maps keep their real holes:
# fragment: needs matplotlib and your own arrays
plot_healpix_sky(values, nside=nside, pixels=pixels, cmap="magma")
Scattered samples — catalog rows, source positions, simulation output — go
through sky_grid directly. Longitude wraps, latitude must be in [-90, 90],
and non-finite samples are dropped:
# fragment: needs matplotlib and your own arrays
from torchsky.viz import plot_sky_grid, sky_grid
grid = sky_grid(ra_deg, dec_deg, density, resolution=(1440, 720))
plot_sky_grid(grid, projection="aitoff", unit="sources deg$^{-2}$")
Projections¶
projection |
View |
|---|---|
mollweide (default), aitoff, hammer |
full sky, 2:1 frame |
polar |
north pole at the centre, colatitude as radius |
The polar view resamples the grid onto an azimuth/radius raster before drawing. A longitude/latitude mesh collapses every column onto the pole, and the degenerate quads leak the background between them; the raster has no such quads and its innermost ring carries the cap average.
Right ascension increases to the left by default (ra_increases_left=True),
which is the convention every astronomy figure uses; for polar the same flag
sets the clockwise/counter-clockwise direction.
Footprints and catalogs¶
# fragment: needs matplotlib and your own arrays
import math
from torchsky.sphere import healpix
from torchsky.sphere.core import lonlat_to_unit_xyz
from torchsky.sphere.moc import MOC, nest2uniq
pixels = healpix.query_disc(nside, lonlat_to_unit_xyz(80.0, -5.0), math.radians(60.0), nest=True)
moc = MOC(nest2uniq(nside, pixels)) # uniq is built from nested pixel indices
plot_healpix_sky(
values,
nside=nside,
moc=moc,
moc_label="Spectral coverage",
points=(ra_deg, dec_deg),
point_label="Catalog sources",
)
values=None draws the graticule and overlays on a blank sky, which is the
usual way to show a survey footprint on its own.
Look and feel¶
theme="dark" (default) or "paper"; cmap accepts any matplotlib colormap
or one of the bundled tile palettes (viridis, magma, thermal, gray,
rdbu_r, seismic); stretch is asinh (default), linear or log.
The figure lays itself out in inches — title block, map, right-ascension row, colour bar, legend band, footnote — so nothing overlaps at any figure size. An overlay legend is a single centred row in its own band under the map, never pinned to the projection's rim. Colorbars grow arrow ends automatically when the display range clips the data, and their ticks stay readable:
asinh/linearbars are labelled at evenly spaced positions along the bar (a data-space locator crowds labels at one end of anasinhstretch),logbars take decade ticks, falling back to the same even spacing when the range straddles no decade,- every label is rounded to two significant digits,
- a
logstretch whose low percentile is zero derives its floor from the same percentile of the positive values, so the bar spans a real dynamic range instead of hundreds of decades.
save= writes PNG/PDF/SVG with the themed background, padding included.
examples/allsky_figures.py renders the four-figure gallery used in talks:
python examples/allsky_figures.py --outdir allsky_figures --nside 256
Cube quick-looks¶
# fragment: needs matplotlib and your own arrays
import torchsky.viz as viz
viz.plot_pseudo_rgb(cube, spectral_axis, title="Pseudo-RGB", save="rgb.png")
viz.plot_pixel_spectrum(cube, spectral_axis, y=120, x=64, save="pixel.png")
plot_pseudo_rgb splits the spectral axis into three equal-count bands so each
channel carries the same amount of data, and shares one display range across
channels so colour ratios stay meaningful. Both helpers theme their artists
explicitly rather than through global rcParams, so a figure keeps its look
whenever it is saved later.
Serve A Directory¶
torchsky viz --root /path/to/data --port 8822
# fragment: needs matplotlib and your own arrays
from torchsky.viz import serve
serve(root="/path/to/data", port=8822)
Operational Notes¶
- Treat the server as a local/development viewer unless you put it behind your own authentication and reverse proxy.
- Keep roots narrow; do not serve an entire home directory by accident.
- The served UI is the committed
static/index.html; there is no npm build step or bundler required. - Large products should use explicit tiling and bounded caches. FITS
handle/decode caches belong to
torchfits.