Skip to content

wcs.naxis should be determined from NAXIS, not presence of C???? keywords - #323

Merged
mdboom merged 5 commits into
astropy:masterfrom
mdboom:wcs/naxis
Aug 18, 2012
Merged

mdboom merged 5 commits into
astropy:masterfrom
mdboom:wcs/naxis

Conversation

@mdboom

@mdboom mdboom commented Aug 2, 2012

Copy link
Copy Markdown
Contributor

If I parse the following header:

SIMPLE  =                    T / conforms to FITS standard
BITPIX  =                  -64 / array data type
NAXIS   =                    2 / number of array dimensions
NAXIS1  =                  600
NAXIS2  =                  600
CTYPE1  = 'RA---SIN'
CRVAL1  =   1.000000000000E+02
CDELT1  =  -1.000000000000E-04
CRPIX1  =   3.010000000000E+02
CUNIT1  = 'deg     '
CTYPE2  = 'DEC--SIN'
CRVAL2  =  -2.000000000000E+00
CDELT2  =   1.000000000000E-04
CRPIX2  =   3.010000000000E+02
CUNIT2  = 'deg     '
CTYPE3  = 'VRAD    '
CRVAL3  =  -1.000000000000E+04
CDELT3  =   1.000000000000E+04
CRPIX3  =   1.000000000000E+00
CUNIT3  = 'm/s     '
CTYPE4  = 'STOKES  '
CRVAL4  =   1.000000000000E+00
CDELT4  =   1.000000000000E+00
CRPIX4  =   1.000000000000E+00
CUNIT4  = '        '
END

with:

In [1]: from astropy.io import fits

In [2]: header = fits.Header.fromtextfile('header.hdr')

In [3]: from astropy import wcs

In [4]: w = wcs.WCS(header)

the naxis attribute has the wrong value:

In [5]: print w.naxis
4

as I would imagine NAXIS takes precedence over anything else. If I try and access naxis3 for example, I have issues:

In [6]: print w.naxis3
ERROR: AttributeError: 'WCS' object has no attribute 'naxis3' [IPython.core.interactiveshell]
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/Volumes/Raptor/Dropbox/code/development/APLpy/APLpy/<ipython-input-6-8c58730432b6> in <module>()
----> 1 print w.naxis3

AttributeError: 'WCS' object has no attribute 'naxis3'

as expected.

@ghost ghost assigned mdboom Jul 26, 2012
@embray

embray commented Jul 27, 2012

Copy link
Copy Markdown
Member

Well it's not Header.fromtextfile that's the problem. So I don't know...

@mdboom

mdboom commented Jul 29, 2012

Copy link
Copy Markdown
Contributor

This behavior is inherited from wcslib. See here:

https://github.com/astropy/astropy/blob/master/astropy/wcs/src/wcslib/C/wcshdr.h#L381

Admittedly, we should probably copy these notes to the Python documentation -- this issue has come up before for pywcs -- it's a little confusing but apparently how the WCS standard is supposed to work.

@mdboom

mdboom commented Jul 29, 2012

Copy link
Copy Markdown
Contributor

If I can paraphrase the reason I was told for this behavior:

It's possible that a WCS may want to specify extra dimensions while still having fewer dimensions in the actual data itself (which of course uses NAXIS).

Also, it is possible to limit the number of dimensions by passing naxis=2 to the constructor.

@astrofrog

Copy link
Copy Markdown
Member Author

But for those kinds of datasets, why not have NAXIS=4 and set NAXIS3 and NAXIS4 to 1?

I guess what confused me was that regardless of this behavior, I would have thought wcs.naxis should be equal to NAXIS, and I would also expect the default behavior to be to accept NAXIS values for transformations. In the example where I ran into an issue, the 3rd and 4th dimensions were just residuals from a slicing algorithm which reduced NAXIS but forgot to remove the associated WCS keywords.

Wouldn't a more sensible behavior be to respect NAXIS by default, and allow the user to override it with WCS(..., naxis=...) which would cause the missing NAXIS* keywords to be set to 1 and the further WCS keywords to be read (with an exception being raised if they are missing). This would seem more natural to me. If further keywords are present compared to NAXIS, one could even raise an INFO message to let the user know they have the option to force naxis to a higher value.

