Generalized World Coordinate System (GWCS)
GWCS (Generalized World Coordinate System) is an Astropy affiliated package designed for managing the World Coordinate System of astronomical data. It provides a flexible and general approach to defining and transforming coordinates between detector pixels and various world coordinate systems. Tightly integrated with Astropy, it leverages Astropy's modeling, units, and coordinates frameworks to build complex transformation pipelines. The library maintains an active development pace with frequent patch and minor releases.
Common errors
-
ValueError: operands could not be broadcast together with shapes (N,) (1,N)
cause In versions prior to 0.25.0, 'vector' shaped arrays (e.g., `(N,)` instead of `(1, N)`) would lose all but their first entry if `with_units=True` was used in `LabelMapperArray`.fixUpgrade to `gwcs` version 0.25.0 or later. Ensure array inputs have the expected shape (e.g., `(1, N)` for 1D data or `(N, M)` for 2D data) if `with_units` (though deprecated) is implicitly used or affects behavior. -
TypeError: 'NoneType' object is not callable
cause Prior to version 1.0.1, GWCS evaluation could fail when input or output frames were `None` or `EmptyFrame` objects, leading to incorrect or uncallable transform steps.fixUpgrade to `gwcs` version 1.0.1 or later. Ensure that coordinate frames are properly defined and not `None` or `EmptyFrame` where a valid frame is expected. -
ValueError: Cannot insert a transform before the first frame in the pipeline.
cause Attempting to insert a new transform before the initial coordinate frame in a `GWCS` pipeline (e.g., using `wcs.insert_transform(frame, transform, before=True)` with the `input_frame`). This was explicitly disallowed or raised an error from version 1.0.0 onwards.fixTransforms should typically be inserted between existing frames or appended. If modifying the very beginning of the pipeline, reconstruct the `WCS` object with the desired initial transform and frame, or append/insert after the initial frame.
Warnings
- breaking As of GWCS 0.25.0, support for Python 3.10 has been dropped. Users must upgrade to Python 3.11 or later to use recent versions of `gwcs`.
- deprecated The `with_units` argument (e.g., in `LabelMapperArray`) was deprecated in version 0.26.0 and will be removed in future releases. It caused issues with array-like inputs losing entries.
- deprecated The private `_toindex` function was deprecated in 0.26.1 in favor of the public `to_index` function.
- gotcha GWCS and Astropy have different default conventions for ordering multi-dimensional bounding boxes ('F' (x, y) for GWCS vs. 'C' (y, x) for Astropy). Setting `bounding_box` directly on a GWCS object will convert to GWCS's 'F' ordering and issue a `GwcsBoundingBoxWarning` if a conversion occurs.
- gotcha The `WCS.numerical_inverse()` method may raise `NoConvergence` exceptions if the iterative process fails to converge or diverges. This can lead to `NaN` or `Inf` values in results if not handled.
Install
-
pip install gwcs -
conda install -c conda-forge gwcs
Imports
- WCS
from gwcs import WCS
- coordinate_frames
from gwcs import coordinate_frames as cf
- models
from astropy.modeling import models
- units
from astropy import units as u
- coordinates
from astropy import coordinates as coord
Quickstart
import numpy as np
from astropy.modeling import models
from astropy import units as u
from astropy import coordinates as coord
from gwcs import wcs
from gwcs import coordinate_frames as cf
# Define the transformations
pixel_scale = 0.05 * u.arcsec / u.pixel
detector_center_pixel = (500, 500)
reference_sky_coord = coord.SkyCoord(ra=20 * u.deg, dec=30 * u.deg, frame='icrs')
# 1. Shift pixel coordinates to have (0,0) at detector center
pixel_to_intermediate_frame = models.Shift(-detector_center_pixel[0] * u.pixel) & \
models.Shift(-detector_center_pixel[1] * u.pixel)
# 2. Apply pixel scale
intermediate_to_celestial_angles = models.Scale(pixel_scale) & models.Scale(pixel_scale)
# 3. Tangent projection (TAN) from celestial angles to sky coordinates
celestial_angles_to_sky = models.Pix2Sky_TAN()
# 4. Rotate and shift to final celestial position (simple case for illustration)
# In a real scenario, this would be more complex, involving proper rotation models
# and matching reference points. Here we directly set the reference_sky_coord.
# Define the frames
detector_frame = cf.Frame2D(name="detector", axes_names=('x', 'y'), unit=(u.pixel, u.pixel))
celestial_frame = cf.CelestialFrame(reference_frame=reference_sky_coord.frame,
name='icrs', unit=(u.deg, u.deg))
# Build the GWCS pipeline
pipeline = [
wcs.Step(detector_frame, pixel_to_intermediate_frame | intermediate_to_celestial_angles | celestial_angles_to_sky),
wcs.Step(celestial_frame, None)
]
# Create the WCS object
image_wcs = wcs.WCS(pipeline)
# Example transformation
pixel_coords = np.array([[100, 200], [500, 500]]) * u.pixel
world_coords = image_wcs.pixel_to_world(pixel_coords[:, 0], pixel_coords[:, 1])
print(f"Input pixel coordinates:\n{pixel_coords}")
print(f"Output world coordinates:\n{world_coords}")