Skip to content

Commit cbb69fa

Browse files
committed
Merge branch 'dev'
2 parents 95b4a1c + 9c241f8 commit cbb69fa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+20372
-1031
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,15 @@ If you make use of HostPhot, please cite the following [paper](https://joss.theo
170170
v3.0.0
171171
* Major internal restructuring of the code
172172
* Convert the cutouts into a module with each survey as a different submodule
173-
* Storage of input parameters
173+
* Storage of input parameters (with timestamps)
174174
* Storage of output parameters in a more friendly format
175175
* Restructuring of output files
176176
* Fix the VISTA SCIENCE ARCHIVE url for downloading VISTA images
177+
* Fix issue with VISTA image size when downloading cuouts
177178
* The download of VISTA images now automatically chooses the survey with overlap for the given coordinates, prioritising the survey with deeper coverage.
178179
* The user experience is kept approximately consistent with previous versions, but there are some changes
179-
180+
* Download of HST images is now working!
181+
* Adding download of JWST images!
180182
___
181183
v2.10.0
182184
* Adding JWST! (few NIRCam filters for now - thanks to Lisa Kelsey for this addition)

src/hostphot/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# -*- coding: utf-8 -*-
22

3-
__version__ = "3.0.0rc0"
3+
__version__ = "3.0.0rc1"

src/hostphot/cutouts/downloads.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from typing import Optional
66

77
from hostphot._constants import workdir
8-
from hostphot.utils import check_work_dir
8+
from hostphot.utils import check_work_dir, store_input
99
from hostphot.surveys_utils import (
1010
get_survey_filters,
1111
check_survey_validity,
@@ -64,8 +64,8 @@ def download_images(
6464

6565
# save input parameters
6666
if save_input is True:
67-
inputs_df = pd.DataFrame({key: [value] for key, value in input_params.items()})
68-
inputs_df.to_csv(survey_dir / "input_cutouts.csv", index=False)
67+
inputs_file = survey_dir / "input_cutouts.csv"
68+
store_input(input_params, inputs_file)
6969

7070
# check existing images
7171
if overwrite is False:
@@ -105,8 +105,9 @@ def download_images(
105105
continue # skip missing filter/image
106106
# get output file name
107107
if survey == "HST":
108-
inst = version.replace("/", "_")
109-
outfile = Path(survey_dir, f"{survey}_{inst}_{filters}.fits")
108+
#inst = version.replace("/", "_")
109+
#outfile = Path(survey_dir, f"{survey}_{inst}_{filters}.fits")
110+
outfile = Path(survey_dir, f"{survey}_{filt}.fits")
110111
else:
111112
outfile = Path(survey_dir, f"{survey}_{filt}.fits")
112113
if overwrite is True or outfile.is_file() is False:

src/hostphot/cutouts/hst.py

Lines changed: 54 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
1-
import shutil
2-
import zipfile
31
import numpy as np
4-
import pandas as pd
52
from pathlib import Path
63

7-
from astropy import wcs
84
import astropy.units as u
95
from astropy.io import fits
6+
from astropy.wcs import WCS
7+
from astropy.nddata import Cutout2D
108
from astropy.coordinates import SkyCoord
119

12-
from pyvo.dal import sia
1310
from astroquery.esa.hubble import ESAHubble # HST
1411

1512
from hostphot._constants import workdir
16-
from hostphot.utils import check_work_dir
17-
from hostphot.surveys_utils import check_HST_filters
13+
from hostphot.utils import check_work_dir, suppress_stdout
14+
from hostphot.surveys_utils import check_HST_filters, survey_pixel_scale
1815

1916
import warnings
2017
from astropy.utils.exceptions import AstropyWarning
@@ -34,7 +31,7 @@ def update_HST_header(hdu: fits.hdu.ImageHDU) -> None:
3431
warnings.simplefilter("ignore", AstropyWarning)
3532
if "PHOTPLAM" not in hdu[0].header:
3633
# MAST image: move things to hdu[0] to homogenise
37-
img_wcs = wcs.WCS(hdu[1].header)
34+
img_wcs = WCS(hdu[1].header)
3835
hdu[0].header.update(img_wcs.to_header())
3936
hdu[0].header["PHOTFLAM"] = hdu[1].header["PHOTFLAM"]
4037
hdu[0].header["PHOTPLAM"] = hdu[1].header["PHOTPLAM"]
@@ -66,6 +63,7 @@ def set_HST_image(file: str, filt: str, name: str) -> None:
6663
"""
6764
# check output directory
6865
check_work_dir(workdir)
66+
check_HST_filters(filt)
6967
obj_dir = Path(workdir, name, "HST")
7068
if obj_dir.is_dir() is False:
7169
obj_dir.mkdir(parents=True, exist_ok=True)
@@ -76,36 +74,24 @@ def set_HST_image(file: str, filt: str, name: str) -> None:
7674
hdu.writeto(outfile, overwrite=True)
7775

7876
def get_HST_images(ra: float, dec: float, size: float | u.Quantity = 3,
79-
filt: str = "WFC3_UVIS_F225W") -> list[fits.ImageHDU]:
77+
filters: list = ["WFC3_UVIS_F225W"]) -> list[fits.ImageHDU]:
8078
"""Downloads a set of HST fits images for a given set
81-
of coordinates and filters using the MAST archive.
79+
of coordinates and filters using astroquery.
8280
8381
Parameters
8482
----------
8583
ra: Right ascension in degrees.
8684
dec: Declination in degrees.
8785
size: Image size. If a float is given, the units are assumed to be arcmin.
88-
filt: Filter to use, e.g. ``WFC3_UVIS_F225W``.
86+
filters: Filters to use, e.g. ``WFC3_UVIS_F225W``.
8987
9088
Return
9189
------
9290
hdu_list: List with fits image for the given filter. ``None`` is returned if no image is found.
9391
"""
9492
esahubble = ESAHubble()
9593
esahubble.get_status_messages()
96-
check_HST_filters(filt)
97-
98-
# separate the instrument name from the actual filter
99-
split_filt = filt.split("_")
100-
if len(split_filt) == 2:
101-
filt = split_filt[-1]
102-
instrument = split_filt[0]
103-
elif len(split_filt) == 3:
104-
filt = split_filt[-1]
105-
instrument = f"{split_filt[0]}/{split_filt[1]}"
106-
else:
107-
raise ValueError(f"Incorrect filter name: {filt}")
108-
94+
10995
if isinstance(size, (float, int)):
11096
size_arcsec = (size * u.arcmin).to(u.arcsec)
11197
else:
@@ -116,43 +102,43 @@ def get_HST_images(ra: float, dec: float, size: float | u.Quantity = 3,
116102
coords = SkyCoord(
117103
ra=ra, dec=dec, unit=(u.degree, u.degree), frame="icrs"
118104
)
119-
120-
version = None
121-
if version == "HLA":
122-
# This does not seem to be faster
123-
fov = 0.2 # field-of-view in degrees
124-
access_url = " https://hla.stsci.edu/cgi-bin/hlaSIAP.cgi"
125-
svc = sia.SIAService(access_url)
126-
imgs_table = svc.search(
127-
(ra, dec), (fov / np.cos(dec * np.pi / 180), fov), verbosity=2
128-
)
129-
obs_df = pd.DataFrame(imgs_table)
130-
obs_df = obs_df[obs_df.Mode == "IMAGE"]
131-
obs_df = obs_df[obs_df.Format.str.endswith("fits")]
132-
obs_df = obs_df[obs_df.Detector == instrument]
133-
obs_df = obs_df[obs_df.Spectral_Elt == filt]
134-
obs_df = obs_df[obs_df.ExpTime == obs_df.ExpTime.max()]
135-
hdu = fits.open(obs_df.URL.values[0])
136-
else:
105+
106+
# query observations at the given coordinates
107+
with suppress_stdout():
137108
result = esahubble.cone_search_criteria(
138109
radius=3,
139110
coordinates=coords,
140111
calibration_level="PRODUCT",
141112
data_product_type="image",
142-
instrument_name=instrument,
143-
filters=filt,
113+
#instrument_name=instrument,
114+
#filters=filt,
144115
async_job=True,
145116
)
146-
147-
obs_df = result.to_pandas()
148-
obs_df = obs_df[obs_df["filter"] == filt]
117+
results_df = result.to_pandas()
118+
119+
hdu_list = []
120+
for filt in filters:
121+
check_HST_filters(filt)
122+
# separate the instrument name from the actual filter
123+
split_filt = filt.split("_")
124+
if len(split_filt) == 2:
125+
filt = split_filt[-1]
126+
instrument = split_filt[0]
127+
elif len(split_filt) == 3:
128+
filt = split_filt[-1]
129+
instrument = f"{split_filt[0]}/{split_filt[1]}"
130+
else:
131+
raise ValueError(f"Incorrect filter name: {filt}")
132+
# filter by filter and instrument
133+
obs_df = results_df[results_df["filter"] == filt]
134+
obs_df = obs_df[obs_df.instrument_name == instrument]
149135
# get only exposures shorter than one hour
150136
obs_df = obs_df[obs_df.exposure_duration < 3600]
151137
obs_df.sort_values(
152138
by=["exposure_duration"], ascending=False, inplace=True
153139
)
154140

155-
print("Looking for HST images...")
141+
# start download
156142
filename = f"HST_tmp_{ra}_{dec}" # the extension is added below
157143
for obs_id in obs_df.observation_id:
158144
try:
@@ -167,14 +153,24 @@ def get_HST_images(ra: float, dec: float, size: float | u.Quantity = 3,
167153
pass
168154
temp_file = Path(f"{filename}.fits.gz")
169155
if temp_file.is_file() is False:
170-
return None
171-
temp_dir = Path(filename) # same name as the extensionless file above
172-
with zipfile.ZipFile(temp_file, "r") as zip_ref:
173-
zip_ref.extractall(temp_dir)
174-
fits_file = [file for file in temp_dir.rglob("*.gz")][0]
175-
hdu = fits.open(fits_file)
176-
# remove the temporary files and directory
156+
hdu_list.append(None)
157+
continue
158+
hdu = fits.open(temp_file)
159+
# remove the temporary files
177160
temp_file.unlink()
178-
shutil.rmtree(temp_dir, ignore_errors=True)
179-
update_HST_header(hdu)
180-
return [hdu]
161+
# add necessary information to the header
162+
update_HST_header(hdu)
163+
hdu_list.append(hdu)
164+
# HST images are large so need to be trimmed
165+
for hdu, filt in zip(hdu_list, filters):
166+
if hdu is None:
167+
continue
168+
pixel_scale = survey_pixel_scale("HST", filt) # same pixel scale for all filters
169+
size_pixels = int(size_arcsec / pixel_scale)
170+
with warnings.catch_warnings():
171+
warnings.simplefilter("ignore", AstropyWarning)
172+
img_wcs = WCS(hdu[0].header)
173+
trimmed_data = Cutout2D(hdu[0].data, coords, size_pixels, img_wcs)
174+
hdu[0].data = trimmed_data.data
175+
hdu[0].header.update(trimmed_data.wcs.to_header())
176+
return hdu_list

src/hostphot/cutouts/jwst.py

Lines changed: 100 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import numpy as np
22
from pathlib import Path
33

4-
from astropy import wcs
4+
import astropy.units as u
55
from astropy.io import fits
6+
from astropy.wcs import WCS
7+
from astropy.nddata import Cutout2D
8+
from astropy.coordinates import SkyCoord
9+
10+
from astroquery.esa.jwst import Jwst
611

712
from hostphot._constants import workdir
8-
from hostphot.utils import check_work_dir
9-
from hostphot.surveys_utils import check_JWST_filters
13+
from hostphot.utils import check_work_dir, suppress_stdout
14+
from hostphot.surveys_utils import check_JWST_filters, survey_pixel_scale
1015

1116
import warnings
1217
from astropy.utils.exceptions import AstropyWarning
@@ -22,7 +27,7 @@ def update_JWST_header(hdu: fits.hdu.ImageHDU) -> None:
2227
# get WCS - SCI is hdu[1] - hostphot always assumes hdu[0] is used, so need to move them over
2328
with warnings.catch_warnings():
2429
warnings.simplefilter("ignore", AstropyWarning)
25-
img_wcs = wcs.WCS(hdu[1].header)
30+
img_wcs = WCS(hdu[1].header)
2631
hdu[0].header.update(img_wcs.to_header())
2732
hdu[0].header["PIXAR_SR"] = hdu[1].header["PIXAR_SR"]
2833
hdu[0].data = hdu[1].data
@@ -52,11 +57,101 @@ def set_JWST_image(file: str, filt: str, name: str) -> None:
5257
"""
5358
# check output directory
5459
check_work_dir(workdir)
60+
check_JWST_filters(filt)
5561
obj_dir = Path(workdir, name, "JWST")
5662
if obj_dir.is_dir() is False:
5763
obj_dir.mkdir(parents=True, exist_ok=True)
5864
# update header and save file
5965
hdu = fits.open(file)
6066
update_JWST_header(hdu)
6167
outfile = obj_dir / f"JWST_{filt}.fits"
62-
hdu.writeto(outfile, overwrite=True)
68+
hdu.writeto(outfile, overwrite=True)
69+
70+
def get_JWST_images(ra: float, dec: float, size: float | u.Quantity = 3,
71+
filters: list = ["WFC3_UVIS_F225W"]) -> list[fits.ImageHDU]:
72+
"""Downloads a set of JWST fits images for a given set
73+
of coordinates and filters using astroquery.
74+
75+
Parameters
76+
----------
77+
ra: Right ascension in degrees.
78+
dec: Declination in degrees.
79+
size: Image size. If a float is given, the units are assumed to be arcmin.
80+
filters: Filters to use, e.g. ``WFC3_UVIS_F225W``.
81+
82+
Return
83+
------
84+
hdu_list: List with fits image for the given filter. ``None`` is returned if no image is found.
85+
"""
86+
Jwst.get_status_messages()
87+
88+
if isinstance(size, (float, int)):
89+
size_arcsec = (size * u.arcmin).to(u.arcsec)
90+
else:
91+
size_arcsec = size.to(u.arcsec)
92+
size_arcsec = size_arcsec.value
93+
with warnings.catch_warnings():
94+
warnings.simplefilter("ignore", AstropyWarning)
95+
coords = SkyCoord(
96+
ra=ra, dec=dec, unit=(u.degree, u.degree), frame="icrs"
97+
)
98+
99+
# query observations at the given coordinates
100+
with suppress_stdout():
101+
result = Jwst.cone_search(
102+
radius=3*u.arcsec,
103+
coordinate=coords,
104+
cal_level="Top",
105+
prod_type="image",
106+
#instrument_name=instrument,
107+
#filter_name=filt,
108+
only_public=True,
109+
async_job=True,
110+
).get_data()
111+
results_df = result.to_pandas()
112+
113+
hdu_list = []
114+
for filt in filters:
115+
check_JWST_filters(filt)
116+
# separate the instrument name from the actual filter
117+
split_filt = filt.split("_")
118+
filt = split_filt[-1]
119+
instrument = split_filt[0].upper() + "/IMAGE"
120+
# filter by filter and instrument
121+
obs_df = results_df[results_df["instrument_name"] == instrument]
122+
obs_df = obs_df[obs_df["energy_bandpassname"] == filt]
123+
obs_df = obs_df[obs_df["calibrationlevel"] == 3]
124+
125+
# start download
126+
temp_file = None
127+
for obs_id in obs_df.observationid:
128+
try:
129+
product_list = Jwst.get_product_list(observation_id=obs_id, product_type='science', cal_level=3).to_pandas()
130+
# choose first file as there es no exptime info and the images take too lonk to download multiple ones
131+
file_name = [file for file in product_list.filename if file.endswith(".fits")][0]
132+
temp_file = Jwst.get_product(file_name=file_name)
133+
break
134+
except:
135+
pass
136+
if temp_file is None:
137+
hdu_list.append(None)
138+
continue
139+
hdu = fits.open(temp_file)
140+
# remove the temporary files
141+
Path(temp_file).unlink()
142+
# add necessary information to the header
143+
update_JWST_header(hdu)
144+
hdu_list.append(hdu)
145+
# JWST images are large so need to be trimmed
146+
for hdu, filt in zip(hdu_list, filters):
147+
if hdu is None:
148+
continue
149+
pixel_scale = survey_pixel_scale("JWST", filt) # same pixel scale for all filters
150+
size_pixels = int(size_arcsec / pixel_scale)
151+
with warnings.catch_warnings():
152+
warnings.simplefilter("ignore", AstropyWarning)
153+
img_wcs = WCS(hdu[0].header)
154+
trimmed_data = Cutout2D(hdu[0].data, coords, size_pixels, img_wcs)
155+
hdu[0].data = trimmed_data.data
156+
hdu[0].header.update(trimmed_data.wcs.to_header())
157+
return hdu_list

0 commit comments

Comments
 (0)