Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update docs 0.11 #118

Merged
merged 8 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change was to resolve an issue in RSP. Though not directly related to documentation, I thought I would include it in this PR since it's a small fix.


# 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
Loading