Beispiel #1
0
def test_simple_gwcs():
    # https://gwcs.readthedocs.io/en/latest/#getting-started
    shift_by_crpix = models.Shift(-(2048 - 1) * u.pix) & models.Shift(-(1024 - 1) * u.pix)
    matrix = np.array([[1.290551569736E-05, 5.9525007864732E-06],
                       [5.0226382102765E-06, -1.2644844123757E-05]])
    rotation = models.AffineTransformation2D(matrix * u.deg, translation=[0, 0] * u.deg)
    rotation.input_units_equivalencies = {"x": u.pixel_scale(1 * (u.deg / u.pix)),
                                          "y": u.pixel_scale(1 * (u.deg / u.pix))}
    rotation.inverse = models.AffineTransformation2D(np.linalg.inv(matrix) * u.pix,
                                                     translation=[0, 0] * u.pix)
    rotation.inverse.input_units_equivalencies = {"x": u.pixel_scale(1 * (u.pix / u.deg)),
                                                  "y": u.pixel_scale(1 * (u.pix / u.deg))}
    tan = models.Pix2Sky_TAN()
    celestial_rotation = models.RotateNative2Celestial(
        5.63056810618 * u.deg, -72.05457184279 * u.deg, 180 * u.deg)
    det2sky = shift_by_crpix | rotation | tan | celestial_rotation
    det2sky.name = "linear_transform"
    detector_frame = cf.Frame2D(name="detector", axes_names=("x", "y"), unit=(u.pix, u.pix))
    sky_frame = cf.CelestialFrame(reference_frame=ICRS(), name='icrs', unit=(u.deg, u.deg))
    pipeline = [(detector_frame, det2sky), (sky_frame, None)]
    w = gwcs.wcs.WCS(pipeline)

    result = wcs_utils.get_compass_info(w, (1024, 2048), r_fac=0.25)
    assert_allclose(result[:-1], (1024.0, 512.0,
                                  1131.0265005852038, 279.446189124443,
                                  1262.0057201165127, 606.2863901330095,
                                  155.2870478938214, -86.89813081941797))
    assert not result[-1]
def test1(tmpdir, ret=False):
    shift_by_crpix = models.Shift(-2048 * u.pix) & models.Shift(-1024 * u.pix)
    matrix = np.array([[1.290551569736E-05, 5.9525007864732E-06],
                       [5.0226382102765E-06, -1.2644844123757E-05]])
    rotation = models.AffineTransformation2D(matrix * u.deg,
                                             translation=[0, 0] * u.deg)
    rotation.input_units_equivalencies = {
        "x": u.pixel_scale(1 * u.deg / u.pix),
        "y": u.pixel_scale(1 * u.deg / u.pix)
    }
    rotation.inverse = models.AffineTransformation2D(
        np.linalg.inv(matrix) * u.pix, translation=[0, 0] * u.pix)
    rotation.inverse.input_units_equivalencies = {
        "x": u.pixel_scale(1 * u.pix / u.deg),
        "y": u.pixel_scale(1 * u.pix / u.deg)
    }
    tan = models.Pix2Sky_TAN()
    celestial_rotation = models.RotateNative2Celestial(5.63056810618 * u.deg,
                                                       -72.05457184279 * u.deg,
                                                       180 * u.deg)
    det2sky = shift_by_crpix | rotation | tan | celestial_rotation
    det2sky.name = "linear_transform"
    detector_frame = cf.Frame2D(name="detector",
                                axes_names=("x", "y"),
                                unit=(u.pix, u.pix))
    sky_frame = cf.CelestialFrame(reference_frame=coords.ICRS(),
                                  name='icrs',
                                  unit=(u.deg, u.deg))
    pipeline = [(detector_frame, det2sky), (sky_frame, None)]
    wcsobj = gwcs.wcs.WCS(pipeline)
    wcs_set = WcsSet(default=wcsobj, extra=wcsobj)
    tree = {'wcs_set': wcs_set}
    if ret:
        return wcs_set
    helpers.assert_roundtrip_tree(tree, tmpdir)
Beispiel #3
0
def gwcs_simple_imaging_units():
    shift_by_crpix = models.Shift(-2048 * u.pix) & models.Shift(-1024 * u.pix)
    matrix = np.array([[1.290551569736E-05, 5.9525007864732E-06],
                       [5.0226382102765E-06, -1.2644844123757E-05]])
    rotation = models.AffineTransformation2D(matrix * u.deg,
                                             translation=[0, 0] * u.deg)
    rotation.input_units_equivalencies = {
        "x": u.pixel_scale(1 * u.deg / u.pix),
        "y": u.pixel_scale(1 * u.deg / u.pix)
    }
    rotation.inverse = models.AffineTransformation2D(
        np.linalg.inv(matrix) * u.pix, translation=[0, 0] * u.pix)
    rotation.inverse.input_units_equivalencies = {
        "x": u.pixel_scale(1 * u.pix / u.deg),
        "y": u.pixel_scale(1 * u.pix / u.deg)
    }
    tan = models.Pix2Sky_TAN()
    celestial_rotation = models.RotateNative2Celestial(5.63056810618 * u.deg,
                                                       -72.05457184279 * u.deg,
                                                       180 * u.deg)
    det2sky = shift_by_crpix | rotation | tan | celestial_rotation
    det2sky.name = "linear_transform"

    detector_frame = cf.Frame2D(name="detector",
                                axes_names=("x", "y"),
                                unit=(u.pix, u.pix))
    sky_frame = cf.CelestialFrame(reference_frame=coord.ICRS(),
                                  name='icrs',
                                  unit=(u.deg, u.deg))
    pipeline = [(detector_frame, det2sky), (sky_frame, None)]
    return wcs.WCS(pipeline)
Beispiel #4
0
def gwcs_7d_complex_mapping():
    """
    Useful features of this WCS (axes indices here are 0-based):
        - includes two celestial axes: input (0, 1) maps to world (2 - RA, 1 - Dec)
        - includes one separable frame with one axis: 4 -> 2
        - includes one frame with 3 input and 4 output axes (1 degenerate),
          with separable world axes (3, 5) and (0, 6).
    """
    offx = models.Shift(-64, name='x_translation')
    offy = models.Shift(-32, name='y_translation')
    cd = np.array([[1.2906, 0.59532], [0.50222, -1.2645]])
    aff = models.AffineTransformation2D(matrix=1e-5 * cd, name='rotation')
    aff2 = models.AffineTransformation2D(matrix=cd, name='rotation2')

    wcslin = (offx & offy) | aff
    tan = models.Pix2Sky_TAN(name='tangent_projection')
    n2c = models.RotateNative2Celestial(5.630568, -72.0546, 180, name='skyrot')
    icrs = cf.CelestialFrame(reference_frame=coord.ICRS(),
                             name='sky',
                             axes_order=(2, 1))
    spec = cf.SpectralFrame(name='wave',
                            unit=[u.m],
                            axes_order=(4, ),
                            axes_names=('lambda', ))
    cmplx = cf.CoordinateFrame(
        name="complex",
        naxes=4,
        axes_order=(3, 5, 0, 6),
        axis_physical_types=(['em.wl', 'em.wl', 'time', 'time']),
        axes_type=("SPATIAL", "SPATIAL", "TIME", "TIME"),
        axes_names=("x", "y", "t", 'tau'),
        unit=(u.m, u.m, u.second, u.second))

    comp_frm = cf.CompositeFrame(frames=[icrs, spec, cmplx], name='TEST 7D')
    wcs_forward = (
        (wcslin & models.Shift(-3.14) & models.Scale(2.7) & aff2) |
        (tan & models.Identity(1) & models.Identity(1) & models.Identity(2)) |
        (n2c & models.Identity(1) & models.Identity(1) & models.Identity(2))
        | models.Mapping((3, 1, 0, 4, 2, 5, 3)))

    detector_frame = cf.CoordinateFrame(name="detector",
                                        naxes=6,
                                        axes_order=(0, 1, 2, 3, 4, 5),
                                        axes_type=("pixel", "pixel", "pixel",
                                                   "pixel", "pixel", "pixel"),
                                        unit=(u.pix, u.pix, u.pix, u.pix,
                                              u.pix, u.pix))

    pipeline = [('detector', wcs_forward), (comp_frm, None)]
    w = wcs.WCS(forward_transform=wcs_forward,
                output_frame=comp_frm,
                input_frame=detector_frame)
    w.bounding_box = ((0, 15), (0, 31), (0, 20), (0, 10), (0, 10), (0, 1))

    w.array_shape = (2, 11, 11, 21, 32, 16)
    w.pixel_shape = (16, 32, 21, 11, 11, 2)

    return w
