You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I am using astropy.wcs and I think that NAXIS1, NAXIS2 are not easily accessible although they are used by WCS.calc_footprint(). See http://docs.astropy.org/en/stable/wcs/
Yes, I can access them via
NAXIS1= hdulist[ext].header['NAXIS1']
but it would seem generally useful to be able to also access them via the wcs package.
wcs.print_contents() does not seem to list NAXISn or axes although calc_footprint must be getting at them.
It would be convenient to be able to use something like:
print('NAXIS: ', WCS.wcs.naxis) WORKS
print('NAXES: ', WCS.wcs.naxes)
print('NAXIS1: ', WCS.wcs.naxis1)
print('NAXIS2: ', WCS.wcs.naxis2)
print('CRPIX: ', WCS.wcs.crpix) WORKS
print('CDELT: ', WCS.wcs.cdelt) WORKS
Just a sidenote, this has come up previously in other packages, e.g.: https://github.com/aplpy/aplpy/pull/284/files
I was sure it had another issue on astropy, but I can't find one, so I guess this is new! Anyway, I agree that a WCS-like object that contains image size info would be useful.
And to expand the discussion a little further, a possible option is to provide a WCS subclass that knows about the image shape, e.g.:
class WCSFrame(WCS):
def __init__(self,...):
super().__init__()
self.naxis1 = something
self.naxis2 = something
self.naxis3 = something
# or just an abstract shape
self.shape
which would be a light wrapper that would parse these keywords from a FITS header on instantiation and would provide the right sizes when sliced. We have a few use cases already: at least, the astropy one, but probably many others. For example, comparing WCSes, you may really care about the shape of the image also being the same
As @astrofrog mentioned astropy.wcs changed a couple of times from exposing to hiding the shape of the image ( #323) . There are good reasons to go either way. A slightly different approach is taken in GWCS, where a domain is a list of dictionaries, where each item corresponds to an axis:
In my opinion NAXIS1,2 should be removed completely from the WCS including calc_footprint which should not belong to WCS. I think calc_footprint is probably the only place in WCS where NAXIS is needed. While WCS is closely tied with an underlying image [and I would have preferred this approach], it appears that PyWCS and now astropy.wcs consider WCS to be an independent object that can live all by itself in some abstract WCS space except for need for NAXIS. I think now it is the time to cut the umbilical cord and let WCS live by itself.
If calc_footprint() is useful, it could be placed in wcsutil and have a form such as calc_footprint(wcs, image_data).
A domain of validity of transformations in a WCS is a nice thing but it is not the same as the dimension/shape of the image's data array which is a property of the image and not WCS. For this reason I think it would be better to get rid of NAXIS1,2. Users should do ny, nx = image_data.shape and not expect WCS to do this for them as in the current astropy.wcs paradigm image data and WCS are independent things.
@pllim Your visualization example is a perfect example when that shows that foot prints should be calculated from image AND WCS and not from WCS alone.
What about my proposal to move calc_footprint to wcsutils and have a function like this: calc_image_footprint(wcs, image_data)?
@mcara I very much want access to an object that knows about the image shape and its WCS. It is important that the shape be an attribute of the same object, and it is important to me that I do not have to have the image (or a representation of the image) in memory. But I'm happy enough if the object that has this information is not the astropy.wcs.WCS object we have now, but some sibling class. Any thoughts on taking that approach?
Again, this is a personal opinion, but I think that fundamentally WCS is tied to an image (and its content - not only its shape). Therefore, your approach is not much different from what is it now. In fact, if we still want to mix WCS with an image, instead of introducing a new class I would rather see ._naxis1 being made public and have it printed to the header in the existing WCS.
Nowhere in the WCS standard (first one or two papers on WCS) could I find anything that would indicate that a WCS needs to know naxis1,2 to be properly defined. Thus, it is strange that anyone needs this info from a WCS and not from the image associated with that WCS.
@keflavich However, I can see your approach perfectly valid if you do it in your own code. That is, if my code was relying on WCS having naxis1,2 then I would have probably subclassed astropy.wcs.WCS just as you did in your example.
I disagree with the WCS being associated with an image's content. My most common use case for WCS is reprojecting one image into the exact pixel space of another, which means that the WCS is independent from the content.
But I also want to be very clear that I'm not making a semantic argument - I don't care if WCS is the object that knows about naxis + other WCS things, as long as there is some object that has both. I don't want to re-implement this multiple times, as would be required if I always subclass.
In the WCSFrame approach I suggested, one of the most important methods is to_header, which WCS also has, but WCSFrame.to_header() would create a fully-valid FITS header that specifies an image's shape too. This is what I need to programatically create a FITS header to reproject multiple data sets into. But this sort of object could also be passed directly to reproject instead of a fits.Header.
keflavich wrote: "My most common use case for WCS is reprojecting one image..." That is my point: you are re-projecting an image (not a WCS) and WCS is a tool to achieve this re-projection. Currently WCS provides you with everything you need to do this re-projection of an image even without NAXIS1,2 stored in the WCS. Size of the underlying image is not necessary to do everything you need otherwise it would have been part of the WCS standard.
It might be useful if I specified ONE of my use cases.
Often I want to determine the location of the boundaries of images to determine the image that an object lies on. The WCS header information for images or image extensions is stored in a metadata table in a database or in a fits catalogue.
It is therefore useful to be able to create a WCS header object using the header keywords. NAXIS1 and NAXIS2 are essential for this operation.
@mcara - what @keflavich meant is that the target WCS needs to have a shape/size - that is, currently in reproject if you specify a target WCS, you also need to specify a target shape, so he just wants a convenience class that contains WCS + shape (equivalent to a FITS header)
@astrofrog@keflavich: "...that is, currently in reproject if you specify a target WCS, you also need to specify a target shape..." I did not know what @keflavich is working on so I may have misunderstood some parts.
However, unless we really tie together an image with its WCS, I would prefer to have them completely "independent" (at least formally). Specifically, let astropy.wcs be a "World Coordinate System" - that is, a set of coordinate transformations as it was intended to be. The image size to which this transformation is going to be applied has nothing to do with the coordinate transformations. Whether the image is 1x1 or 100,000x100,000 - it's all the same (from the coordinate transformation point of view). At the very least, conceptually, image size has nothing to do with coordinate transformation.
I understand that having naxis1,2 stored with the WCS may be convenient in some cases but I do not think it is necessary. A long time ago I also was relying on WCS having naxis1,2 in my code. So I was pretty upset when naxis was made private. However, I have moved on and I do not even care what naxis1,2 is in a WCS object.
The problem with having image properties mixed with coordinate transformations is that things become a mess and lead to issues such as #4498 or someone could cut the image, or rescale it, etc. and not update naxis1,2 in the WCS. It is my belief that things are much cleaner when we conceptually separate image data/properties from coordinate transformations (unless we tightly unify these concepts in a single class ImageOnTheSky).
Regarding @keflavich idea of subclassing WCS (class WCSFrame(WCS))... I think it would be fine to do this in your project and require users to explicitly "tie" a WCS with image properties (shape). I just would not use "Frame" in the name as "frame" hints (to me) to a "coordinate frame" (detector, sky, etc.) I have done something similar to deal with limitations of old pyregion not using distortions defined in WCS - see class _AuxSTWCS here: https://github.com/spacetelescope/drizzlepac/blob/master/lib/drizzlepac/mapreg.py
NAXIS1 and NAXIS2 is required to create a FITS header containing data and so NAXIS1,2 should indicate the shape of data. If you want to save a WCS to a fits file without data - you can still do it as you will see soon in the next part.
Part 2.
Let's go through the example in the "Building a WCS structure programmatically" link (and let's also play with that WCS's footprint - just for fun).
that is, the image has 0 axes while the WCS has two axes. WCS does not need to know how "large" is each axis. In addition, notice that the header created with fits.PrimaryHDU(header=header) does not contain NAXIS1 or NAXIS2 !!! In order to get "reasonable" NAXIS1,2 values, you will need to provide "data" to fits.PrimaryHDU.
Finally, let's see what calc_footprints gives us with the above WCS:
Just to add to my previous post. In your IDL script you are using information about BOTH coordinate transformation (WCS) AND image properties (in particular, image size) to produce a header that has NAXIS1,2. However, this header may be wrong if you do not save image data to the FITS file. The way it is currently done in astropy you do not need to worry about this if you do the things using, e.g., fits.PrimaryHDU():
just to be clear; for this use case; i do not write an image.
I am building a WCS from headers keywords that are within a table and using WCS to determine the footprint of a image. I take each RA, Dec that I am working with;
(i) do a radial pairwise search on the image metadata using an inscribed radius defined by the image corners.
(ii) then use WCS to compute the pixel a position has on an image; test against the image shape as defined by NAXIS1, NAXIS2
It would convenient to use the use the WCS object as a container that includes he NAXIS1, 2 since I can then pass the WCS and
NAXIS1, 2 info as single argument.
I understand the rationale for separation of image shape from WCS transformation and do not have anything significant to contribute.
I welcome very much the very comprensive support for WCS in astropy as someone who deals with imaging data from a multitude of imaging surveys and wanted to move away from IDL and bespoke code.
I also understand that the concept of image WCS and image properties are different concepts and also the risk of creating images with the
wrong headers when one trims or cutouts out a subset.
Hopefully my use case has some merit in the planning of features. I am not blocked at the moment.
Just to add to my previous post. In your IDL script you are using information about BOTH coordinate transformation (WCS) AND image properties (in particular, image size) to produce a header that has NAXIS1,2. However, this header may be wrong if you do not save image data to the FITS file. The way it is currently done in astropy you do not need to worry about this if you do the things using, e.g., fits.PrimaryHDU():
d = numpy.zeros((15,15
))
hdu = fits.PrimaryHDU(header=header, data=
d)
hdu.header
SIMPLE = T / conforms to FITS
standard
BITPIX = -64 / array data type
NAXIS = 2 /
number of array dimensions
NAXIS1 = 15
NAXIS2 = 15
WCSAXES = 2 /
Number of coordinate axes
CRPIX1 = -234.75 /
Pixel coordinate of reference point
CRPIX2 = 8.3393 /
Pixel coordinate of reference point
CDELT1 = -0.066667 /
[deg] Coordinate increment at reference point
CDELT2 = 0.066667 /
[deg] Coordinate increment at reference point
CUNIT1 = 'deg' / Units of coordinate increment and
value
CUNIT2 = 'deg' / Units of coordinate increment and
value
CTYPE1 = 'RA---AIR' / Right ascension, Airy's zenithal projection
CTYPE2 = 'DEC--AIR' / Declination, Airy's zenithal projection
CRVAL1 = 0.0 /
[deg] Coordinate value at reference point
CRVAL2 = -90.0 /
[deg] Coordinate value at reference point
PV2_1 = 45.0 / AIR
projection parameter
LONPOLE = 180.0 /
[deg] Native longitude of celestial pole
LATPOLE = -90.0 /
[deg] Native latitude of celestial pole
RADESYS = 'ICRS' / Equatorial coordinate system
Notice that PrimaryHDU header now has NAXIS=2 and NAXIS1,2=15. So, it is PrimaryHDU that takes care of putting correct NAXIS1,2 based on image data.
—
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
@mcara - I think that there are genuine use cases where one wants to represent WCS + an image extent without wanting to set specific pixel data or create a FITS header (which is FITS-specific). So to move this discussion forward, I'd like to suggest we start thinking about @keflavich's suggestion to make a WCS subclass. I understand (and agree that) WCSFrame may not be a good name, so does anyone have any other ideas of what this could be? We could then remove entirely any concept of size from the WCS class (including in private attributes).
I believe this has actually been addressed as pixel_shape and array_shape are exposed and optional in APE 14:
In [1]: fromastropy.wcsimportWCSIn [2]: wcs=WCS('filename.fits')
In [3]: wcs.array_shapeOut[3]: (2000, 1000)
In [4]: wcs.pixel_shapeOut[4]: (1000, 2000)
Activity
astrofrog commentedon Mar 5, 2016
Just a quick note: NAXIS1 and NAXIS2 were made private in #323
astrofrog commentedon Mar 5, 2016
cc @nden
keflavich commentedon Mar 27, 2016
Just a sidenote, this has come up previously in other packages, e.g.: https://github.com/aplpy/aplpy/pull/284/files
I was sure it had another issue on astropy, but I can't find one, so I guess this is new! Anyway, I agree that a WCS-like object that contains image size info would be useful.
keflavich commentedon Mar 28, 2016
And to expand the discussion a little further, a possible option is to provide a WCS subclass that knows about the image shape, e.g.:
which would be a light wrapper that would parse these keywords from a FITS header on instantiation and would provide the right sizes when sliced. We have a few use cases already: at least, the astropy one, but probably many others. For example, comparing WCSes, you may really care about the shape of the image also being the same
nden commentedon Apr 19, 2016
As @astrofrog mentioned
astropy.wcschanged a couple of times from exposing to hiding the shape of the image ( #323) . There are good reasons to go either way. A slightly different approach is taken in GWCS, where adomainis a list of dictionaries, where each item corresponds to an axis:[{'lower': float, 'upper': float, 'includes_lower': bool, 'includes_upper': bool, 'step': float}]Perhaps something like this can be useful in
astropy.wcsmcara commentedon Apr 19, 2016
In my opinion
NAXIS1,2should be removed completely from the WCS includingcalc_footprintwhich should not belong to WCS. I thinkcalc_footprintis probably the only place in WCS whereNAXISis needed. While WCS is closely tied with an underlying image [and I would have preferred this approach], it appears thatPyWCSand nowastropy.wcsconsider WCS to be an independent object that can live all by itself in some abstract WCS space except for need forNAXIS. I think now it is the time to cut the umbilical cord and let WCS live by itself.If
calc_footprint()is useful, it could be placed inwcsutiland have a form such ascalc_footprint(wcs, image_data).mcara commentedon Apr 19, 2016
A domain of validity of transformations in a WCS is a nice thing but it is not the same as the dimension/shape of the image's data array which is a property of the image and not WCS. For this reason I think it would be better to get rid of
NAXIS1,2. Users should dony, nx = image_data.shapeand not expect WCS to do this for them as in the currentastropy.wcsparadigm image data and WCS are independent things.pllim commentedon Apr 19, 2016
👎 to removing
calc_footprintbecause I use it.mcara commentedon Apr 19, 2016
@pllim stop using it! It's unhealthy :)
pllim commentedon Apr 19, 2016
I need it to calculate footprint for visualization. Unless you have the equivalent functionality to replace this, I am opposed to removing it.
mcara commentedon Apr 19, 2016
@pllim Your visualization example is a perfect example when that shows that foot prints should be calculated from image AND WCS and not from WCS alone.
What about my proposal to move
calc_footprinttowcsutilsand have a function like this:calc_image_footprint(wcs, image_data)?keflavich commentedon Apr 19, 2016
@mcara I very much want access to an object that knows about the image shape and its WCS. It is important that the shape be an attribute of the same object, and it is important to me that I do not have to have the image (or a representation of the image) in memory. But I'm happy enough if the object that has this information is not the
astropy.wcs.WCSobject we have now, but some sibling class. Any thoughts on taking that approach?mcara commentedon Apr 19, 2016
Again, this is a personal opinion, but I think that fundamentally WCS is tied to an image (and its content - not only its shape). Therefore, your approach is not much different from what is it now. In fact, if we still want to mix WCS with an image, instead of introducing a new class I would rather see
._naxis1being made public and have it printed to the header in the existing WCS.Nowhere in the WCS standard (first one or two papers on WCS) could I find anything that would indicate that a WCS needs to know
naxis1,2to be properly defined. Thus, it is strange that anyone needs this info from a WCS and not from the image associated with that WCS.mcara commentedon Apr 19, 2016
@keflavich However, I can see your approach perfectly valid if you do it in your own code. That is, if my code was relying on WCS having
naxis1,2then I would have probably subclassedastropy.wcs.WCSjust as you did in your example.keflavich commentedon Apr 19, 2016
I disagree with the WCS being associated with an image's content. My most common use case for WCS is reprojecting one image into the exact pixel space of another, which means that the WCS is independent from the content.
But I also want to be very clear that I'm not making a semantic argument - I don't care if WCS is the object that knows about naxis + other WCS things, as long as there is some object that has both. I don't want to re-implement this multiple times, as would be required if I always subclass.
In the
WCSFrameapproach I suggested, one of the most important methods isto_header, whichWCSalso has, butWCSFrame.to_header()would create a fully-valid FITS header that specifies an image's shape too. This is what I need to programatically create a FITS header to reproject multiple data sets into. But this sort of object could also be passed directly to reproject instead of afits.Header.mcara commentedon Apr 19, 2016
keflavich wrote: "My most common use case for WCS is reprojecting one image..." That is my point: you are re-projecting an image (not a WCS) and WCS is a tool to achieve this re-projection. Currently WCS provides you with everything you need to do this re-projection of an image even without
NAXIS1,2stored in the WCS. Size of the underlying image is not necessary to do everything you need otherwise it would have been part of the WCS standard.richardgmcmahon commentedon Apr 19, 2016
It might be useful if I specified ONE of my use cases.
Often I want to determine the location of the boundaries of images to determine the image that an object lies on. The WCS header information for images or image extensions is stored in a metadata table in a database or in a fits catalogue.
It is therefore useful to be able to create a WCS header object using the header keywords. NAXIS1 and NAXIS2 are essential for this operation.
This similar to "Building a WCS structure programmatically": http://docs.astropy.org/en/stable/wcs/
I am coming for IDL where I could do this with:
http://idlastro.gsfc.nasa.gov/ftp/pro/astrom/make_astr.pro
astrofrog commentedon Apr 19, 2016
@mcara - what @keflavich meant is that the target WCS needs to have a shape/size - that is, currently in reproject if you specify a target WCS, you also need to specify a target shape, so he just wants a convenience class that contains WCS + shape (equivalent to a FITS header)
mcara commentedon Apr 19, 2016
@astrofrog @keflavich: "...that is, currently in reproject if you specify a target WCS, you also need to specify a target shape..." I did not know what @keflavich is working on so I may have misunderstood some parts.
However, unless we really tie together an image with its WCS, I would prefer to have them completely "independent" (at least formally). Specifically, let
astropy.wcsbe a "World Coordinate System" - that is, a set of coordinate transformations as it was intended to be. The image size to which this transformation is going to be applied has nothing to do with the coordinate transformations. Whether the image is 1x1 or 100,000x100,000 - it's all the same (from the coordinate transformation point of view). At the very least, conceptually, image size has nothing to do with coordinate transformation.I understand that having
naxis1,2stored with the WCS may be convenient in some cases but I do not think it is necessary. A long time ago I also was relying on WCS havingnaxis1,2in my code. So I was pretty upset when naxis was made private. However, I have moved on and I do not even care what naxis1,2 is in a WCS object.The problem with having image properties mixed with coordinate transformations is that things become a mess and lead to issues such as #4498 or someone could cut the image, or rescale it, etc. and not update
naxis1,2in the WCS. It is my belief that things are much cleaner when we conceptually separate image data/properties from coordinate transformations (unless we tightly unify these concepts in a single classImageOnTheSky).mcara commentedon Apr 19, 2016
Regarding @keflavich idea of subclassing WCS (
class WCSFrame(WCS))... I think it would be fine to do this in your project and require users to explicitly "tie" a WCS with image properties (shape). I just would not use "Frame" in the name as "frame" hints (to me) to a "coordinate frame" (detector, sky, etc.) I have done something similar to deal with limitations of oldpyregionnot using distortions defined in WCS - see class_AuxSTWCShere: https://github.com/spacetelescope/drizzlepac/blob/master/lib/drizzlepac/mapreg.pymcara commentedon Apr 19, 2016
@richardgmcmahon Let me address some issues mentioned by you in a previous post. You said:
Part 1.
NAXIS1andNAXIS2is required to create a FITS header containing data and soNAXIS1,2should indicate the shape of data. If you want to save a WCS to a fits file without data - you can still do it as you will see soon in the next part.Part 2.
Let's go through the example in the "Building a WCS structure programmatically" link (and let's also play with that WCS's footprint - just for fun).
Please notice that:
that is, the image has 0 axes while the WCS has two axes. WCS does not need to know how "large" is each axis. In addition, notice that the header created with
fits.PrimaryHDU(header=header)does not containNAXIS1orNAXIS2!!! In order to get "reasonable"NAXIS1,2values, you will need to provide "data" tofits.PrimaryHDU.Finally, let's see what
calc_footprintsgives us with the above WCS:So, the footprint of this image is 1x1 pixel large (should have been 0, in my opinion) - probably not even close to the size of your real image.
mcara commentedon Apr 19, 2016
Just to add to my previous post. In your IDL script you are using information about BOTH coordinate transformation (WCS) AND image properties (in particular, image size) to produce a header that has
NAXIS1,2. However, this header may be wrong if you do not save image data to the FITS file. The way it is currently done inastropyyou do not need to worry about this if you do the things using, e.g.,fits.PrimaryHDU():Notice that PrimaryHDU header now has
NAXIS=2andNAXIS1,2=15. So, it isPrimaryHDUthat takes care of putting correctNAXIS1,2based on image data.richardgmcmahon commentedon Apr 20, 2016
Thanks for the clarification;
just to be clear; for this use case; i do not write an image.
I am building a WCS from headers keywords that are within a table and using WCS to determine the footprint of a image. I take each RA, Dec that I am working with;
(i) do a radial pairwise search on the image metadata using an inscribed radius defined by the image corners.
(ii) then use WCS to compute the pixel a position has on an image; test against the image shape as defined by NAXIS1, NAXIS2
It would convenient to use the use the WCS object as a container that includes he NAXIS1, 2 since I can then pass the WCS and
NAXIS1, 2 info as single argument.
I understand the rationale for separation of image shape from WCS transformation and do not have anything significant to contribute.
I welcome very much the very comprensive support for WCS in astropy as someone who deals with imaging data from a multitude of imaging surveys and wanted to move away from IDL and bespoke code.
I also understand that the concept of image WCS and image properties are different concepts and also the risk of creating images with the
wrong headers when one trims or cutouts out a subset.
Hopefully my use case has some merit in the planning of features. I am not blocked at the moment.
regards Richard
astrofrog commentedon Apr 20, 2016
@mcara - I think that there are genuine use cases where one wants to represent WCS + an image extent without wanting to set specific pixel data or create a FITS header (which is FITS-specific). So to move this discussion forward, I'd like to suggest we start thinking about @keflavich's suggestion to make a
WCSsubclass. I understand (and agree that)WCSFramemay not be a good name, so does anyone have any other ideas of what this could be? We could then remove entirely any concept of size from theWCSclass (including in private attributes).astrofrog commentedon Sep 23, 2026
I believe this has actually been addressed as
pixel_shapeandarray_shapeare exposed and optional in APE 14: