
Jdaviz Image Demo#
Author: Clare Shanahan, Space Telescope Science Institute
Last update: May 5, 2026
Tutorial Overview#
This tutorial will demonstrate an example workflow to show of some key features of Jdaviz for imaging data. We will be looking at some basic functionality (starting the app in a notebook, loading data, pan/zoom, subsets/regions of interest) as well as some of the higher level plugin tools including catalog search and aperture photometry.
Starting Jdaviz and loading data (NGC 346, star forming region).
Setting display options for loaded data.
Aligning images by pixel / wcs. Blinking between loaded images.
Loading GAIA catalog and plotting sources on images. Zooming and panning on image.
Drawing and recentering a subset.
Aperture photometry.
The notebook demo will demonstrate how to do these tasks in the application UI, but this notebook has API calls to do the same steps programatically. (That is the magic of jdaviz!)
First, import, create, and show an Jdaviz app instance in the notebook. It will be empty until we load data, but the application displayed in the following cell will be our workspace, where all changes made by clicking or programatically via the API will be reflected. (You will be scrolling up to this cell frequently if you are following along in the notebook.)
import jdaviz as jd
jd.show()
Loading Data#
In addition to loading local data files or array data (e.g., a Spectrum1D or numpy array/CCDData) in a notebook, jdaviz can download and load data directly from the MAST archive when given a URI.
We will download two level 3 NIRCAM images of NGC 346 in two different filters (F335M and F227M) and load them as two layers in the same image viewer.
(With ‘cache=True’, a local copy of the data is saved so it will not need to be downloaded next time.)
filenames = ['jw01227025001_04101_00001_nrcalong_i2d.fits', # F277W
'jw01227025001_02101_00001_nrcalong_i2d.fits', # F335M
'jw01227025001_06101_00001_nrcalong_i2d.fits'] # F444W
with jd.batch_load(): # not necessary, but this context manager makes loading multiple files more efficient
for filename in filenames:
jd.load(f'mast:JWST/product/{filename}', format='Image', cache=True) # to re-download from MAST (or use cached files)
# jd.load(f'./data/{filename}', format = 'Image', cache=True) # pre-downloaded data
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01227025001_04101_00001_nrcalong_i2d.fits to ./jw01227025001_04101_00001_nrcalong_i2d.fits ...
[Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01227025001_02101_00001_nrcalong_i2d.fits to ./jw01227025001_02101_00001_nrcalong_i2d.fits ...
[Done]
Downloading URL https://mast.stsci.edu/api/v0.1/Download/file?uri=mast:JWST/product/jw01227025001_06101_00001_nrcalong_i2d.fits to ./jw01227025001_06101_00001_nrcalong_i2d.fits ...
[Done]
Once these files are loaded (which will take longer the first time if they need to be downloaded), they will appear in the Jdaviz app in the cell above. Both datasets (given default labels ‘A’, ‘B’, and ‘C’) were loaded into the same viewer. They appear in the data layer menu on the right hand side. You can select/deselect loaded data to display, remove/re-add data from the viewer, and delete loaded data from the app. To blink between images, press the ‘b’ key (note that blinking will de-select non active layers).
Linking by WCS#
By default, images are pixel linked when loaded. We can link them by WCS in the ‘Orientation’ plugin. (This doesn’t matter so much in this case, it so happens, but we want to be using world coordinates for catalog functionality).
orientation = jd.plugins['Orientation']
orientation.align_by = "WCS"
Modifying Image Display Options#
Now, we will modify some of the display options to better suit our data. For the live demo, we will do this in the UI in the ‘Plot Options’ plugin by modifying the image stretch from linear to logarithmic, and setting vmax to a more appropriate value. We will make use of ‘layer multiselect’ to apply these options to all images at the same time, but you can set different display options for each image independently as well. The following cell accomplishes the same task from the API.
(Take a look around the Plot Options plugin, there is a lot more you can do to customize display settings including colormap, setting layer colors and opacities to create composite RGB images, and displaying contours.)
# the following code is the API equivalent to the series of UI clicks we will do in the live demo
# get the 'Plot Options' plugin
plot_options = jd.plugins['Plot Options']
# enable mutiselect so our chosen options are applied to all images
plot_options.layer.multiselect = True
plot_options.select_all()
# switch stretch function from default linear to log
plot_options.stretch_function = 'log'
# use the 99.5% stretch function preset
plot_options.stretch_preset = '99.5%'
# increase vmax to a more suitable value
plot_options.stretch_vmax = 6
Now that we know how to set our own plot options, let’s use one of the RGB presets, just for fun. This will apply preset color, stretch, and opacity settings to each layer.
# the 'obj' here means that we're using a method not yet exposed as part of the public API
plot_options._obj.image_color_mode_value = 'One color per layer'
plot_options.apply_RGB_presets()
Loading Catalogs#
SDSS and Gaia catalogs can be loaded directly from jdaviz (with more catalog support planned in the future). Additionally, you can load your own catalog into the application.
In this demo, we are going to query for Gaia sources in the FOV of our image, plot some of them over the image, select them all and zoom to the region containing the sources using ‘zoom_to_selected’, and finally we will select just one source and zoom to that.
catalogs_plugin = jd.plugins['Catalog Search']._obj
# select Gaia catalog
catalogs_plugin.catalog.selected = 'Gaia'
# Set an upper limit on the number of sources
catalogs_plugin.max_sources = 500
# and run the search
catalogs_plugin.search()
# select all catalog table entries
catalogs_plugin.table.selected_rows = catalogs_plugin.table.items
# and zoom to region containing these points
catalogs_plugin.zoom_to_selected()
In preparation for Gaia DR4, the Gaia archive is in evolution. Unfortunately, it may be unstable at times and particular types of queries may time out. Please consider registering for a user account (https://www.cosmos.esa.int/web/gaia-users/register). For questions or advice, please contact the Gaia helpdesk (https://www.cosmos.esa.int/web/gaia/gaia-helpdesk).
# Pick out one source as an example- we will use the source at index 455
idx = 455
# now select just the matching source
catalogs_plugin.table.selected_rows = catalogs_plugin.table.items[idx: idx+1]
# and zoom to that point
catalogs_plugin.zoom_to_selected()
# Get the RA and Dec of the selected source
ra = float(catalogs_plugin.table.items[idx]['Right Ascension (degrees)'])
dec = float(catalogs_plugin.table.items[idx]['Declination (degrees)'])
print(f"RA: {ra}")
print(f"Dec: {dec}")
RA: 14.73761
Dec: -72.14252
Drawing and interacting with regions (subsets)#
Lets do some rough analysis of our single selected gaia source. We can draw a circular ‘subset’ near the souce, then use ‘recenter’ to centroid the position a little better. Again, we will be doing this in the UI during the demo but the following API calls will replicate these steps.
from regions import CircleSkyRegion
from astropy.coordinates import SkyCoord
import astropy.units as u
# get the 'Subsets Tools' plugin where we can create and interact with spatial regions in jdaviz
subset_plugin = jd.plugins['Subset Tools']
# load a circular region at the location of our selected catalog item
# just for demo sake, shift the coordinates a tiny bit from the catalog position so we
# can use the 'recenter' position. This recreates the scenario of freehand drawing a circular subset
# rather than placing it at an exact location
shift = 3. * 0.062 / 3600. # shift by 3 pixels
circular_region = CircleSkyRegion(center=SkyCoord(ra + shift, dec, unit='degree'), radius=0.0001*u.deg)
subset_plugin.import_region(circular_region)
# use the 'recenter' function to get our drawn region closer to the center of the source
# call this a few times to converge on a better position
subset_plugin.subset = 'Subset 1'
subset_plugin.recenter()
subset_plugin.recenter()
subset_plugin.recenter()
Aperture Photometry#
With a subset created and placed on one of the sources in the image, we can use the Aperture Photometry plugin to do some analysis. We can make use of ‘batch mode’ to get photometry for all loaded images using the same subset (which is useful assuming images are well aligned).
# get the plugin
aperture_photometry = jd.plugins['Aperture Photometry']._obj
# enable multiselect mode to do photometry on multiple datasets at once
aperture_photometry.multiselect = True
# select all datasets
aperture_photometry.dataset.select_all()
# select our photometric aperture
aperture_photometry.aperture.selected = 'Subset 1'
# and run photometry to produce output table
aperture_photometry.vue_do_aper_phot()
/home/runner/micromamba/envs/ci-env/lib/python3.11/site-packages/jupyter_client/session.py:727: UserWarning: Message serialization failed with:
Out of range float values are not JSON compliant
Supporting this message is deprecated in jupyter-client 7, please make sure your message is JSON-compliant
content = self.pack(content)
/home/runner/micromamba/envs/ci-env/lib/python3.11/site-packages/jupyter_client/session.py:727: UserWarning: Message serialization failed with:
Out of range float values are not JSON compliant
Supporting this message is deprecated in jupyter-client 7, please make sure your message is JSON-compliant
content = self.pack(content)
/home/runner/micromamba/envs/ci-env/lib/python3.11/site-packages/jupyter_client/session.py:727: UserWarning: Message serialization failed with:
Out of range float values are not JSON compliant
Supporting this message is deprecated in jupyter-client 7, please make sure your message is JSON-compliant
content = self.pack(content)
We can look at the output table to see the photometry results for the aperture on each image layer and compare, for example, the magnitude in each filter.
aperture_photometry.table
(Bonus exercise if there is time remaining: Disable multiselect in Aperture Photometry and calculate and display a radial profile on each layer)