Construct FITS-WCS from WCS Wrappers
Describe the feature
ndcube only requires that its WCS is APE-14 compliant and is agnostic to the underlying WCS implementation. However, many users want to write their NDCube out to FITS. However, even if an NDCube starts with a FITS-WCS, certain operations will cause the WCS to be wrapped in an APE-14-compliant wrapper that cannot itself be read back to FITS. However, for many of these wrappers, it should be possible to reconstruct a FITS-WCS by combining the wrapper information with the original underlying FITS-WCS. This will enable users to write their modified NDCube out to FITS.
Currently, there are only two NDCube operations that will produce a WCS wrapper:
- slicing/crop (
SlicedLowLevelWCS) - rebin (
ResampledWCS)
Aside
NDCube.reproject_to will also replace the WCS. However, the WCS is provided by the user and so the user can ensure that the new WCS is FITS-WCS.
Proposed solution
This issue proposes a set of functions to live in a new module in the ndcube.wcs sunpackage. These can be applied to the NDCube.wcs by the user to produce a new FITS-WCS. The user can then use this new FITS WCS to write their NDCube out to a FITS file.
The functions should be:
unravel_wcs_wrappers_to_fitswcs- Takes a WCS and drills down the base-level WCS implementation and confirms it's FITS-WCS. Otherwise, raise a
TypeError - Determines what wrapper type(s) is/are used. Wrappers can be wrapped around wrappers, so there could be multiple layers
- Identify the lowest level wrapper and build a new FITS-WCS by calling one of the functions below to edit the underlying FITS-WCS based on the wrapper information.
- Repeat for each wrapper in the chain until a FITS-WCS is produced that describes the current
NDCube. - As each wrapper needs to be special-cased, this function should raise a error
TypeErroris an unsupported wrapper type if found.
- Takes a WCS and drills down the base-level WCS implementation and confirms it's FITS-WCS. Otherwise, raise a
slice_fitswcs- Takes a
tupleof slice items/intin pixel order and a FITS-WCS and alters theCRPIXvalues of the FITS-WCS. - If an
intis given, then that axis is dropped, and so all FITS-keywords for that axis (e.g.CRPIX1,CRVAL1, etc.) must be deleted, and the names of keywords for following axes have their numbers decremented.
- Takes a
- `resample_fitswcs
- Takes a FITS-WCS and a scale and offset input for each axis and edit the
CDELTandCRPIXkeywords as necessary.
- Takes a FITS-WCS and a scale and offset input for each axis and edit the
- Future functions:
- This infrastructure and be appended to in the future for other operations by writing a new function and adding a check for the corresponding wrapper type in
unravel_wcs_wrappers_to_fitswcs
- This infrastructure and be appended to in the future for other operations by writing a new function and adding a check for the corresponding wrapper type in
ping @jmason86 @Cadair
I think these 3 functions would solve your problem @jmason86 and should not be an unreasonable amount of work. Your SunCET pipeline could then use NDCube natively and be significantly simplified. And you could write the end product our to FITS in only a couple of lines of code.
While I think writing the convert-back-to-fits functionality is good and should be done, I think there's a better way of doing this holistically.
Ideally, when constructing a low level wrapper, the wrapper class should see if it can modify the input WCS rather than return an instance of itself. Ideally (I think), this would be dispatched out to the input WCS (i.e WCS._resample_wcs) which could return NotImplemented if the operation can not be done on that particular WCS.
I think this would be a good addition to the APE 14 API to extend it into analysis operations.