def homothetic_det2sky(input_center, angle, scale, output_center):
    """
    Create the homothetic transform from a .pcf file.

    The forward direction is sky to detector.
    Parameters
    ----------
    input_center : ndarray of shape (1,2) or iterable
        (x, y) coordinate of the input rotation center
    angle : float
        Rotation angle in degrees
    scale : ndarray of shape (1,2) or iterable
        Scaling factors in x, y directions
    output_center : ndarray of shape (1,2) or iterable
        (x, y) coordinate of the output rotation center

    """

    input_center = np.array(input_center, dtype=np.float)
    output_center = np.array(output_center, dtype=np.float)
    scale = np.array(scale, dtype=np.float)
    angle = np.deg2rad(angle)

    rotmat_sky2det = [[np.cos(angle), -np.sin(angle)],
                      [np.sin(angle), np.cos(angle)]]
    scaling = np.array([[1 / scale[0], 0], [0, 1 / scale[1]]])
    mat_sky2det = np.dot(rotmat_sky2det, scaling)

    aff = models.AffineTransformation2D(matrix=mat_sky2det)


    transform = models.Shift(-output_center[0]) & models.Shift(-output_center[1]) | \
              aff | models.Shift(input_center[0]) & models.Shift(input_center[1])
    return transform
def time_init_7_no_units():
    m = (models.Shift(-10.5) & models.Shift(-13.2) |
         models.AffineTransformation2D(matrix=[[1, 0], [0, 1]],
                                       translation=[0, 0]) |
         models.Scale(.01) & models.Scale(.04) |
         models.Pix2Sky_TAN() |
         models.RotateNative2Celestial(5.6, -72.05, 180))
Beispiel #7
0
 def setup(self):
     aff = models.AffineTransformation2D(matrix=[[1, 0], [0, 1]],
                                         translation=[0, 0])
     self.model = (models.Shift(-10.5) & models.Shift(-13.2) | aff
                   | models.Scale(.01) & models.Scale(.04)
                   | models.Pix2Sky_TAN()
                   | models.RotateNative2Celestial(5.6, -72.05, 180))
Beispiel #8
0
    def from_fits(cls, header):
        if not isinstance(header, fits.Header):
            raise TypeError("expected a FIST Header")
        fitswcs = util.read_wcs_from_header(header)
        wcsaxes = fitswcs['WCSAXES']

        if fitswcs['has_cd']:
            pc = fitswcs['CD']
        else:
            pc = fitswcs['PC']
        # get the part of the PC matrix corresponding to the imaging axes
        sky_axes = None
        if pc.shape != (2, 2):
            sky_axes, _ = util.get_axes(fitswcs)
            i, j = sky_axes
            sky_pc = np.zeros((2,2))
            sky_pc[0, 0] = pc[i, i]
            sky_pc[0, 1] = pc[i, j]
            sky_pc[1, 0] = pc[j, i]
            sky_pc[1, 1] = pc[j, j]
            pc = sky_pc.copy()

        if sky_axes is not None:
            crpix = []
            cdelt = []
            for i in sky_axes:
                crpix.append(fitswcs['CRPIX'][i])
                cdelt.append(fitswcs['CDELT'][i])
        else:
            cdelt = fitswcs['CDELT']
            crpix = fitswcs['CRPIX']
        translation = astmodels.Shift(-crpix[0], name='offset_x') & astmodels.Shift(-crpix[1], name='offset_y')
        rotation = astmodels.AffineTransformation2D(matrix=pc, name='orient')
        scale = astmodels.Scale(cdelt[0], name='scale_x') & astmodels.Scale(cdelt[1], name='scale_y')
        return cls(translation, rotation, scale)
Beispiel #9
0
def generate_celestial_transform(crpix: Union[Iterable[float], u.Quantity],
                                 cdelt: Union[Iterable[float], u.Quantity],
                                 pc: Union[ArrayLike, u.Quantity],
                                 crval: Union[Iterable[float], u.Quantity],
                                 lon_pole: Union[float, u.Quantity] = None,
                                 projection: Model = m.Pix2Sky_TAN()) -> CompoundModel:
    """
    Create a simple celestial transform from FITS like parameters.

    Supports unitful or unitless parameters, but if any parameters have units
    all must have units, if parameters are unitless they are assumed to be in
    degrees.

    Parameters
    ----------
    crpix
        The reference pixel (a length two array).
    crval
        The world coordinate at the reference pixel (a length two array).
    pc
        The rotation matrix for the affine transform. If specifying parameters
        with units this should have celestial (``u.deg``) units.
    lon_pole
        The longitude of the celestial pole, defaults to 180 degrees.
    projection
        The map projection to use, defaults to ``TAN``.

    Notes
    -----

    This function has not been tested with more complex projections. Ensure
    that your lon_pole is correct for your projection.
    """
    spatial_unit = None
    if hasattr(crval[0], "unit"):
        spatial_unit = crval[0].unit
    # TODO: Note this assumption is only valid for certain projections.
    if lon_pole is None:
        lon_pole = 180
    if spatial_unit is not None:
        lon_pole = u.Quantity(lon_pole, unit=spatial_unit)

    # Make translation unitful if all parameters have units
    translation = (0, 0)
    if spatial_unit is not None:
        translation *= pc.unit
        # If we have units then we need to convert all things to Quantity
        # as they might be Parameter classes
        crpix = u.Quantity(crpix)
        cdelt = u.Quantity(cdelt)
        crval = u.Quantity(crval)
        lon_pole = u.Quantity(lon_pole)
        pc = u.Quantity(pc)

    shift = m.Shift(-crpix[0]) & m.Shift(-crpix[1])
    scale = m.Multiply(cdelt[0]) & m.Multiply(cdelt[1])
    rot = m.AffineTransformation2D(pc, translation=translation)
    skyrot = m.RotateNative2Celestial(crval[0], crval[1], lon_pole)
    return shift | scale | rot | projection | skyrot
 def setup(self):
     aff = models.AffineTransformation2D(matrix=[[1, 0], [0, 1]] * u.arcsec,
                                         translation=[0, 0] * u.arcsec)
     aff.input_units_equivalencies = {'x': u.pixel_scale(1 * u.arcsec/u.pix),
                                      'y': u.pixel_scale(1 * u.arcsec/u.pix)}
     self.model = (models.Shift(-10.5 * u.pix) & models.Shift(-13.2 * u.pix) |
                   aff |
                   models.Scale(.01 * u.arcsec) & models.Scale(.04 * u.deg) |
                   models.Pix2Sky_TAN() |
                   models.RotateNative2Celestial(5.6 * u.deg, -72.05 * u.deg, 180 * u.deg))
Beispiel #11
0
def test_transforms_compound(tmpdir):
    tree = {
        'compound':
        astmodels.Shift(1) & astmodels.Shift(2) | astmodels.Sky2Pix_TAN()
        | astmodels.Rotation2D()
        | astmodels.AffineTransformation2D([[2, 0], [0, 2]], [42, 32]) +
        astmodels.Rotation2D(32)
    }

    helpers.assert_roundtrip_tree(tree, tmpdir)
Beispiel #12
0
def test_auto_inline_recursive(tmpdir):
    from astropy.modeling import models
    aff = models.AffineTransformation2D(matrix=[[1, 2], [3, 4]])
    tree = {'test': aff}

    def check_asdf(asdf):
        assert len(list(asdf.blocks.internal_blocks)) == 0

    helpers.assert_roundtrip_tree(tree, tmpdir, check_asdf, None,
                                  {'auto_inline': 64})