@mdboom

mdboom commented Jul 31, 2012

Copy link
Copy Markdown
Contributor

There is justification for this behavior in Paper I, section 2.2.

http://www.atnf.csiro.au/people/mcalabre/WCS/wcs.pdf

(I suggest reading that section in its entirety, but it essentially amounts to "the addition of degenerate axes to otherwise n-dimensional data may mean some software will reject it unnecessarily").

None of this precludes what you're suggesting, but I'm wary to deviate from the behavior of wcslib unless there's something that can't be done any other way. In this particular case, one can pass an explicit naxis kwarg to the constructor to force a requested number of axes. However, there are some things we can do to make this easier on the user. I propose:

  1. Adding a warning when NAXIS < naxis

  2. Changing wcs.naxis to be equal to the NAXIS FITS keyword is a bad idea. It should match the number of axes in the transformation, not in the data. And this is a class for WCS transformations, after all. We have io.fits/pyfits to handle the dimensions of the data.

  3. naxis1 and naxis2 should probably be made private (through some gradual deprecation perhaps, since that's backward-incompatible). They are used pretty much exclusively by the footprint calculation which is 2D only. Having them available here just causes confusion, and they really should be obtained directly from the dimensions of the data array in the pyfits HDU object. I could also be persuaded here that we define naxisX for all X <= wcs.naxis, and the missing values are 1. That would at least provide a consistent interface to get dimensions even when naxis > NAXIS and wouldn't force the use of degenerate axes in the FITS file itself.

EDIT: Changed != to < in proposal #1.

@mdboom

mdboom commented Aug 2, 2012

Copy link
Copy Markdown
Contributor

I've put my proposals into code here so you can play with it.

@astrofrog

Copy link
Copy Markdown
Member Author

Sorry for the delay, I will try this out today.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are the changes in this file needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes. This header was actually only working by "accident" -- the wcslib parser is fuzzy about FITS headers with carriage returns. It doesn't complain -- it reads some cards that don't overlap an 80 character boundary and silently rejects others. I didn't notice this problem with this header until, in this PR, the header needed to be re-parsed by io.fits in order to read the NAXIS card.

@astrofrog

Copy link
Copy Markdown
Member Author

I like the addition of the warning and that naxis keywords default to 1. Just a couple of comments:

  • why the changes in nonstandard_units.hdr?
  • I also do understand now why naxis? should not really be public attributes, in the sense that the WCS transformation doesn't depend on that. In fact, one could even remove it completely by making the footprint calculator ask for a given header or dimensions (e.g. I might want to calculate the footprint for different sized images for the same WCS transformation). In any case, I'd be fine with having the naxis? attributes be deprecated if that's what you think is best.

@mdboom

mdboom commented Aug 14, 2012

Copy link
Copy Markdown
Contributor

I have updated this to WCS.naxis1 and WCS.naxis2 are deprecated, negating the need to set naxisN to 1 etc.

@iguananaut: I added a deprecate_attribute function that piggy backs on the deprecate decorator. It's of course possible to do this by turning the attribute into a property and deprecating the property's methods, but this turns all of that into a one liner in the case where the property doesn't need to do anything special. You might want to have a look as the original @deprecate author.

@astrofrog

Copy link
Copy Markdown
Member Author

Ok, this looks good and consistent to me, so feel free to merge!

mdboom added a commit that referenced this pull request Aug 18, 2012
wcs.naxis should be determined from NAXIS, not presence of C???? keywords
@mdboom
mdboom merged commit e12c914 into astropy:master Aug 18, 2012
@astrofrog

Copy link
Copy Markdown
Member Author

@mdboom

mdboom commented Aug 18, 2012

Copy link
Copy Markdown
Contributor

Thanks. I think this is fixed in master now.

keflavich pushed a commit to keflavich/astropy that referenced this pull request Oct 9, 2013
keflavich pushed a commit to keflavich/astropy that referenced this pull request Oct 9, 2013
wcs.naxis should be determined from NAXIS, not presence of C???? keywords
@mdboom
mdboom deleted the wcs/naxis branch May 22, 2014 00:00
astrofrog added a commit to astrofrog/astropy that referenced this pull request Jun 12, 2019
Updating bundled sphinx-automodapi to v0.6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants