World Coordinate System¶
- class fitsy.Wcs(header, alt=' ')¶
Bases:
objectWorld Coordinate System for an HDU.
Constructed via
FitsFile.wcs(),ImageHdu.wcs(), or directly from a header. Supports celestial, spectral, time, phase and generic linear axes. Recognizes the SIP, TPV, TNX/ZPX,-TABand DSS distortion conventions.Examples
>>> with fitsy.open("image.fits") as f: ... wcs = f[0].wcs() ... kinds = wcs.axis_kinds() ... world = wcs.pixel_to_world([512.0, 512.0]) ... ra = world[kinds.index("longitude")]
- axis_kinds()¶
Kind of coordinate each axis carries, in axis order.
Use this to find an axis by meaning rather than by position.
pixel_to_world()returns one value per axis in the same order, so entryihere names valueithere.- Returns:
list of str – One entry per axis, each one of
'longitude','latitude','spectral','time','phase','stokes'or'linear'.
Notes
The kind comes from the type half of
CTYPEia, so an axis driven by a-TABlookup still reports its coordinate type.is_tabular()reports the lookup itself.Examples
Find the spectral axis of a cube and read its world value:
>>> with fitsy.open("cube.fits") as f: ... wcs = f[0].wcs() ... kinds = wcs.axis_kinds() ... world = wcs.pixel_to_world([31.0, 23.0, 4.0]) ... freq = world[kinds.index("spectral")]
- celestial_axes()¶
Indices of the celestial axes.
- Returns:
tuple of int or None –
(lon_axis, lat_axis)(zero-based), orNoneif no celestial pair is declared.
- ctype¶
Per-axis
CTYPEstrings.
- cunit¶
Per-axis
CUNITstrings. Empty for an axis whose header carries noCUNITcard; that axis then uses the default unit for its axis type.
- footprint()¶
World coordinates of the image’s corner pixels.
Corners are pixel centers, not the outer edge of the grid. For the outer edge, call
pixel_to_world()with-0.5andn - 0.5.Corners come back in Gray-code order, so consecutive corners differ on one axis alone. A two-axis image therefore yields
(0, 0),(nx-1, 0),(nx-1, ny-1),(0, ny-1), which walks the image counter-clockwise in pixel space and closes the ring.- Returns:
numpy.ndarray – Shape
(2**k, naxis).kis the number of axespixel_shapecovers, which isnaxisfor a normal image. A two-axis image gives(4, 2)of(ra, dec)in degrees. A corner the WCS cannot transform comes back asnan. See the notes below.- Raises:
FitsError – In three cases: -
pixel_shapeisNone. A fitted WCS has no image to take corners from. - An axispixel_shapecovers has length zero. - The WCS has more than 16 axes.
Notes
This reports corners, not an axis-aligned bounding box. A rotated image has corners outside the box its own minimum and maximum describe, and an
RAaxis that crosses zero makes such a box meaningless.Corners go through
pixel_to_world()in its batch form. A corner outside the projection’s domain therefore fills its row withnaninstead of raising. A wide-fieldSINorAZPimage can put every corner outside that domain. The whole array is thennan. Test the result withnumpy.isfinite. Pass one corner topixel_to_world()to read the reason it failed.WCSAXESmay exceedNAXIS. A coordinate axis past the end ofpixel_shapethen has no length to take a corner from. That axis holds its reference pixel for every corner. The corner count follows the image, and every corner still carries a full world vector.
- is_celestial¶
True when the WCS has a celestial axis pair.
- is_tabular(axis)¶
Whether an axis takes its coordinate from a
-TABlookup.- Parameters:
axis (int) – Zero-based axis index.
- Returns:
bool –
Truewhen axisaxisis tabular.Falsefor any other axis, and for an index this WCS does not have.
Notes
This is a property of the algorithm, not of the coordinate, so it is independent of
axis_kinds(). A tabular axis needs its binary table loaded before it can transform.fitsy.FitsFile.wcs()andfitsy.ImageHdu.wcs()load it.fitsy.Wcs(header)cannot, because a header alone does not reach the table.
- naxis¶
Number of WCS axes. May exceed the header’s
NAXISwhenWCSAXESasets a higher axis count.
- pixel_scale_at(px, py, origin=0)¶
Local pixel scale at
(px, py).- Parameters:
- Returns:
tuple of float – Pixel scale in arcseconds per pixel along the two celestial axes.
- Raises:
FitsError – If the WCS has no celestial axis pair, if it has unresolved
-TABaxes, or if(px, py)or an adjacent pixel used for the finite difference falls outside the projection’s valid domain.
Notes
fitsy measures this by finite difference on the sphere, so the result includes projection distortion and any local skew. It is a great-circle distance, always positive, not the signed
CDELTvalue. An image with flipped RA still reports a positive scale.
- pixel_shape¶
Size of the image this WCS came from, in FITS axis order (
NAXIS1first), orNonewhen unknown.This is a snapshot of the
NAXISncards taken when the WCS was parsed. It is not part of the coordinate description: no transform reads it, and no transform checks a pixel coordinate against it. It isNonefor a WCS fromfit_wcs(), which has no image, or for a header withoutNAXISncards. A cropped or rebinned image leaves this value stale.
- pixel_to_world(pix, origin=0)¶
Forward transform pixel coordinates to world coordinates.
Accepts one point or many. A length-
naxissequence is one point and returns a list. An(N, naxis)array isNpoints and returns an(N, naxis)array.- Parameters:
pix (array-like) – Shape
(naxis,)for a single point, or(N, naxis)for a batch.origin (int, optional) –
0(default) treatspixas 0-based,1treats it as 1-based FITS coordinates.
- Returns:
list of float or numpy.ndarray – World coordinates with units given by
cunit. A list for a single point, an(N, naxis)array for a batch.- Raises:
ValueError – In three cases: -
originis neither0nor1. -pixis neither 1-D nor 2-D. - A batch does not have exactlynaxiscolumns. An(naxis, N)array is the transpose of a batch, not a batch ofNpoints.FitsError – In three cases: - A single point does not have
naxiselements. - The WCS has unresolved-TABaxes. -pixfalls outside the projection’s valid domain. This applies to a single point only.
Notes
The two forms differ on a point the WCS cannot transform. The single-point form raises, so the message names the reason. The batch form fills that point with
nanand keeps going, because a wide field routinely mixes valid and invalid pixels.
- to_header(alt=' ')¶
Serialize this WCS to a fresh
Header.- Parameters:
alt (str, optional) –
' '(default) for the primary description, or'A'through'Z'for an alternate.- Returns:
Header – A new header holding every WCS keyword fitsy’s reader understands: the linear pipeline,
LONPOLE/LATPOLEand the projection’sPVparameters, SIP, TPV, TNX/ZPX, DSS plate solutions, spectral rest quantities, and the-TABpointer cards.- Raises:
FitsError – If
altis not' 'or'A'-'Z'.
Notes
Parsing the returned header reproduces this WCS. Two things a bare header cannot carry:
NAXISnare written as zero placeholders, because a WCS carries no image dimensions. Merge the result into a header that already has the real values.A
-TABaxis names its lookup table byEXTNAME. That BINTABLE is a separate HDU and must be written alongside this header.
- world_to_pixel(world, origin=0)¶
Inverse transform world coordinates to pixel coordinates.
Accepts one point or many, and mirrors
pixel_to_world()in both shape handling and error handling.- Parameters:
world (array-like) – Shape
(naxis,)for a single point, or(N, naxis)for a batch.origin (int, optional) –
0(default) returns 0-based pixel coordinates,1returns 1-based FITS coordinates.
- Returns:
list of float or numpy.ndarray – Pixel coordinates in the chosen origin. A list for a single point, an
(N, naxis)array for a batch.- Raises:
ValueError – In three cases: -
originis neither0nor1. -worldis neither 1-D nor 2-D. - A batch does not have exactlynaxiscolumns.FitsError – In three cases: - A single point does not have
naxiselements. - The WCS has unresolved-TABaxes. - The inverse transform does not converge. This applies to a single point only.
Notes
As in
pixel_to_world(), the batch form yieldsnanfor a point that does not transform rather than raising.
- class fitsy.WcsFit¶
Bases:
objectResult of
fit_wcs().Carries the fitted
Wcsand per-point residuals.- Variables:
- max_arcsec¶
- residuals_arcsec¶
Per-point residuals as a numpy array of shape
(N, 2), holding(delta_alpha * cos(delta), delta_dec)in arcseconds.
- rms_arcsec¶
- wcs¶
- fitsy.fit_wcs(pixels, sky, projection='TAN', crpix=None, crval=None, sip_order=None, fit_sip_inverse=True, frame='equatorial', origin=0)¶
Fit a celestial WCS to
(pixel, sky)reference correspondences.- Parameters:
pixels (array-like) – Shape
(N, 2)array-like of pixel coordinates (numpy array, list of lists, tuple of tuples, etc.).sky (array-like) – Shape
(N, 2)array-like of(ra, dec)in degrees, or more generally(lon, lat)in the chosenframe(numpy array, list of lists, tuple of tuples, etc.).projection (str, optional) – Three-letter projection code (Paper II Table 13), case-insensitive. One of
AZP,SZP,TAN,STG,SIN,ARC,ZPN,ZEA,AIR,CYP,CEA,CAR,MER,SFL,PAR,MOL,AIT,COP,COE,COD,COO,BON,PCO,TSC,CSC,QSC,HPXorXPH. Default"TAN".crpix (tuple of float, optional) – Pin the reference pixel. Interpreted in the same
originaspixels. DefaultNone, which solves for the reference pixel as part of the fit.crval (tuple of float, optional) – Pin the tangent point, in degrees. Default
None, which uses the spherical centroid of the sky points.sip_order (int, optional) – Order of a SIP polynomial distortion fit. Valid range is 2 to 9. Default
None, which fits no SIP distortion. Orders 0 and 1 are rejected, becauseCRPIXand theCDmatrix already absorb those terms.fit_sip_inverse (bool, optional) – When
sip_orderis given, also fit the AP/BP inverse polynomial. DefaultTrue.frame (str, optional) – Celestial frame for the sky coordinates, case-insensitive.
'equatorial'(default),'icrs','fk5'and'fk4'are synonyms; all four emit theRA--/DEC-CTYPE prefixes. The other accepted values are'galactic','ecliptic','supergalactic'and'helioecliptic'.origin (int, optional) –
0(default, numpy/C convention) treatspixelsandcrpixas 0-based;1treats them as 1-based FITS coordinates. The fitted WCS itself always carries 1-basedCRPIXvalues per the FITS standard.
- Returns:
WcsFit – Fitted WCS, residuals, and summary statistics.
- Raises:
ValueError – If
pixelsorskyis not an(N, 2)array, if they have different numbers of rows, or ifframenames none of the values above.FitsError – If
projectionnames none of the codes above, if fewer than 2 points are given (or fewer than 3 without a pinnedcrpix), or if the fit is ill-conditioned, for example from collinear points.
Examples
>>> import numpy as np, fitsy >>> pix = np.array([[100.0, 100.0], [200.0, 100.0], [100.0, 200.0]]) >>> sky = np.array([[10.00, -5.00], [10.05, -5.00], [10.00, -4.95]]) >>> fit = fitsy.fit_wcs(pix, sky, projection="TAN") >>> fit.rms_arcsec < 1e-6 True