Beispiel #13
0
def test_to_fits_sip_pc_normalization(gwcs_simple_imaging_units, matrix_type):
    y, x = np.mgrid[:1024:10, :1024:10]
    xflat = np.ravel(x[1:-1, 1:-1])
    yflat = np.ravel(y[1:-1, 1:-1])
    bounding_box = ((0, 1024), (0, 1024))

    # create a simple imaging WCS without distortions:
    cdmat = np.array([[1.29e-5, 5.95e-6], [5.02e-6, -1.26e-5]])
    aff = models.AffineTransformation2D(matrix=cdmat, name='rotation')

    offx = models.Shift(-501, name='x_translation')
    offy = models.Shift(-501, name='y_translation')

    wcslin = (offx & offy) | aff

    n2c = models.RotateNative2Celestial(5.63, -72.05, 180, name='sky_rotation')
    tan = models.Pix2Sky_TAN(name='tangent_projection')

    wcs_forward = wcslin | tan | n2c

    sky_cs = cf.CelestialFrame(reference_frame=coord.ICRS(), name='sky')
    pipeline = [('detector', wcs_forward), (sky_cs, None)]

    wcs_lin = wcs.WCS(
        input_frame=cf.Frame2D(name='detector'),
        output_frame=sky_cs,
        forward_transform=pipeline
    )

    _, _, celestial_group = wcs_lin._separable_groups(detect_celestial=True)
    fits_wcs = wcs_lin._to_fits_sip(
        celestial_group=celestial_group,
        keep_axis_position=False,
        bounding_box=bounding_box,
        max_pix_error=0.1,
        degree=None,
        max_inv_pix_error=0.1,
        inv_degree=None,
        npoints=32,
        crpix=None,
        projection='TAN',
        matrix_type=matrix_type,
        verbose=False
    )
    fitssip = astwcs.WCS(fits_wcs)

    fitsvalx, fitsvaly = fitssip.wcs_pix2world(xflat, yflat, 0)
    inv_fitsvalx, inv_fitsvaly = fitssip.wcs_world2pix(fitsvalx, fitsvaly, 0)
    gwcsvalx, gwcsvaly = wcs_lin(xflat, yflat)

    assert_allclose(gwcsvalx, fitsvalx, atol=4e-11, rtol=0)
    assert_allclose(gwcsvaly, fitsvaly, atol=4e-11, rtol=0)

    assert_allclose(xflat, inv_fitsvalx, atol=5e-9, rtol=0)
    assert_allclose(yflat, inv_fitsvaly, atol=5e-9, rtol=0)
Beispiel #14
0
def gwcs_spec_cel_time_4d():
    """
    A complex 4D mixed celestial + spectral + time WCS.
    """
    # spectroscopic frame:
    wave_model = models.Shift(-5) | models.Multiply(3.7) | models.Shift(20)
    wave_model.bounding_box = (7, 50)
    wave_frame = cf.SpectralFrame(name='wave',
                                  unit=u.m,
                                  axes_order=(0, ),
                                  axes_names=('lambda', ))

    # time frame:
    time_model = models.Identity(1)  # models.Linear1D(10, 0)
    time_frame = cf.TemporalFrame(Time("2010-01-01T00:00"),
                                  name='time',
                                  unit=u.s,
                                  axes_order=(3, ))

    # Values from data/acs.hdr:
    crpix = (12, 13)
    crval = (5.63, -72.05)
    cd = [[1.291E-05, 5.9532E-06], [5.02215E-06, -1.2645E-05]]
    aff = models.AffineTransformation2D(matrix=cd, name='rotation')
    offx = models.Shift(-crpix[0], name='x_translation')
    offy = models.Shift(-crpix[1], name='y_translation')
    wcslin = models.Mapping((1, 0)) | (offx & offy) | aff
    tan = models.Pix2Sky_TAN(name='tangent_projection')
    n2c = models.RotateNative2Celestial(*crval, 180, name='sky_rotation')
    cel_model = wcslin | tan | n2c
    icrs = cf.CelestialFrame(reference_frame=coord.ICRS(),
                             name='sky',
                             axes_order=(2, 1))

    wcs_forward = wave_model & cel_model & time_model

    comp_frm = cf.CompositeFrame(frames=[wave_frame, icrs, time_frame],
                                 name='TEST 4D FRAME')

    detector_frame = cf.CoordinateFrame(name="detector",
                                        naxes=4,
                                        axes_order=(0, 1, 2, 3),
                                        axes_type=("pixel", "pixel", "pixel",
                                                   "pixel"),
                                        unit=(u.pix, u.pix, u.pix, u.pix))

    w = wcs.WCS(forward_transform=wcs_forward,
                output_frame=comp_frm,
                input_frame=detector_frame)

    w.bounding_box = ((0, 63), (0, 127), (0, 255), (0, 9))
    w.array_shape = (10, 256, 128, 64)
    w.pixel_shape = (64, 128, 256, 10)
    return w
Beispiel #15
0
def test_auto_inline_recursive(tmpdir):
    astropy = pytest.importorskip('astropy')
    from astropy.modeling import models

    aff = models.AffineTransformation2D(matrix=[[1, 2], [3, 4]])
    tree = {'test': aff}

    def check_asdf(asdf):
        assert len(list(asdf.blocks.internal_blocks)) == 0

    helpers.assert_roundtrip_tree(tree, tmpdir, asdf_check_func=check_asdf,
                                  write_options={'auto_inline': 64})
Beispiel #16
0
def test_overwrite(tmpdir):
    # This is intended to reproduce the following issue:
    # https://github.com/asdf-format/asdf/issues/100
    tmpfile = os.path.join(str(tmpdir), 'test.asdf')
    aff = models.AffineTransformation2D(matrix=[[1, 2], [3, 4]])
    f = asdf.AsdfFile()
    f.tree['model'] = aff
    f.write_to(tmpfile)
    model = f.tree['model']

    ff = asdf.AsdfFile()
    ff.tree['model'] = model
    ff.write_to(tmpfile)
Beispiel #17
0
def gwcs_cube_with_separable_time(request):
    """
    A mixed celestial + time WCS.
    """
    cube_size = (64, 32, 128)

    axes_order = request.param
    time_axes_order = (axes_order.index(2), )
    cel_axes_order = (axes_order.index(0), axes_order.index(1))

    detector_frame = cf.CoordinateFrame(name="detector",
                                        naxes=3,
                                        axes_order=(0, 1, 2),
                                        axes_type=("pixel", "pixel", "pixel"),
                                        unit=(u.pix, u.pix, u.pix))

    # time frame:
    time_model = models.Identity(1)  # models.Linear1D(10, 0)
    time_frame = cf.TemporalFrame(Time("2010-01-01T00:00"),
                                  name='time',
                                  unit=u.s,
                                  axes_order=time_axes_order)

    # Values from data/acs.hdr:
    crpix = (12, 13)
    crval = (5.63, -72.05)
    cd = [[1.291E-05, 5.9532E-06], [5.02215E-06, -1.2645E-05]]
    aff = models.AffineTransformation2D(matrix=cd, name='rotation')
    offx = models.Shift(-crpix[0], name='x_translation')
    offy = models.Shift(-crpix[1], name='y_translation')
    wcslin = models.Mapping((1, 0)) | (offx & offy) | aff
    tan = models.Pix2Sky_TAN(name='tangent_projection')
    n2c = models.RotateNative2Celestial(*crval, 180, name='sky_rotation')
    cel_model = wcslin | tan | n2c
    icrs = cf.CelestialFrame(reference_frame=coord.ICRS(),
                             name='sky',
                             axes_order=cel_axes_order)

    wcs_forward = (cel_model & time_model) | models.Mapping(axes_order)

    comp_frm = cf.CompositeFrame(frames=[icrs, time_frame],
                                 name='TEST 3D FRAME WITH TIME')

    w = wcs.WCS(forward_transform=wcs_forward,
                output_frame=comp_frm,
                input_frame=detector_frame)

    w.bounding_box = tuple((0, k - 1) for k in cube_size)
    w.pixel_shape = cube_size
    w.array_shape = w.pixel_shape[::-1]
    return w
