Skip to content

Commit

Permalink
Update docs 0.11 (#118)
Browse files Browse the repository at this point in the history
* Documentation for asdf_cut

* Remove anon from s3fs
  • Loading branch information
snbianco authored May 28, 2024
1 parent 8ba4902 commit 29ee109
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 49 deletions.
1 change: 1 addition & 0 deletions astrocut/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ class UnsupportedPythonError(Exception):
from .cutouts import fits_cut, img_cut, normalize_img # noqa
from .cutout_processing import (path_to_footprints, center_on_path, # noqa
CutoutsCombiner, build_default_combine_function) # noqa
from .asdf_cutouts import asdf_cut, get_center_pixel # noqa
71 changes: 39 additions & 32 deletions astrocut/asdf_cutouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,42 @@


def _get_cloud_http(s3_uri: str) -> str:
""" Get the HTTP URI of a cloud resource from an S3 URI
"""
Get the HTTP URI of a cloud resource from an S3 URI.
Parameters
----------
s3_uri : string
the S3 URI of the cloud resource
"""
# create file system
fs = s3fs.S3FileSystem(anon=True)
fs = s3fs.S3FileSystem()

# open resource and get URL
with fs.open(s3_uri, 'rb') as f:
return f.url()


def get_center_pixel(gwcsobj: gwcs.wcs.WCS, ra: float, dec: float) -> tuple:
""" Get the center pixel from a roman 2d science image
"""
Get the center pixel from a Roman 2D science image.
For an input RA, Dec sky coordinate, get the closest pixel location
on the input Roman image.
Parameters
----------
gwcsobj : gwcs.wcs.WCS
the Roman GWCS object
The Roman GWCS object.
ra : float
the input Right Ascension
The input right ascension.
dec : float
the input Declination
The input declination.
Returns
-------
tuple
the pixel position, FITS wcs object
The pixel position, FITS wcs object
"""

# Convert the gwcs object to an astropy FITS WCS header
Expand All @@ -73,11 +75,12 @@ def get_center_pixel(gwcsobj: gwcs.wcs.WCS, ra: float, dec: float) -> tuple:
return (row, col), wcs_updated


def get_cutout(data: asdf.tags.core.ndarray.NDArrayType, coords: Union[tuple, SkyCoord],
wcs: astropy.wcs.wcs.WCS = None, size: int = 20, outfile: str = "example_roman_cutout.fits",
write_file: bool = True, fill_value: Union[int, float] = np.nan,
gwcsobj: gwcs.wcs.WCS = None) -> astropy.nddata.Cutout2D:
""" Get a Roman image cutout
def _get_cutout(data: asdf.tags.core.ndarray.NDArrayType, coords: Union[tuple, SkyCoord],
wcs: astropy.wcs.wcs.WCS = None, size: int = 20, outfile: str = "example_roman_cutout.fits",
write_file: bool = True, fill_value: Union[int, float] = np.nan,
gwcsobj: gwcs.wcs.WCS = None) -> astropy.nddata.Cutout2D:
"""
Get a Roman image cutout.
Cut out a square section from the input image data array. The ``coords`` can either be a tuple of x, y
pixel coordinates or an astropy SkyCoord object, in which case, a wcs is required. Writes out a
Expand Down Expand Up @@ -154,7 +157,8 @@ def get_cutout(data: asdf.tags.core.ndarray.NDArrayType, coords: Union[tuple, Sk


def _write_fits(cutout: astropy.nddata.Cutout2D, outfile: str = "example_roman_cutout.fits"):
""" Write cutout as FITS file
"""
Write cutout as FITS file.
Parameters
----------
Expand All @@ -173,7 +177,8 @@ def _write_fits(cutout: astropy.nddata.Cutout2D, outfile: str = "example_roman_c


def _slice_gwcs(gwcsobj: gwcs.wcs.WCS, slices: Tuple[slice, slice]) -> gwcs.wcs.WCS:
""" Slice the original gwcs object
"""
Slice the original gwcs object.
"Slices" the original gwcs object down to the cutout shape. This is a hack
until proper gwcs slicing is in place a la fits WCS slicing. The ``slices``
Expand Down Expand Up @@ -211,7 +216,8 @@ def _slice_gwcs(gwcsobj: gwcs.wcs.WCS, slices: Tuple[slice, slice]) -> gwcs.wcs.


def _write_asdf(cutout: astropy.nddata.Cutout2D, gwcsobj: gwcs.wcs.WCS, outfile: str = "example_roman_cutout.asdf"):
""" Write cutout as ASDF file
"""
Write cutout as ASDF file.
Parameters
----------
Expand All @@ -236,32 +242,33 @@ def _write_asdf(cutout: astropy.nddata.Cutout2D, gwcsobj: gwcs.wcs.WCS, outfile:
def asdf_cut(input_file: str, ra: float, dec: float, cutout_size: int = 20,
output_file: str = "example_roman_cutout.fits",
write_file: bool = True, fill_value: Union[int, float] = np.nan) -> astropy.nddata.Cutout2D:
""" Preliminary proof-of-concept functionality.
"""
Takes a single ASDF input file (`input_file`) and generates a cutout of designated size `cutout_size`
around the given coordinates (`coordinates`).
Takes a single ASDF input file (``input_file``) and generates a cutout of designated size ``cutout_size``
around the given coordinates (``coordinates``).
Preliminary proof-of-concept functionality.
Parameters
----------
input_file : str
the input ASDF file
The input ASDF file.
ra : float
the Right Ascension of the central cutout
The right ascension of the central cutout.
dec : float
the Declination of the central cutout
cutout_size : int, optional
the image cutout pixel size, by default 20
output_file : str, optional
the name of the output cutout file, by default "example_roman_cutout.fits"
write_file : bool, by default True
Flag to write the cutout to a file or not
fill_value: int | float, by default np.nan
The fill value for pixels outside the original image.
The declination of the central cutout.
cutout_size : int
Optional, default 20. The image cutout pixel size.
output_file : str
Optional, default "example_roman_cutout.fits". The name of the output cutout file.
write_file : bool
Optional, default True. Flag to write the cutout to a file or not.
fill_value: int | float
Optional, default `np.nan`. The fill value for pixels outside the original image.
Returns
-------
astropy.nddata.Cutout2D:
an image cutout object
An image cutout object.
"""

# if file comes from AWS cloud bucket, get HTTP URL to open with asdf
Expand All @@ -278,5 +285,5 @@ def asdf_cut(input_file: str, ra: float, dec: float, cutout_size: int = 20,
pixel_coordinates, wcs = get_center_pixel(gwcsobj, ra, dec)

# create the 2d image cutout
return get_cutout(data, pixel_coordinates, wcs, size=cutout_size, outfile=output_file,
write_file=write_file, fill_value=fill_value, gwcsobj=gwcsobj)
return _get_cutout(data, pixel_coordinates, wcs, size=cutout_size, outfile=output_file,
write_file=write_file, fill_value=fill_value, gwcsobj=gwcsobj)
20 changes: 10 additions & 10 deletions astrocut/tests/test_asdf_cut.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from astropy.wcs.utils import pixel_to_skycoord
from gwcs import wcs
from gwcs import coordinate_frames as cf
from astrocut.asdf_cutouts import get_center_pixel, get_cutout, asdf_cut, _slice_gwcs, _get_cloud_http
from astrocut.asdf_cutouts import get_center_pixel, asdf_cut, _get_cutout, _slice_gwcs, _get_cloud_http


def make_wcs(xsize, ysize, ra=30., dec=45.):
Expand Down Expand Up @@ -137,7 +137,7 @@ def test_get_cutout(output, fakedata, quantity):
data = data.value

# create cutout
cutout = get_cutout(data, skycoord, wcs, size=10, outfile=output_file)
cutout = _get_cutout(data, skycoord, wcs, size=10, outfile=output_file)

assert_same_coord(5, 10, cutout, wcs)

Expand Down Expand Up @@ -185,7 +185,7 @@ def test_fail_write_asdf(fakedata, output):
data, gwcs = fakedata
skycoord = gwcs(25, 25, with_units=True)
wcs = WCS(gwcs.to_fits_sip())
get_cutout(data, skycoord, wcs, size=10, outfile=output_file)
_get_cutout(data, skycoord, wcs, size=10, outfile=output_file)


def test_cutout_nofile(make_file, output):
Expand Down Expand Up @@ -217,7 +217,7 @@ def test_cutout_poles(makefake):
wcs = WCS(gwcs.to_fits_sip())

# get cutout
cutout = get_cutout(data, cc, wcs, size=50, write_file=False)
cutout = _get_cutout(data, cc, wcs, size=50, write_file=False)
assert_same_coord(5, 10, cutout, wcs)

# check cutout contains all data
Expand All @@ -232,7 +232,7 @@ def test_fail_cutout_outside(fakedata):

with pytest.raises(RuntimeError, match='Could not create 2d cutout. The requested '
'cutout does not overlap with the original image'):
get_cutout(data, cc, wcs, size=50, write_file=False)
_get_cutout(data, cc, wcs, size=50, write_file=False)


def assert_same_coord(x, y, cutout, wcs):
Expand All @@ -251,7 +251,7 @@ def test_partial_cutout(makefake, asint, fill):

wcs = WCS(gwcs.to_fits_sip())
cc = coord.SkyCoord(29.999, 44.998, unit=u.degree)
cutout = get_cutout(data, cc, wcs, size=50, write_file=False, fill_value=fill)
cutout = _get_cutout(data, cc, wcs, size=50, write_file=False, fill_value=fill)
assert cutout.shape == (50, 50)
if asint:
assert -9999 in cutout.data
Expand All @@ -266,7 +266,7 @@ def test_bad_fill(makefake):
wcs = WCS(gwcs.to_fits_sip())
cc = coord.SkyCoord(29.999, 44.998, unit=u.degree)
with pytest.raises(ValueError, match='fill_value is inconsistent with the data type of the input array'):
get_cutout(data, cc, wcs, size=50, write_file=False)
_get_cutout(data, cc, wcs, size=50, write_file=False)


def test_cutout_raedge(makefake):
Expand All @@ -284,7 +284,7 @@ def test_cutout_raedge(makefake):
wcs = WCS(gg.to_fits_sip())

# get cutout
cutout = get_cutout(data, cc, wcs, size=100, write_file=False)
cutout = _get_cutout(data, cc, wcs, size=100, write_file=False)
assert_same_coord(5, 10, cutout, wcs)

# assert the RA cutout bounds are > 359 and < 0
Expand All @@ -299,7 +299,7 @@ def test_slice_gwcs(fakedata):
skycoord = gwcsobj(250, 250)
wcs = WCS(gwcsobj.to_fits_sip())

cutout = get_cutout(data, skycoord, wcs, size=50, write_file=False)
cutout = _get_cutout(data, skycoord, wcs, size=50, write_file=False)

sliced = _slice_gwcs(gwcsobj, cutout.slices_original)

Expand Down Expand Up @@ -329,6 +329,6 @@ def test_get_cloud_http(mock_s3fs):
http_uri = _get_cloud_http(s3_uri)

assert http_uri == HTTP_URI
mock_s3fs.assert_called_once_with(anon=True)
mock_s3fs.assert_called_once_with()
mock_fs.open.assert_called_once_with(s3_uri, 'rb')
mock_file.url.assert_called_once()
20 changes: 20 additions & 0 deletions docs/astrocut/file_formats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,26 @@ it contains the name of the file the cutout comes from.



ASDF Cutout Files
==================

ASDF files output by `asdf_cut` are a minimal tree structure that mirrors the format of the original Roman image file.

.. code-block:: python
asdf_cutout = {
"roman": {
"meta": {
"wcs" - the gwcs of the cutout
},
"data" - the cutout data
}
}
`wcs` is the original `gwcs` object from the input ASDF file that has been sliced into the shape of the cutout.



Cube Files
==========

Expand Down
65 changes: 58 additions & 7 deletions docs/astrocut/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ Three main areas of functionality are included:



FITS file image cutouts
FITS File Image Cutouts
=======================

These functions provide general purpose astronomical cutout functionality for FITS files.
There are two main cutout functions, `~astrocut.fits_cut` for creating cutout FITS files,
and `~astrocut.img_cut` for creating cutout JPG or PNG files. An image normalization
(`~astrocut.normalize_img`) function is also available.

Creating FITS cutouts
Creating FITS Cutouts
---------------------

The function `~astrocut.fits_cut` takes one or more FITS files and performs the same cutout
Expand Down Expand Up @@ -107,7 +107,7 @@ The cutout(s) can also be returned in memory as `~astropy.io.fits.HDUList` objec
1 CUTOUT 1 ImageHDU 97 (100, 100) float32
Creating image cutouts
Creating Image Cutouts
----------------------

The function `~astrocut.img_cut` takes one or more FITS files and performs the same cutout
Expand Down Expand Up @@ -179,7 +179,7 @@ the same Sector, camera, and CCD.
If you are creating a small number of cutouts, the TESSCut web service
may suit your needs: `mast.stsci.edu/tesscut <https://mast.stsci.edu/tesscut/>`_

Making image cubes
Making Image Cubes
------------------

.. important::
Expand Down Expand Up @@ -250,7 +250,7 @@ The output image cube file format is described `here <file_formats.html#cube-fil
2 1 BinTableHDU 302 144R x 147C [24A, J, J, J, J, J, J, D, 24A, J, 24A, 24A, J, J, D, 24A, 24A, 24A, J, D, 24A, D, D, D, D, 24A, 24A, D, D, D, D, D, 24A, D, D, D, D, J, D, D, D, D, D, D, D, D, D, D, D, D, J, J, D, J, J, J, J, J, J, J, J, J, J, D, J, J, J, J, J, J, D, J, J, J, J, J, J, D, J, J, J, J, J, J, D, J, J, J, J, J, J, J, J, 24A, D, J, 24A, 24A, D, D, D, D, D, D, D, D, J, J, D, D, D, D, D, D, J, J, D, D, D, D, D, D, D, D, D, D, D, D, 24A, J, 24A, 24A, J, J, D, 24A, 24A, J, J, D, D, D, D, J, 24A, 24A, 24A]
Making cutout target pixel files
Making Cutout Target Pixel Files
--------------------------------

To make a cutout, you must already have an image cube from which to create the cutout.
Expand Down Expand Up @@ -356,10 +356,61 @@ Note that multithreading is disabled by default.
Total time: 7.8 sec
ASDF File Cutouts
===================

The Nancy Grace Roman Space Telescope will store its data using the Advanced Scientific Data Format (ASDF). With the `asdf_cut` function, users can create cutouts of Roman mission products.

Creating ASDF Cutouts
----------------------

The function `asdf_cut` performs a cutout of an ASDF file and returns the result as either a FITS file or an ASDF file.
The format of the cutout is determined by the filename extension of the `output_file` parameter. In the below example, a cutout is written as a FITS file.
The cutout FITS file format is described `here <file_formats.html#fits-cutout-files>`__.

.. code-block:: python
>>> from astrocut import asdf_cut
>>> from astropy.coordinates import SkyCoord
>>> from astropy.io import fits
>>> input_file = "" # Path to local ASDF file or URI
>>> center_coord = SkyCoord("80.15189743 29.74561219", unit='deg')
>>> cutout_file = asdf_cut(input_file, center_coord.ra, center_coord.dec, cutout_size=200,
... output_file="roman-demo.fits") #doctest: +SKIP
>>> cutout_hdulist = fits.open(cutout_file) #doctest: +SKIP
>>> cutout_hdulist.info() #doctest: +SKIP
Filename: roman-demo.fits
No. Name Ver Type Cards Dimensions Format
0 PRIMARY 1 PrimaryHDU 25 (200, 200) float32
`asdf_cut` accepts S3 URIs to perform cutouts on ASDF files from the cloud.
In this example, a cutout is performed on a cloud file and written as an ASDF file. The cutout ASDF file format is described `here <file_formats.html#asdf-cutout-files>`__.

.. code-block:: python
>>> from astrocut import asdf_cut
>>> from astropy.coordinates import SkyCoord
>>> s3_uri = "s3://..." # Complete URI
>>> center_coord = SkyCoord("80.15189743 29.74561219", unit='deg')
>>> cutout_file = asdf_cut(s3_uri, center_coord.ra, center_coord.dec, cutout_size=200,
... output_file="roman-demo.asdf") #doctest: +SKIP
When requesting a cutout that is partially outside of image bounds, the `fill_value` parameter is used to preserve the cutout shape and fill outside pixels.



Additional Cutout Processing
============================

Path-based cutouts
Path-based Cutouts
------------------

The `~astrocut.center_on_path` function allows the user to take one or more Astrocut cutout
Expand Down Expand Up @@ -436,7 +487,7 @@ cutout location/size(s) necesary to cover the entire path.
2 APERTURE 1 ImageHDU 97 (2136, 2078) int32
Combining cutouts
Combining Cutouts
-----------------

The `~astrocut.CutoutsCombiner` class allows the user to take one or more Astrocut cutout
Expand Down

0 comments on commit 29ee109

Please sign in to comment.