103.2. Simple Image Access (SIA) service#
103.2. Simple Image Access (SIA) service¶
For the Rubin Science Platform at data.lsst.cloud.
Data Release: Data Preview 2
Container Size: large
LSST Science Pipelines version: r30.0.9
Last verified to run: 2026-07-20
Repository: github.com/lsst/tutorial-notebooks
Learning objective: How to use the SIA2 service to access image data.
LSST data products: deep_coadd
Packages: lsst.rsp, lsst.rsp.service, pyvo.dal
Credit: Originally developed by the Rubin Community Science team. Please consider acknowledging them if this notebook is used for the preparation of journal articles, software releases, or other notebooks.
Get Support: Everyone is encouraged to ask questions or raise issues in the Support Category of the Rubin Community Forum. Rubin staff will respond to all questions posted there.
1. Introduction¶
Simple Image Access (SIA) is a protocol of the International Virtual Observatory Alliance (IVOA). It provides a standardized model for image metadata, and the capability to query and retrieve image datasets. Learn more in the IVOA SIA documentation.
Why use the SIA service?
SIA is an image access service that is broadly-used and is relatively easy to get started with.
When to use the SIA service.
Use the SIA service to find, retrieve, and display whole images, based on their spatial, temporal, and spectral coverage. SIA is good for quick access to images' pixel and header data, for display and examination by eye.
If only a small part of the image is needed, use the image cutouts tool instead.
For science cases that involve source detection or image analysis with the LSST Science Pipelines, the butler should instead be used to find and retrieve images.
The SIA Version 2 service is used in this tutorial (SIA2).
Related tutorials: Other 100-level tutorials demonstrate how to access images via other services, such as ObsTAP and the Butler. The 100-level tutorials on image display have more on using Firefly. The 200-level tutorials on coadded and visit images have more on these data products (e.g., the pixel data and metadata).
1.1. Import packages¶
Import common scientific analysis packages numpy and astropy.
Import LSST Science Pipelines packages for image display, image type conversion, and utilities for remote data access.
Import pyvo packages for working with the SIA2Service.
import numpy as np
from astropy.coordinates import SkyCoord
import lsst.afw.display as afwDisplay
from lsst.images.serialization import read_archive
from lsst.rsp import RSPDiscovery
from lsst.rsp.utils import get_pyvo_auth
1.2. Define parameters and functions¶
Define search coordinates right ascension (target_ra) and declination (target_dec), in degrees, to use in all queries.
These coordinates are in the ELAISS1 deep drilling field.
target_ra = 10.26
target_dec = -44.49
Set afwDisplay to use Firefly, and define afw_display to show images in frame 1.
afwDisplay.setDefaultBackend("firefly")
afw_display = afwDisplay.Display(frame=1)
2. Instantiate the SIA2 service¶
Instantiate the SIA2 service.
discovery = RSPDiscovery("dp2")
sia_client = discovery.get_sia_client()
3. Query for images¶
It is recommended to tightly constrain image queries, so that they return only the image data products needed for a given scientific analysis.
The main constraints on SIA2 Service queries are:
- position / coordinate / region overlap
- image type (calibration level: raw, processed, coadded)
- band (filter, spectral coverage)
- time (date and time of acquisition)
Find a list of all possible constraints, and their formats, in the SIA2Query documentation.
3.1. Position¶
Query by position.
To query only by position is not recommended because it would return every image that overlaps that region, including raw, direct, difference, and coadded images. Typically, for scientific analyses, a specific image type or band is desired and should be included in the query, as demonstrated in following sections.
For these examples, set the maximum number of records (maxrec) to 3.
use_maxrec = 3
The position is a region, defined as a 3-, 4-, or $\geq$6-element tuple:
- circle: (ra, dec, radius)
- range: (long1, long2, lat1, lat2)
- polygon: (ra, dec, ra, dec, ra, dec, ...)
Define pos as a circle using a tuple of right ascension, declination, and radius in degrees.
circle = (target_ra, target_dec, 0.05)
Run the SIA2 query.
results = sia_client.search(pos=circle, maxrec=use_maxrec)
Show the results as a table.
Notice: Some exposures of ECDFS erroneously have a target name of
slew_icrs, but they are of ECDFS.
results.to_table()
| dataproduct_type | dataproduct_subtype | facility_name | calib_level | target_name | obs_id | obs_collection | obs_publisher_did | access_url | access_format | s_resolution | s_xel1 | s_xel2 | t_xel | t_min | t_max | t_exptime | t_resolution | em_xel | em_min | em_max | em_res_power | em_filter_name | o_ucd | pol_xel | instrument_name | lsst_visit | lsst_detector | lsst_tract | lsst_patch | lsst_band | lsst_filter | obs_title | s_ra | s_dec | s_fov | s_region |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| arcsec | d | d | s | s | m | m | deg | deg | deg | |||||||||||||||||||||||||||
| object | object | object | int32 | object | object | object | object | object | object | float64 | int64 | int64 | int64 | float64 | float64 | float64 | float64 | int64 | float64 | float64 | float64 | object | object | int64 | object | int64 | int64 | int64 | int64 | object | object | object | float64 | float64 | float64 | object |
| image | lsst.deep_coadd | Rubin:Simonyi | 3 | lsst_cells_v2-2704-95 | LSST.DP2 | ivo://org.rubinobs/usdac/lsst-dp2?repo=dp2&id=019ed8bf-a4b6-7dcf-897a-09a868297313 | https://data.lsst.cloud/api/datalink/links?ID=ivo%3A%2F%2Forg.rubinobs%2Flsst-dp2%3Frepo%3Ddp2%26id%3D019ed8bf-a4b6-7dcf-897a-09a868297313 | application/x-votable+xml;content=datalink | -- | 3300 | 3300 | -- | -- | -- | -- | -- | -- | 5.51e-07 | 6.891e-07 | -- | r | phot.flux.density | -- | LSSTCam | -- | -- | 2704 | 95 | r | deep_coadd - r - tract=2704 patch=95 | 10.287514752713435 | -44.62185860844496 | 0.25924239384409 | POLYGON ICRS 10.158344 -44.713304 10.416311 -44.713568 10.416274 -44.530266 10.159130 -44.530004 | ||
| image | lsst.deep_coadd | Rubin:Simonyi | 3 | lsst_cells_v2-2704-95 | LSST.DP2 | ivo://org.rubinobs/usdac/lsst-dp2?repo=dp2&id=019ed8bf-acdf-7c1f-9900-1c07550adbe2 | https://data.lsst.cloud/api/datalink/links?ID=ivo%3A%2F%2Forg.rubinobs%2Flsst-dp2%3Frepo%3Ddp2%26id%3D019ed8bf-acdf-7c1f-9900-1c07550adbe2 | application/x-votable+xml;content=datalink | -- | 3300 | 3300 | -- | -- | -- | -- | -- | -- | 4.026e-07 | 5.483e-07 | -- | g | phot.flux.density | -- | LSSTCam | -- | -- | 2704 | 95 | g | deep_coadd - g - tract=2704 patch=95 | 10.287514752713435 | -44.62185860844496 | 0.25924239384409 | POLYGON ICRS 10.158344 -44.713304 10.416311 -44.713568 10.416274 -44.530266 10.159130 -44.530004 | ||
| image | lsst.deep_coadd | Rubin:Simonyi | 3 | lsst_cells_v2-2704-95 | LSST.DP2 | ivo://org.rubinobs/usdac/lsst-dp2?repo=dp2&id=019ed8bf-f486-7d17-b2a2-9a1dc219ee5e | https://data.lsst.cloud/api/datalink/links?ID=ivo%3A%2F%2Forg.rubinobs%2Flsst-dp2%3Frepo%3Ddp2%26id%3D019ed8bf-f486-7d17-b2a2-9a1dc219ee5e | application/x-votable+xml;content=datalink | -- | 3300 | 3300 | -- | -- | -- | -- | -- | -- | 6.936e-07 | 8.188e-07 | -- | i | phot.flux.density | -- | LSSTCam | -- | -- | 2704 | 95 | i | deep_coadd - i - tract=2704 patch=95 | 10.287514752713435 | -44.62185860844496 | 0.25924239384409 | POLYGON ICRS 10.158344 -44.713304 10.416311 -44.713568 10.416274 -44.530266 10.159130 -44.530004 |
Keep circle to reuse below, but delete the results.
del results
Option to pass the coordinates to the SIA2 service as an astropy SkyCoord.
# coord = SkyCoord(target_ra, target_dec, unit="deg")
# coord_circle = (coord, 0.05)
# results = sia_client.search(pos=coord_circle, maxrec=use_maxrec)
# results.to_table()
# del coord, coord_circle, results
Option to define search boundaries as maximum and minimum coordinates.
# bounds = (target_ra-0.05, target_ra+0.05, target_dec-0.05, target_dec+0.05)
# results = sia_client.search(pos=bounds, maxrec=use_maxrec)
# results.to_table()
# del bounds, results
Option to define a polygon with four points.
# polygon = (target_ra-0.05, target_dec-0.05,
# target_ra+0.05, target_dec-0.05,
# target_ra+0.05, target_dec+0.05,
# target_ra-0.05, target_dec+0.05)
# results = sia_client.search(pos=polygon, maxrec=use_maxrec)
# results.to_table()
# del polygon, results
3.2. Image type¶
Query by image type, which in the SIA2 service is specified by the calibration level (calib_level)
and by the data product subtype (dpsubtype).
Calibration levels and data product subtypes names are:
- 1 :
raw - 2 :
visit_image - 3 :
deep_coadd,template_coadd,difference_image
3.2.1. Deep coadd images¶
The only images that have been released in EDP2 are the deep_coadds.
Query for deep_coadd images by setting calib_level=3 and dpsubtype='lsst.deep_coadd'.
Do not pass use_maxrec here, retrieve all the deep_coadd images that match the search constraints.
results = sia_client.search(pos=circle, calib_level=3, dpsubtype='lsst.deep_coadd').to_table()
print(len(results))
12
Option to show the full table of results.
# results
Print the number of unique values of lsst_band (filters).
values, counts = np.unique(results['lsst_band'],
return_counts=True)
for value, count in zip(values, counts):
print(value, count)
g 2 i 2 r 2 u 2 y 2 z 2
There are 2 overlapping patches for the search coordinates, and thus 2 deep_coadd images for each of the six filters.
del results, values, counts
3.3. Band¶
Query by band.
With the SIA2 Service, band is not the letter name of a filter, but a wavelength range (in meters).
For LSST, the filter transmission curves barely overlap and these values can be used as the band "edges".
band_edges = {'u': (3.000e-07, 3.932e-07),
'g': (4.026e-07, 5.483e-07),
'r': (5.510e-07, 6.891e-07),
'i': (6.936e-07, 8.188e-07),
'z': (8.192e-07, 9.201e-07),
'y': (9.278e-07, 1.100e-06)}
Define the band to pass to the SIA2 service.
Use the lower end of the LSST r-band, and the upper end of the LSST i-band,
to retrieve both r- and i-band images.
band = (band_edges['r'][0], band_edges['i'][1])
print(band)
(5.51e-07, 8.188e-07)
Query the SIA2 service, and convert the results to an astropy table.
results = sia_client.search(pos=circle, band=band, calib_level=3)
table = results.to_table()
Print the number of r- and i-band images returned.
values, counts = np.unique(table['lsst_band'], return_counts=True)
for value, count in zip(values, counts):
print(value, count)
del values, counts
i 2 r 2
del results, table
Alternatively, define a single wavelength (5e-07 m is in the g-band) and the SIA2 service will return all images with a bandpass that overlaps that wavelength.
results = sia_client.search(pos=circle, band=(5e-07), calib_level=3)
print(len(results))
2
del band
4. Retrieve and display an image¶
Use the results from the query in Section 3.3.
Extract the access URL from the first row of the results as datalink_url.
Retrieve the datalink VOTable document as dl_result. At this point the service requires authorization credentials, which are passed with the utility function get_pyvo_auth() from the lsst.rsp package.
Get the URL for the specific image as image_url.
dl_result = discovery.get_datalink_results(results[0])
datalink_url = results[0].access_url
image_url = dl_result.getrecord(0).get('access_url')
Option to display the URLs.
# datalink_url
# dl_result
# image_url
Use the read_archive method to read the entire image into a CellCoadd object. The CellCoadd format is designed to work with seamlessly with afw.display and so is recommended for all Rubin coadd images.
deep_coadd = read_archive(image_url)
Display the exposure in the Firefly tab.
afw_display.mtv(deep_coadd)
Set the mask transparency to 100% transparent.
afw_display.setMaskTransparency(100)