Beispiel #18
0
def gwcs_cube_with_separable_spectral(request):
    cube_size = (128, 64, 100)

    axes_order = request.param
    spectral_axes_order = (axes_order.index(2), )
    cel_axes_order = (axes_order.index(0), axes_order.index(1))

    # Values from data/acs.hdr:
    crpix = (64, 32)
    crval = (5.63056810618, -72.0545718428)
    cd = [[1.29058667557984E-05, 5.95320245884555E-06],
          [5.02215195623825E-06, -1.2645010396976E-05]]

    aff = models.AffineTransformation2D(matrix=cd, name='rotation')
    offx = models.Shift(-crpix[0], name='x_translation')
    offy = models.Shift(-crpix[1], name='y_translation')

    wcslin = (offx & offy) | aff
    tan = models.Pix2Sky_TAN(name='tangent_projection')
    n2c = models.RotateNative2Celestial(*crval, 180, name='sky_rotation')
    icrs = cf.CelestialFrame(reference_frame=coord.ICRS(),
                             name='sky',
                             axes_order=cel_axes_order)
    spec = cf.SpectralFrame(name='wave',
                            unit=[
                                u.m,
                            ],
                            axes_order=spectral_axes_order,
                            axes_names=('lambda', ))
    comp_frm = cf.CompositeFrame(frames=[icrs, spec],
                                 name='TEST 3D FRAME WITH SPECTRAL AXIS')
    wcs_forward = ((wcslin & models.Identity(1)) | (tan & models.Identity(1)) |
                   (n2c & models.Identity(1)) | models.Mapping(axes_order))

    detector_frame = cf.CoordinateFrame(name="detector",
                                        naxes=3,
                                        axes_order=(0, 1, 2),
                                        axes_type=("pixel", "pixel", "pixel"),
                                        unit=(u.pix, u.pix, u.pix))

    w = wcs.WCS(forward_transform=wcs_forward,
                output_frame=comp_frm,
                input_frame=detector_frame)
    w.bounding_box = tuple((0, k - 1) for k in cube_size)
    w.pixel_shape = cube_size
    w.array_shape = w.pixel_shape[::-1]

    return w, axes_order
Beispiel #19
0
    def setup_class(self):
        hdr = fits.Header.fromtextfile(get_pkg_data_filename("data/acs.hdr"),
                                       endcard=False)
        #warnings.filterwarnings("ignore", message="^The WCS transformation has more axes (2)",
        #                        module="astropy.wcs.wcs")
        self.fitsw = astwcs.WCS(hdr)
        a_coeff = hdr['A_*']
        a_order = a_coeff.pop('A_ORDER')
        b_coeff = hdr['B_*']
        b_order = b_coeff.pop('B_ORDER')

        crpix = [hdr['CRPIX1'], hdr['CRPIX2']]
        distortion = models.SIP(
            crpix, a_order, b_order, a_coeff, b_coeff,
            name='sip_distorion') + models.Identity(2)

        cdmat = np.array([[hdr['CD1_1'], hdr['CD1_2']],
                          [hdr['CD2_1'], hdr['CD2_2']]])
        aff = models.AffineTransformation2D(matrix=cdmat, name='rotation')

        offx = models.Shift(-hdr['CRPIX1'], name='x_translation')
        offy = models.Shift(-hdr['CRPIX2'], name='y_translation')

        wcslin = (offx & offy) | aff

        phi = hdr['CRVAL1']
        lon = hdr['CRVAL2']
        theta = 180
        n2c = models.RotateNative2Celestial(phi,
                                            lon,
                                            theta,
                                            name='sky_rotation')

        tan = models.Pix2Sky_TAN(name='tangent_projection')
        sky_cs = cf.CelestialFrame(reference_frame=coord.ICRS(), name='sky')
        det = cf.Frame2D('detector')
        wcs_forward = wcslin | tan | n2c
        pipeline = [('detector', distortion), ('focal', wcs_forward),
                    (sky_cs, None)]

        self.wcs = wcs.WCS(input_frame=det,
                           output_frame=sky_cs,
                           forward_transform=pipeline)
        nx, ny = (5, 2)
        x = np.linspace(0, 1, nx)
        y = np.linspace(0, 1, ny)
        self.xv, self.yv = np.meshgrid(x, y)
Beispiel #20
0
def create_asdf_ref_files(fname):
    #f = fits.open('../j94f05bgq_flt.fits')
    f = fits.open(fname)
    from gwcs import util
    whdr = util.read_wcs_from_header(f[1].header)
    crpix = whdr['CRPIX']
    shift = models.Shift(crpix[0]) & models.Shift(crpix[1])
    rotation = models.AffineTransformation2D(matrix=whdr['PC'])
    cdelt = whdr['CDELT']
    scale = models.Scale(cdelt[0]) & models.Scale(cdelt[1])
    #print util.get_projcode(whdr['CTYPE'][0])
    tan = models.Pix2Sky_TAN()
    crval = whdr['CRVAL']
    n2c = models.RotateNative2Celestial(crval[0], crval[1], 180)
    foc2sky = (shift | rotation | scale | tan | n2c).rename('foc2sky')
    fasdf = AsdfFile()
    fasdf.tree = {'model': foc2sky}
    fasdf.write_to('foc2sky.asdf')
Beispiel #21
0
def homothetic_sky2det(input_center, angle, scale, output_center, name=""):
    """
    Create the homothetic transform from a .pcf file.

    The forward direction is sky to detector.
    Parameters
    ----------
    input_center : ndarray of shape (1,2) or iterable
        (x, y) coordinate of the input rotation center
    angle : float
        Rotation angle in degrees
    scale : ndarray of shape (1,2) or iterable
        Scaling factors in x, y directions
    output_center : ndarray of shape (1,2) or iterable
        (x, y) coordinate of the output rotation center

    """

    input_center = np.array(input_center, dtype=np.float)
    output_center = np.array(output_center, dtype=np.float)
    scale = np.array(scale, dtype=np.float)
    angle = np.deg2rad(angle)

    rotmat_sky2det = [[np.cos(angle), np.sin(angle)],
                      [-np.sin(angle), np.cos(angle)]]
    scaling = np.array([[scale[0], 0], [0, scale[1]]])
    mat_sky2det = np.dot(scaling, rotmat_sky2det)

    aff = models.AffineTransformation2D(matrix=mat_sky2det,
                                        name="{0}_affine".format(name))


    transform = models.Shift(-input_center[0], name="{0}_xincen_s2d".format(name)) & \
              models.Shift(-input_center[1], name="{0}_yincen_s2d".format(name)) | aff | \
              models.Shift(output_center[0], name="{0}_xoutcen_s2d".format(name)) & \
              models.Shift(output_center[1], name="{0}_youtcen_s2d".format(name))
    return transform
Beispiel #22
0
def fpa2asdf(fpafile, author, description, useafter):
    """
    Create an asdf reference file with the FPA description.

    The CDP2 delivery includes a fits file - "FPA.fpa" which is the
    input to this function. This file is converted to asdf and is a
    reference file of type "FPA".

    fpa2asdf('Ref_Files/CoordTransform/Description/FPA.fpa', 'fpa.asdf')

    Parameters
    ----------
    fpafile : str
        A fits file with FPA description (FPA.fpa)
    outname : str
        Name of output ASDF file.
    """
    with open(fpafile) as f:
        lines = [l.strip() for l in f.readlines()]

    # NRS1
    ind = lines.index("*SCA491_PitchX")
    nrs1_pitchx = float(lines[ind + 1])
    ind = lines.index("*SCA491_PitchY")
    nrs1_pitchy = float(lines[ind + 1])
    ind = lines.index("*SCA491_RotAngle")
    nrs1_angle = float(lines[ind + 1])
    ind = lines.index("*SCA491_PosX")
    nrs1_posx = float(lines[ind + 1])
    ind = lines.index("*SCA491_PosY")
    nrs1_posy = float(lines[ind + 1])

    # NRS2
    ind = lines.index("*SCA492_PitchX")
    nrs2_pitchx = float(lines[ind + 1])
    ind = lines.index("*SCA492_PitchY")
    nrs2_pitchy = float(lines[ind + 1])
    ind = lines.index("*SCA492_RotAngle")
    nrs2_angle = float(lines[ind + 1])
    ind = lines.index("*SCA492_PosX")
    nrs2_posx = float(lines[ind + 1])
    ind = lines.index("*SCA492_PosY")
    nrs2_posy = float(lines[ind + 1])

    # NRS1 Sky to Detector
    scaling = np.array([[1 / nrs1_pitchx, 0], [0, 1 / nrs1_pitchy]])
    rotmat = models.Rotation2D._compute_matrix(-nrs1_angle)
    matrix = np.dot(scaling, rotmat)
    aff = models.AffineTransformation2D(matrix, name='fpa_affine_s2d')
    nrs1_sky2det = models.Shift(-nrs1_posx, name='fpa_x_s2d') & \
        models.Shift(-nrs1_posy, name='fpa_y_s2d') | aff

    # NRS1 Detector to Sky
    rotmat = models.Rotation2D._compute_matrix(-nrs1_angle)
    scaling = np.array([[nrs1_pitchx, 0], [0, nrs1_pitchy]])
    matrix = np.dot(rotmat, scaling)
    aff = models.AffineTransformation2D(matrix, name='fpa_affine_d2s')
    nrs1_det2sky = aff | models.Shift(nrs1_posx, name='fpa_x_d2s') & \
        models.Shift(nrs1_posy, name='fpa_y_d2s')

    nrs1_det2sky.inverse = nrs1_sky2det

    # NRS2 Sky to Detector
    scaling = np.array([[-1 / nrs2_pitchx, 0], [0, -1 / nrs2_pitchy]])
    rotmat = models.Rotation2D._compute_matrix(-nrs2_angle)
    matrix = np.dot(scaling, rotmat)
    aff = models.AffineTransformation2D(matrix, name='fpa_affine_s2d')
    nrs2_sky2det = models.Shift(-nrs2_posx, name='fpa_x_s2d') & \
        models.Shift(-nrs2_posy, name='fpa_y_s2d') | aff

    # NRS2 Detector to Sky
    rotmat = models.Rotation2D._compute_matrix(nrs2_angle)
    scaling = np.array([[-nrs2_pitchx, 0], [0, -nrs2_pitchy]])
    matrix = np.dot(scaling, rotmat)
    aff = models.AffineTransformation2D(matrix, name='fpa_affine_d2s')
    nrs2_det2sky = aff | models.Shift(nrs2_posx, name='fpa_x_d2s') &  \
        models.Shift(nrs2_posy, name='fpa_y_d2s')

    nrs2_det2sky.inverse = nrs2_sky2det

    fpa_model = FPAModel()
    fpa_model.nrs1_model = nrs1_det2sky
    fpa_model.nrs2_model = nrs2_det2sky
    fpa_model.meta.author = author
    fpa_model.meta.description = description
    fpa_model.meta.useafter = useafter
    fpa_model.meta.pedigree = "GROUND"

    return fpa_model
Beispiel #23
0
def fitswcs_linear(header):
    """
    Create a WCS linear transform from a FITS header.

    Parameters
    ----------
    header : astropy.io.fits.Header or dict
        FITS Header or dict with basic FITS WCS keywords.

    """
    if isinstance(header, fits.Header):
        wcs_info = read_wcs_from_header(header)
    elif isinstance(header, dict):
        wcs_info = header
    else:
        raise TypeError("Expected a FITS Header or a dict.")

    pc = wcs_info['PC']
    # get the part of the PC matrix corresponding to the imaging axes
    sky_axes, spec_axes, unknown = get_axes(wcs_info)
    if pc.shape != (2, 2):
        if sky_axes:
            i, j = sky_axes
        elif unknown and len(unknown) == 2:
            i, j = unknown
        sky_pc = np.zeros((2, 2))
        sky_pc[0, 0] = pc[i, i]
        sky_pc[0, 1] = pc[i, j]
        sky_pc[1, 0] = pc[j, i]
        sky_pc[1, 1] = pc[j, j]
        pc = sky_pc.copy()

    sky_axes.extend(unknown)
    if sky_axes:
        crpix = []
        cdelt = []
        for i in sky_axes:
            crpix.append(wcs_info['CRPIX'][i])
            cdelt.append(wcs_info['CDELT'][i])
    else:
        cdelt = wcs_info['CDELT']
        crpix = wcs_info['CRPIX']

    # if wcsaxes == 2:
    rotation = astmodels.AffineTransformation2D(matrix=pc, name='pc_matrix')
    # elif wcsaxes == 3 :
    # rotation = AffineTransformation3D(matrix=matrix)
    # else:
    # raise DimensionsError("WCSLinearTransform supports only 2 or 3 dimensions, "
    # "{0} given".format(wcsaxes))

    translation_models = [
        astmodels.Shift(-(shift - 1), name='crpix' + str(i + 1))
        for i, shift in enumerate(crpix)
    ]
    translation = functools.reduce(lambda x, y: x & y, translation_models)

    if not wcs_info['has_cd']:
        # Do not compute scaling since CDELT* = 1 if CD is present.
        scaling_models = [
            astmodels.Scale(scale, name='cdelt' + str(i + 1))
            for i, scale in enumerate(cdelt)
        ]

        scaling = functools.reduce(lambda x, y: x & y, scaling_models)
        wcs_linear = translation | rotation | scaling
    else:
        wcs_linear = translation | rotation

    return wcs_linear
Beispiel #24
0
def wcs_from_footprints(dmodels, refmodel=None, transform=None, domain=None):
    """
    Create a WCS from a list of input data models.

    A fiducial point in the output coordinate frame is created from  the
    footprints of all WCS objects. For a spatial frame this is the center
    of the union of the footprints. For a spectral frame the fiducial is in
    the beginning of the footprint range.
    If ``refmodel`` is None, the first WCS object in the list is considered
    a reference. The output coordinate frame and projection (for celestial frames)
    is taken from ``refmodel``.
    If ``transform`` is not suplied, a compound transform is created using
    CDELTs and PC.
    If ``domain`` is not supplied, the domain of the new WCS is computed
    from the domains of all input WCSs.

    Parameters
    ----------
    dmodels : list of `~jwst.datamodels.DataModel`
        A list of data models.
    refmodel : `~jwst.datamodels.DataModel`, optional
        This model's WCS is used as a reference.
        WCS. The output coordinate frame, the projection and a
        scaling and rotation transform is created from it. If not supplied
        the first model in the list is used as ``refmodel``.
    transform : `~astropy.modeling.core.Model`, optional
        A transform, passed to :class_method:`~gwcs.WCS.wcs_from_fiducial`
        If not supplied Scaling | Rotation is computed from ``refmodel``.
    domain : list of dicts, optional
        Domain of the new WCS.
        If not supplied it is computed from the domain of all inputs.
    """
    wcslist = [im.meta.wcs for im in dmodels]
    if not isiterable(wcslist):
        raise ValueError(
            "Expected 'wcslist' to be an iterable of WCS objects.")
    if not all([isinstance(w, WCS) for w in wcslist]):
        raise TypeError(
            "All items in wcslist are to be instances of gwcs.WCS.")
    if refmodel is None:
        refmodel = dmodels[0]
    else:
        if not isinstance(refmodel, DataModel):
            raise TypeError(
                "Expected refmodel to be an instance of DataModel.")

    fiducial = compute_fiducial(wcslist, domain)

    prj = astmodels.Pix2Sky_TAN()

    if transform is None:
        transform = []
        wcsinfo = pointing.wcsinfo_from_model(refmodel)
        sky_axes, _ = gwutils.get_axes(wcsinfo)
        rotation = astmodels.AffineTransformation2D(np.array(wcsinfo['PC']))
        transform.append(rotation)
        if sky_axes:
            scale = wcsinfo['CDELT'][sky_axes].mean()
            scales = astmodels.Scale(scale) & astmodels.Scale(scale)
            transform.append(scales)

        if transform:
            transform = functools.reduce(lambda x, y: x | y, transform)

    out_frame = refmodel.meta.wcs.output_frame
    wnew = wcs_from_fiducial(fiducial,
                             coordinate_frame=out_frame,
                             projection=prj,
                             transform=transform)

    footprints = [w.footprint() for w in wcslist]
    domain_bounds = np.hstack(
        [wnew.backward_transform(*f) for f in footprints])
    for axs in domain_bounds:
        axs -= axs.min()
    domain = []
    for axis in out_frame.axes_order:
        axis_min, axis_max = domain_bounds[axis].min(
        ), domain_bounds[axis].max()
        domain.append({
            'lower': axis_min,
            'upper': axis_max,
            'includes_lower': True,
            'includes_upper': True
        })
    #ax1, ax2 = domain[sky_axes] # change when domain is a bounding_box
    ax1, ax2 = domain
    offset1 = (ax1['upper'] - ax1['lower']) / 2
    offset2 = (ax2['upper'] - ax2['lower']) / 2
    offsets = astmodels.Shift(-offset1) & astmodels.Shift(-offset2)

    wnew.insert_transform('detector', offsets, after=True)
    wnew.domain = domain
    return wnew
Beispiel #25
0
def make_gwcs(shape, galactic=False):
    """
    Create a simple celestial gWCS object in the ICRS coordinate frame.

    This function requires the `gwcs
    <https://github.com/spacetelescope/gwcs>`_ package.

    Parameters
    ----------
    shape : 2-tuple of int
        The shape of the 2D array to be used with the output
        `~gwcs.wcs.WCS` object.

    galactic : bool, optional
        If `True`, then the output WCS will be in the Galactic
        coordinate frame.  If `False` (default), then the output WCS
        will be in the ICRS coordinate frame.

    Returns
    -------
    wcs : `gwcs.wcs.WCS` object
        The generalized world coordinate system (WCS) transformation.

    See Also
    --------
    make_wcs, make_imagehdu

    Notes
    -----
    The `make_wcs` function returns an equivalent WCS transformation to
    this one, but as an `astropy.wcs.WCS` object.

    Examples
    --------
    >>> from photutils.datasets import make_gwcs
    >>> shape = (100, 100)
    >>> gwcs = make_gwcs(shape)
    >>> print(gwcs)
      From      Transform
    -------- ----------------
    detector linear_transform
        icrs             None
    """

    from gwcs import wcs as gwcs_wcs
    from gwcs import coordinate_frames as cf

    rho = np.pi / 3.
    scale = 0.1 / 3600.  # 0.1 arcsec/pixel in deg/pix

    shift_by_crpix = (models.Shift((-shape[1] / 2) + 1)
                      & models.Shift((-shape[0] / 2) + 1))

    cd_matrix = np.array([[-scale * np.cos(rho), scale * np.sin(rho)],
                          [scale * np.sin(rho), scale * np.cos(rho)]])

    rotation = models.AffineTransformation2D(cd_matrix, translation=[0, 0])
    rotation.inverse = models.AffineTransformation2D(np.linalg.inv(cd_matrix),
                                                     translation=[0, 0])

    tan = models.Pix2Sky_TAN()
    celestial_rotation = models.RotateNative2Celestial(197.8925, -1.36555556,
                                                       180.0)

    det2sky = shift_by_crpix | rotation | tan | celestial_rotation
    det2sky.name = 'linear_transform'

    detector_frame = cf.Frame2D(name='detector',
                                axes_names=('x', 'y'),
                                unit=(u.pix, u.pix))

    if galactic:
        sky_frame = cf.CelestialFrame(reference_frame=coord.Galactic(),
                                      name='galactic',
                                      unit=(u.deg, u.deg))
    else:
        sky_frame = cf.CelestialFrame(reference_frame=coord.ICRS(),
                                      name='icrs',
                                      unit=(u.deg, u.deg))

    pipeline = [(detector_frame, det2sky), (sky_frame, None)]

    return gwcs_wcs.WCS(pipeline)
Beispiel #26
0
def fitswcs_image(header):
    """
    Make a complete transform from CRPIX-shifted pixels to
    sky coordinates from FITS WCS keywords. A Mapping is inserted
    at the beginning, which may be removed later

    Parameters
    ----------
    header : `astropy.io.fits.Header` or dict
        FITS Header or dict with basic FITS WCS keywords.

    """
    if isinstance(header, fits.Header):
        wcs_info = read_wcs_from_header(header)
    elif isinstance(header, dict):
        wcs_info = header
    else:
        raise TypeError("Expected a FITS Header or a dict.")

    crpix = wcs_info['CRPIX']
    cd = wcs_info['CD']
    # get the part of the PC matrix corresponding to the imaging axes
    sky_axes, spec_axes, unknown = get_axes(wcs_info)
    if not sky_axes:
        if len(unknown) == 2:
            sky_axes = unknown
        else:  # No sky here
            return
    pixel_axes = _get_contributing_axes(wcs_info, sky_axes)
    if len(pixel_axes) > 2:
        raise ValueError(
            "More than 2 pixel axes contribute to the sky coordinates")

    translation_models = [
        models.Shift(-(crpix[i] - 1), name='crpix' + str(i + 1))
        for i in pixel_axes
    ]
    translation = functools.reduce(lambda x, y: x & y, translation_models)
    transforms = [translation]

    # If only one axis is contributing to the sky (e.g., slit spectrum)
    # then it must be that there's an extra axis in the CD matrix, so we
    # create a "ghost" orthogonal axis here so an inverse can be defined
    # Modify the CD matrix in case we have to use a backup Matrix Model later
    if len(pixel_axes) == 1:
        cd[sky_axes[0], -1] = -cd[sky_axes[1], pixel_axes[0]]
        cd[sky_axes[1], -1] = cd[sky_axes[0], pixel_axes[0]]
        sky_cd = cd[np.ix_(sky_axes, pixel_axes + [-1])]
        affine = models.AffineTransformation2D(matrix=sky_cd, name='cd_matrix')
        # TODO: replace when PR#10362 is in astropy
        #rotation = models.fix_inputs(affine, {'y': 0})
        rotation = models.Mapping(
            (0, 0)) | models.Identity(1) & models.Const1D(0) | affine
        rotation.inverse = affine.inverse | models.Mapping((0, ), n_inputs=2)
    else:
        sky_cd = cd[np.ix_(sky_axes, pixel_axes)]
        rotation = models.AffineTransformation2D(matrix=sky_cd,
                                                 name='cd_matrix')
    transforms.append(rotation)

    projection = gwutils.fitswcs_nonlinear(wcs_info)
    if projection:
        transforms.append(projection)

    sky_model = functools.reduce(lambda x, y: x | y, transforms)
    sky_model.name = 'SKY'
    sky_model.meta.update({'input_axes': pixel_axes, 'output_axes': sky_axes})
    return sky_model
    def _attach_static_distortion(self, adinputs=None):
        """
        This primitive modifies the WCS of its input AD objects to include the
        static distortion read from the lookup table. It will ignore any ADs
        which have CoordinateFrames named "static" on all the extensions, and
        raise an OSError if any ADs have "static" on some (but not all)
        extensions.
        """
        log = self.log

        # Check the inputs haven't been mosaicked or tiled
        mosaic_kws = {
            self.timestamp_keys[prim]
            for prim in ("mosaicDetectors", "tileArrays")
        }
        if any(mosaic_kws.intersection(ad.phu) for ad in adinputs):
            raise ValueError(f"Inputs to {self.myself()} must not have been "
                             "mosaicked or tiled.")

        static_corrections = gsdi.STATIC_CORRECTIONS
        sdmodels = []
        sdsubmodels = {}
        for direction in ("forward", "backward"):
            sdsubmodels[direction] = []
            for component in static_corrections[direction]:
                model_type = getattr(models, component["model"])
                for arr in component["parameters"]:
                    for ordinate in "xy":
                        pars = arr[ordinate]
                        max_xpower = max([int(k[1]) for k in pars])
                        max_ypower = max([int(k[-1]) for k in pars])
                        if component["model"] == "Polynomial2D":
                            degree = {"degree": max(max_xpower, max_ypower)}
                        else:
                            degree = {
                                "xdegree": max_xpower,
                                "ydegree": max_ypower
                            }
                        sdsubmodels[direction].append(
                            model_type(**degree, **pars))
        for index, pixref in enumerate(static_corrections["pixel_references"]):
            sdmodel = (models.Mapping((0, 1, 0, 1)) | (reduce(
                Model.__add__, sdsubmodels["forward"][index * 2::8]) & reduce(
                    Model.__add__, sdsubmodels["forward"][index * 2 + 1::8])))
            sdmodel.inverse = (models.Mapping((0, 1, 0, 1)) | (reduce(
                Model.__add__, sdsubmodels["backward"][index * 2::8]) & reduce(
                    Model.__add__, sdsubmodels["backward"][index * 2 + 1::8])))
            xref, yref = sdmodel.inverse(0, 0)
            if 0 < xref < 2048 and 0 < yref < 2048:
                ref_location = (index, xref - 1, yref - 1
                                )  # store 0-indexed pixel location
            sdmodels.append(sdmodel)

        for ad in adinputs:
            applied_static = sum("static" in ext.wcs.available_frames
                                 for ext in ad)
            if applied_static not in (0, len(ad)):
                raise OSError(
                    f"Some (but not all) extensions in {ad.filename}"
                    " have had the static disortion correction applied")
            # No-op silently
            if applied_static == len(ad):
                continue
            ref_wcs = ad[ref_location[0]].wcs
            ra, dec = ref_wcs(*ref_location[1:])
            for ext, arrsec, sdmodel in zip(ad, ad.array_section(), sdmodels):
                # Include ROI shift
                sdmodel = (models.Shift(arrsec.x1 + 1)
                           & models.Shift(arrsec.y1 + 1) | sdmodel)
                static_frame = cf.Frame2D(unit=(u.arcsec, u.arcsec),
                                          name="static")
                sky_model = models.Scale(1 / 3600) & models.Scale(1 / 3600)
                # the PA header keyword isn't good enough
                wcs_dict = adwcs.gwcs_to_fits(ad[ref_location[0]].nddata)
                pa = np.arctan2(wcs_dict["CD2_1"] - wcs_dict["CD1_2"],
                                wcs_dict["CD1_1"] + wcs_dict["CD2_2"])
                if abs(pa) > 0.00175:  # radians ~ 0.01 degrees
                    # Rotation2D() breaks things because it explicitly converts
                    # the Column objects to Quantity objects, which retain units
                    # of "pix" that Pix2Sky objects to. The AffineTransformation2D
                    # does some multiplications (like Scale) which are agnostic to
                    # the presence or absence of units.
                    sky_model |= models.AffineTransformation2D(
                        matrix=[[math.cos(pa), -math.sin(pa)],
                                [math.sin(pa), math.cos(pa)]])
                sky_model |= models.Pix2Sky_TAN(
                ) | models.RotateNative2Celestial(ra, dec, 180)
                ext.wcs = gWCS([(ext.wcs.input_frame, sdmodel),
                                (static_frame, sky_model),
                                (ext.wcs.output_frame, None)])

        return adinputs
Beispiel #28
0
def wcs_from_footprints(dmodels,
                        refmodel=None,
                        transform=None,
                        bounding_box=None,
                        domain=None):
    """
    Create a WCS from a list of input data models.

    A fiducial point in the output coordinate frame is created from  the
    footprints of all WCS objects. For a spatial frame this is the center
    of the union of the footprints. For a spectral frame the fiducial is in
    the beginning of the footprint range.
    If ``refmodel`` is None, the first WCS object in the list is considered
    a reference. The output coordinate frame and projection (for celestial frames)
    is taken from ``refmodel``.
    If ``transform`` is not suplied, a compound transform is created using
    CDELTs and PC.
    If ``bounding_box`` is not supplied, the bounding_box of the new WCS is computed
    from bounding_box of all input WCSs.

    Parameters
    ----------
    dmodels : list of `~jwst.datamodels.DataModel`
        A list of data models.
    refmodel : `~jwst.datamodels.DataModel`, optional
        This model's WCS is used as a reference.
        WCS. The output coordinate frame, the projection and a
        scaling and rotation transform is created from it. If not supplied
        the first model in the list is used as ``refmodel``.
    transform : `~astropy.modeling.core.Model`, optional
        A transform, passed to :meth:`~gwcs.wcstools.wcs_from_fiducial`
        If not supplied Scaling | Rotation is computed from ``refmodel``.
    bounding_box : tuple, optional
        Bounding_box of the new WCS.
        If not supplied it is computed from the bounding_box of all inputs.
    """
    if domain is not None:
        warnings.warning(
            "'domain' was deprecated in 0.8 and will be removed from next"
            "version. Use 'bounding_box' instead.")
        bb = _domain_to_bounding_box(domain)
    else:
        bb = bounding_box
    wcslist = [im.meta.wcs for im in dmodels]
    if not isiterable(wcslist):
        raise ValueError(
            "Expected 'wcslist' to be an iterable of WCS objects.")
    if not all([isinstance(w, WCS) for w in wcslist]):
        raise TypeError(
            "All items in wcslist are to be instances of gwcs.WCS.")
    if refmodel is None:
        refmodel = dmodels[0]
    else:
        if not isinstance(refmodel, DataModel):
            raise TypeError(
                "Expected refmodel to be an instance of DataModel.")

    fiducial = compute_fiducial(wcslist, bb)

    prj = astmodels.Pix2Sky_TAN()

    if transform is None:
        transform = []
        wcsinfo = pointing.wcsinfo_from_model(refmodel)
        sky_axes, spec, other = gwutils.get_axes(wcsinfo)
        rotation = astmodels.AffineTransformation2D(wcsinfo['PC'])
        transform.append(rotation)
        if sky_axes:
            cdelt1, cdelt2 = wcsinfo['CDELT'][sky_axes]
            scale = np.sqrt(np.abs(cdelt1 * cdelt2))
            scales = astmodels.Scale(scale) & astmodels.Scale(scale)
            transform.append(scales)

        if transform:
            transform = functools.reduce(lambda x, y: x | y, transform)

    out_frame = refmodel.meta.wcs.output_frame
    wnew = wcs_from_fiducial(fiducial,
                             coordinate_frame=out_frame,
                             projection=prj,
                             transform=transform)

    footprints = [w.footprint().T for w in wcslist]
    domain_bounds = np.hstack(
        [wnew.backward_transform(*f) for f in footprints])
    for axs in domain_bounds:
        axs -= axs.min()
    bounding_box = []
    for axis in out_frame.axes_order:
        axis_min, axis_max = domain_bounds[axis].min(
        ), domain_bounds[axis].max()
        bounding_box.append((axis_min, axis_max))
    bounding_box = tuple(bounding_box)
    ax1, ax2 = np.array(bounding_box)[sky_axes]
    offset1 = (ax1[1] - ax1[0]) / 2
    offset2 = (ax2[1] - ax2[0]) / 2
    offsets = astmodels.Shift(-offset1) & astmodels.Shift(-offset2)

    wnew.insert_transform('detector', offsets, after=True)
    wnew.bounding_box = bounding_box
    return wnew
Beispiel #29
0
def wcs_from_footprints(dmodels, refmodel=None, transform=None, bounding_box=None,
                        pscale_ratio=None):
    """
    Create a WCS from a list of input data models.

    A fiducial point in the output coordinate frame is created from  the
    footprints of all WCS objects. For a spatial frame this is the center
    of the union of the footprints. For a spectral frame the fiducial is in
    the beginning of the footprint range.
    If ``refmodel`` is None, the first WCS object in the list is considered
    a reference. The output coordinate frame and projection (for celestial frames)
    is taken from ``refmodel``.
    If ``transform`` is not suplied, a compound transform is created using
    CDELTs and PC.
    If ``bounding_box`` is not supplied, the bounding_box of the new WCS is computed
    from bounding_box of all input WCSs.

    Parameters
    ----------
    dmodels : list of `~jwst.datamodels.DataModel`
        A list of data models.
    refmodel : `~jwst.datamodels.DataModel`, optional
        This model's WCS is used as a reference.
        WCS. The output coordinate frame, the projection and a
        scaling and rotation transform is created from it. If not supplied
        the first model in the list is used as ``refmodel``.
    transform : `~astropy.modeling.core.Model`, optional
        A transform, passed to :meth:`~gwcs.wcstools.wcs_from_fiducial`
        If not supplied Scaling | Rotation is computed from ``refmodel``.
    bounding_box : tuple, optional
        Bounding_box of the new WCS.
        If not supplied it is computed from the bounding_box of all inputs.
    pscale_ratio : float, optional
        Ratio of input to output pixel scale.
    """
    bb = bounding_box
    wcslist = [im.meta.wcs for im in dmodels]

    if not isiterable(wcslist):
        raise ValueError("Expected 'wcslist' to be an iterable of WCS objects.")

    if not all([isinstance(w, WCS) for w in wcslist]):
        raise TypeError("All items in wcslist are to be instances of gwcs.WCS.")

    if refmodel is None:
        refmodel = dmodels[0]
    else:
        if not isinstance(refmodel, DataModel):
            raise TypeError("Expected refmodel to be an instance of DataModel.")

    fiducial = compute_fiducial(wcslist, bb)
    ref_fiducial = compute_fiducial([refmodel.meta.wcs])

    prj = astmodels.Pix2Sky_TAN()

    if transform is None:
        transform = []
        wcsinfo = pointing.wcsinfo_from_model(refmodel)
        sky_axes, spec, other = gwutils.get_axes(wcsinfo)

        # Need to put the rotation matrix (List[float, float, float, float])
        # returned from calc_rotation_matrix into the correct shape for
        # constructing the transformation
        roll_ref = np.deg2rad(refmodel.meta.wcsinfo.roll_ref)
        v3yangle = np.deg2rad(refmodel.meta.wcsinfo.v3yangle)
        vparity = refmodel.meta.wcsinfo.vparity
        pc = np.reshape(
            calc_rotation_matrix(roll_ref, v3yangle, vparity=vparity),
            (2, 2)
        )

        rotation = astmodels.AffineTransformation2D(pc)
        transform.append(rotation)

        if sky_axes:
            scale = compute_scale(refmodel.meta.wcs, ref_fiducial,
                                  pscale_ratio=pscale_ratio)
            transform.append(astmodels.Scale(scale) & astmodels.Scale(scale))

        if transform:
            transform = functools.reduce(lambda x, y: x | y, transform)

    out_frame = refmodel.meta.wcs.output_frame
    input_frame = dmodels[0].meta.wcs.input_frame
    wnew = wcs_from_fiducial(fiducial, coordinate_frame=out_frame, projection=prj,
                             transform=transform, input_frame=input_frame)

    footprints = [w.footprint().T for w in wcslist]
    domain_bounds = np.hstack([wnew.backward_transform(*f) for f in footprints])

    for axs in domain_bounds:
        axs -= (axs.min() + .5)

    output_bounding_box = []
    for axis in out_frame.axes_order:
        axis_min, axis_max = domain_bounds[axis].min(), domain_bounds[axis].max()
        output_bounding_box.append((axis_min, axis_max))

    output_bounding_box = tuple(output_bounding_box)
    ax1, ax2 = np.array(output_bounding_box)[sky_axes]
    offset1 = (ax1[1] - ax1[0]) / 2
    offset2 = (ax2[1] - ax2[0]) / 2
    offsets = astmodels.Shift(-offset1) & astmodels.Shift(-offset2)

    wnew.insert_transform('detector', offsets, after=True)
    wnew.bounding_box = output_bounding_box

    return wnew
def fpa2asdf(fpafile, outname, ref_kw):
    """
    Create an asdf reference file with the FPA description.

    The CDP2 delivery includes a fits file - "FPA.fpa" which is the
    input to this function. This file is converted to asdf and is a
    reference file of type "FPA".

    nirspec_fs_ref_tools.fpa2asdf('Ref_Files/CoordTransform/Description/FPA.fpa', 'fpa.asdf')

    Parameters
    ----------
    fpafile : str
        A fits file with FPA description (FPA.fpa)
    outname : str
        Name of output ASDF file.
    """
    with open(fpafile) as f:
        lines = [l.strip() for l in f.readlines()]

    # NRS1
    ind = lines.index("*SCA491_PitchX")
    nrs1_pitchx = float(lines[ind + 1])
    ind = lines.index("*SCA491_PitchY")
    nrs1_pitchy = float(lines[ind + 1])
    ind = lines.index("*SCA491_RotAngle")
    nrs1_angle = float(lines[ind + 1])
    ind = lines.index("*SCA491_PosX")
    nrs1_posx = float(lines[ind + 1])
    ind = lines.index("*SCA491_PosY")
    nrs1_posy = float(lines[ind + 1])

    # NRS2
    ind = lines.index("*SCA492_PitchX")
    nrs2_pitchx = float(lines[ind + 1])
    ind = lines.index("*SCA492_PitchY")
    nrs2_pitchy = float(lines[ind + 1])
    ind = lines.index("*SCA492_RotAngle")
    nrs2_angle = float(lines[ind + 1])
    ind = lines.index("*SCA492_PosX")
    nrs2_posx = float(lines[ind + 1])
    ind = lines.index("*SCA492_PosY")
    nrs2_posy = float(lines[ind + 1])

    tree = ref_kw.copy()

    # NRS1 Sky to Detector
    scaling = np.array([[1 / nrs1_pitchx, 0], [0, 1 / nrs1_pitchy]])
    rotmat = models.Rotation2D._compute_matrix(-nrs1_angle)
    matrix = np.dot(rotmat, scaling)
    aff = models.AffineTransformation2D(matrix, name='fpa_affine_sky2detector')
    nrs1_sky2det = models.Shift(-nrs1_posx, name='fpa_shift_x') & \
                 models.Shift(-nrs1_posy, name='fpa_shift_y') | aff

    # NRS1 Detector to Sky
    rotmat = models.Rotation2D._compute_matrix(-nrs1_angle)
    scaling = np.array([[nrs1_pitchx, 0], [0, nrs1_pitchy]])
    matrix = np.dot(rotmat, scaling)
    aff = models.AffineTransformation2D(matrix, name='fpa_affine_detector2sky')
    nrs1_det2sky = aff | models.Shift(nrs1_posx, name='fpa_shift_x_det2sky') & \
                 models.Shift(nrs1_posy, name='fpa_shift_y_det2sky')

    nrs1_det2sky.inverse = nrs1_sky2det

    # NRS2 Sky to Detector
    scaling = np.array([[-1 / nrs2_pitchx, 0], [0, -1 / nrs2_pitchy]])
    rotmat = models.Rotation2D._compute_matrix(-nrs2_angle)
    matrix = np.dot(rotmat, scaling)
    aff = models.AffineTransformation2D(matrix, name='fpa_affine_sky2detector')
    nrs2_sky2det = models.Shift(-nrs2_posx, name='fpa_shixft_x') & \
                 models.Shift(-nrs2_posy, name='fpa_shift_y') | aff

    # NRS2 Detector to Sky
    rotmat = models.Rotation2D._compute_matrix(nrs2_angle)
    scaling = np.array([[-nrs2_pitchx, 0], [0, -nrs2_pitchy]])
    matrix = np.dot(rotmat, scaling)
    aff = models.AffineTransformation2D(matrix, name='fpa_affine_detector2sky')
    nrs2_det2sky = aff | models.Shift(nrs2_posx, name='fpa_shift_x_det2sky') & \
                 models.Shift(nrs2_posy, name='fpa_shift_y_det2sky')

    nrs2_det2sky.inverse = nrs2_sky2det

    tree['NRS1'] = nrs1_det2sky
    tree['NRS2'] = nrs2_det2sky
    fasdf = AsdfFile()
    fasdf.tree = tree
    fasdf.add_history_entry("Build 6")
    fasdf.write_to(outname)
    return fasdf