Exemple #1
0
def _pixel_scale_angle_at_skycoord(skycoord, wcs, offset=1.0 * u.arcsec):
    """
    Calculate the pixel scale and WCS rotation angle at the position of
    a SkyCoord coordinate.

    Parameters
    ----------
    skycoord : `~astropy.coordinates.SkyCoord`
        The SkyCoord coordinate.

    wcs : WCS object
        A world coordinate system (WCS) transformation that supports the
        `astropy shared interface for WCS
        <https://docs.astropy.org/en/stable/wcs/wcsapi.html>`_ (e.g.
        `astropy.wcs.WCS`, `gwcs.wcs.WCS`).

    offset : `~astropy.units.Quantity`
        A small angular offset to use to compute the pixel scale and
        position angle.

    Returns
    -------
    scale : `~astropy.units.Quantity`
        The pixel scale in arcsec/pixel.

    angle : `~astropy.units.Quantity`
        The angle (in degrees) measured counterclockwise from the
        positive x axis to the "North" axis of the celestial coordinate
        system.

    Notes
    -----
    If distortions are present in the image, the x and y pixel scales
    likely differ.  This function computes a single pixel scale along
    the North/South axis.
    """

    try:
        x, y = wcs.world_to_pixel(skycoord)

        # We take a point directly North (i.e. latitude offset) the input
        # sky coordinate and convert it to pixel coordinates, then we use the
        # pixel deltas between the input and offset sky coordinate to
        # calculate the pixel scale and angle.
        skycoord_offset = skycoord.directional_offset_by(0.0, offset)
        x_offset, y_offset = wcs.world_to_pixel(skycoord_offset)
    except AttributeError:
        # for Astropy < 3.1 WCS support
        x, y = skycoord_to_pixel(skycoord, wcs)
        coord = skycoord.represent_as('unitspherical')
        coord_new = UnitSphericalRepresentation(coord.lon, coord.lat + offset)
        coord_offset = skycoord.realize_frame(coord_new)
        x_offset, y_offset = skycoord_to_pixel(coord_offset, wcs)

    dx = x_offset - x
    dy = y_offset - y
    scale = offset.to(u.arcsec) / (np.hypot(dx, dy) * u.pixel)
    angle = (np.arctan2(dy, dx) * u.radian).to(u.deg)

    return scale, angle
Exemple #2
0
    def test_camera_tilt(self):
        """Apply, then remove camera tilt and check that result is same as starting coordinate"""

        param_set = ModelParamSet(None, None, None, None)
        model = MountModel(param_set)

        for _ in range(20):

            params = ModelParameters(
                axis_0_offset=Angle(
                    0 * u.deg),  # does not affect the method under test
                axis_1_offset=Angle(
                    0 * u.deg),  # does not affect the method under test
                pole_rot_axis_az=Angle(
                    0 * u.deg),  # does not affect the method under test
                pole_rot_angle=Angle(
                    0 * u.deg),  # does not affect the method under test
                camera_tilt=Angle(np.random.uniform(-90.0, 90.0) * u.deg),
            )
            model.model_params = params

            coord = UnitSphericalRepresentation(
                np.random.uniform(0.0, 360.0) * u.deg,
                np.random.uniform(-90.0, 90.0) * u.deg)
            meridian_side = random.choice(
                (MeridianSide.WEST, MeridianSide.EAST))
            tilted_coord = model.apply_camera_tilt(coord, meridian_side)
            final_coord = model.remove_camera_tilt(tilted_coord, meridian_side)
            assertSphericalClose(coord, final_coord)
Exemple #3
0
def nominal_to_altaz(norm_coord, altaz_coord):
    """
    Transformation from nominal system to astropy AltAz system

    Parameters
    ----------
    norm_coord: `astropy.coordinates.SkyCoord`
        nominal system
    altaz_coord: `astropy.coordinates.SkyCoord`
        AltAz system

    Returns
    -------
    AltAz Coordinates
    """
    alt_norm, az_norm = norm_coord.array_direction.alt, norm_coord.array_direction.az

    if type(norm_coord.x.value).__module__ != np.__name__:
        x_off = np.zeros(1)
        x_off[0] = norm_coord.x.value
        x_off = x_off * norm_coord.x.unit
        y_off = np.zeros(1)
        y_off[0] = norm_coord.y.value
        y_off = y_off * norm_coord.y.unit
    else:
        x_off = norm_coord.x
        y_off = norm_coord.y

    altitude, azimuth = offset_to_altaz(x_off, y_off, az_norm, alt_norm)

    representation = UnitSphericalRepresentation(lon=azimuth.to(u.deg),
                                                 lat=altitude.to(u.deg))
    return altaz_coord.realize_frame(representation)
Exemple #4
0
    def test_camera_tilt_inverse(self):
        """Remove, then apply camera tilt for reachable positions and check result"""

        param_set = ModelParamSet(None, None, None, None)
        model = MountModel(param_set)

        for _ in range(20):

            tilt = Angle(np.random.uniform(-90.0, 90.0) * u.deg)

            params = ModelParameters(
                axis_0_offset=Angle(
                    0 * u.deg),  # does not affect the method under test
                axis_1_offset=Angle(
                    0 * u.deg),  # does not affect the method under test
                pole_rot_axis_az=Angle(
                    0 * u.deg),  # does not affect the method under test
                pole_rot_angle=Angle(
                    0 * u.deg),  # does not affect the method under test
                camera_tilt=tilt,
            )
            model.model_params = params

            coord = UnitSphericalRepresentation(
                np.random.uniform(0.0, 360.0) * u.deg,
                np.random.uniform(-90.0 + abs(tilt.deg), 90.0 - abs(tilt.deg))
                * u.deg)
            meridian_side = random.choice(
                (MeridianSide.WEST, MeridianSide.EAST))
            untilted_coord = model.remove_camera_tilt(coord, meridian_side)
            final_coord = model.apply_camera_tilt(untilted_coord,
                                                  meridian_side)
            assertSphericalClose(coord, final_coord)
Exemple #5
0
def gcrs_to_geosolarecliptic(gcrs_coo, to_frame):

    if not to_frame.obstime.isscalar:
        raise ValueError(
            "To perform this transformation the obstime Attribute must be a scalar."
        )

    _earth_orbit_perpen_point_gcrs = UnitSphericalRepresentation(
        lon=0 * u.deg,
        lat=(90 * u.deg - _obliquity_rotation_value(to_frame.obstime)))

    _earth_detilt_matrix = _make_rotation_matrix_from_reprs(
        _earth_orbit_perpen_point_gcrs, CartesianRepresentation(0, 0, 1))

    sun_pos_gcrs = get_body("sun", to_frame.obstime).cartesian
    earth_pos_gcrs = get_body("earth", to_frame.obstime).cartesian
    sun_earth = sun_pos_gcrs - earth_pos_gcrs

    sun_earth_detilt = sun_earth.transform(_earth_detilt_matrix)

    # Earth-Sun Line in Geocentric Solar Ecliptic Frame
    x_axis = CartesianRepresentation(1, 0, 0)

    rot_matrix = _make_rotation_matrix_from_reprs(sun_earth_detilt, x_axis)

    return matrix_product(rot_matrix, _earth_detilt_matrix)
Exemple #6
0
def camera_to_telescope(camera_coord, telescope_frame):
    '''
    Transformation between CameraFrame and TelescopeFrame.
    Is called when a SkyCoord is transformed from CameraFrame into TelescopeFrame
    '''
    x_pos = camera_coord.cartesian.x
    y_pos = camera_coord.cartesian.y

    rot = camera_coord.rotation
    if rot == 0:  # if no rotation applied save a few cycles
        x_rotated = x_pos
        y_rotated = y_pos
    else:
        cosrot = np.cos(rot)
        sinrot = np.sin(rot)
        x_rotated = x_pos * cosrot - y_pos * sinrot
        y_rotated = x_pos * sinrot + y_pos * cosrot

    focal_length = camera_coord.focal_length

    # this assumes an equidistant mapping function of the telescope optics
    # or a small angle approximation of most other mapping functions
    # this could be replaced by actually defining the mapping function
    # as an Attribute of `CameraFrame` that maps f(r, focal_length) -> theta,
    # where theta is the angle to the optical axis and r is the distance
    # to the camera center in the focal plane
    delta_alt = u.Quantity((x_rotated / focal_length).to_value(u.dimensionless_unscaled), u.rad)
    delta_az = u.Quantity((y_rotated / focal_length).to_value(u.dimensionless_unscaled), u.rad)

    representation = UnitSphericalRepresentation(lat=delta_alt, lon=delta_az)

    return telescope_frame.realize_frame(representation)
Exemple #7
0
    def setup_grid(self, wavelength=656*u.nm):
        # use HEALPIX to get evenly sized tiles
        NSIDE = hp.npix2nside(self.ntiles)

        colat, lon = hp.pix2ang(NSIDE, np.arange(0, self.ntiles))
        # co-latitude
        theta_values = u.Quantity(colat, unit=u.rad)
        # longitude
        phi_values = u.Quantity(lon, unit=u.rad)

        # the following formulae use the Roche approximation and assume
        # solid body rotation
        # solve for radius of rotating star at these co-latitudes
        if self.distortion:
            radii = self.radius*np.array([newton(surface, 1.01, args=(self.omega, x)) for x in theta_values])
        else:
            radii = self.radius*np.ones(self.ntiles)

        # and effective gravities
        geff = np.sqrt((-const.G*self.mass/radii**2 + self.Omega**2 * radii * np.sin(theta_values)**2)**2 +
                       self.Omega**4 * radii**2 * np.sin(theta_values)**2 * np.cos(theta_values)**2)

        # now make a ntiles sized CartesianRepresentation of positions
        self.tile_locs = SphericalRepresentation(phi_values,
                                                 90*u.deg-theta_values,
                                                 radii).to_cartesian()

        # normal to tile is the direction of the derivate of the potential
        # this is the vector form of geff above
        # the easiest way to express it is that it differs from (r, theta, phi)
        # by a small amount in the theta direction epsilon
        x = radii/self.radius
        a = 1./x**2 - (8./27.)*self.omega**2 * x * np.sin(theta_values)**2
        b = np.sqrt(
                (-1./x**2 + (8./27)*self.omega**2 * x * np.sin(theta_values)**2)**2 +
                ((8./27)*self.omega**2 * x * np.sin(theta_values) * np.cos(theta_values))**2
            )
        epsilon = np.arccos(a/b)
        self.tile_dirs = UnitSphericalRepresentation(phi_values,
                                                     90*u.deg - theta_values - epsilon)
        self.tile_dirs = self.tile_dirs.to_cartesian()

        # and ntiles sized arrays of tile properties
        tile_temperatures = 2000.0 * u.K * (geff / geff.max())**self.beta

        # fluxes, not accounting for limb darkening
        self.tile_scales = np.ones(self.ntiles)
        self.tile_fluxes = blackbody_nu(wavelength, tile_temperatures)

        # tile areas
        spher = self.tile_locs.represent_as(SphericalRepresentation)
        self.tile_areas = spher.distance**2 * hp.nside2pixarea(NSIDE) * u.rad * u.rad

        omega_vec = CartesianRepresentation(
            u.Quantity([0.0, 0.0, self.Omega.value],
                       unit=self.Omega.unit)
        )
        # get velocities of tiles
        self.tile_velocities = cross(omega_vec, self.tile_locs)
Exemple #8
0
def skycoord_to_pixel_scale_angle(skycoord, wcs, small_offset=1 * u.arcsec):
    """
    Convert a set of SkyCoord coordinates into pixel coordinates, pixel
    scales, and position angles.

    Parameters
    ----------
    skycoord : `~astropy.coordinates.SkyCoord`
        Sky coordinates
    wcs : `~astropy.wcs.WCS`
        The WCS transformation to use
    small_offset : `~astropy.units.Quantity`
        A small offset to use to compute the angle

    Returns
    -------
    pixcoord : `~regions.PixCoord`
        Pixel coordinates
    scale : float
        The pixel scale at each location, in degrees/pixel
    angle : `~astropy.units.Quantity`
        The position angle of the celestial coordinate system in pixel space.
    """

    # Convert to pixel coordinates
    x, y = skycoord_to_pixel(skycoord, wcs, mode=skycoord_to_pixel_mode)
    pixcoord = PixCoord(x=x, y=y)

    # We take a point directly 'above' (in latitude) the position requested
    # and convert it to pixel coordinates, then we use that to figure out the
    # scale and position angle of the coordinate system at the location of
    # the points.

    # Find the coordinates as a representation object
    r_old = skycoord.represent_as('unitspherical')

    # Add a a small perturbation in the latitude direction (since longitude
    # is more difficult because it is not directly an angle).
    dlat = small_offset
    r_new = UnitSphericalRepresentation(r_old.lon, r_old.lat + dlat)
    coords_offset = skycoord.realize_frame(r_new)

    # Find pixel coordinates of offset coordinates
    x_offset, y_offset = skycoord_to_pixel(coords_offset,
                                           wcs,
                                           mode=skycoord_to_pixel_mode)

    # Find vector
    dx = x_offset - x
    dy = y_offset - y

    # Find the length of the vector
    scale = np.hypot(dx, dy) / dlat.to('degree').value

    # Find the position angle
    angle = np.arctan2(dy, dx) * u.radian

    return pixcoord, scale, angle
Exemple #9
0
def test_regression_6236():
    # sunpy changes its representation upon initialisation of a frame,
    # including via `realize_frame`. Ensure this works.
    class MyFrame(BaseCoordinateFrame):
        default_representation = CartesianRepresentation
        my_attr = QuantityAttribute(default=0, unit=u.m)

    class MySpecialFrame(MyFrame):
        def __init__(self, *args, **kwargs):
            _rep_kwarg = kwargs.get('representation_type', None)
            super().__init__(*args, **kwargs)
            if not _rep_kwarg:
                self.representation_type = self.default_representation
                self._data = self.data.represent_as(self.representation_type)

    rep1 = UnitSphericalRepresentation([0., 1] * u.deg, [2., 3.] * u.deg)
    rep2 = SphericalRepresentation([10., 11] * u.deg, [12., 13.] * u.deg,
                                   [14., 15.] * u.kpc)
    mf1 = MyFrame(rep1, my_attr=1. * u.km)
    mf2 = mf1.realize_frame(rep2)
    # Normally, data is stored as is, but the representation gets set to a
    # default, even if a different representation instance was passed in.
    # realize_frame should do the same. Just in case, check attrs are passed.
    assert mf1.data is rep1
    assert mf2.data is rep2
    assert mf1.representation_type is CartesianRepresentation
    assert mf2.representation_type is CartesianRepresentation
    assert mf2.my_attr == mf1.my_attr
    # It should be independent of whether I set the reprensentation explicitly
    mf3 = MyFrame(rep1, my_attr=1. * u.km, representation_type='unitspherical')
    mf4 = mf3.realize_frame(rep2)
    assert mf3.data is rep1
    assert mf4.data is rep2
    assert mf3.representation_type is UnitSphericalRepresentation
    assert mf4.representation_type is CartesianRepresentation
    assert mf4.my_attr == mf3.my_attr
    # This should be enough to help sunpy, but just to be sure, a test
    # even closer to what is done there, i.e., transform the representation.
    msf1 = MySpecialFrame(rep1, my_attr=1. * u.km)
    msf2 = msf1.realize_frame(rep2)
    assert msf1.data is not rep1  # Gets transformed to Cartesian.
    assert msf2.data is not rep2
    assert type(msf1.data) is CartesianRepresentation
    assert type(msf2.data) is CartesianRepresentation
    assert msf1.representation_type is CartesianRepresentation
    assert msf2.representation_type is CartesianRepresentation
    assert msf2.my_attr == msf1.my_attr
    # And finally a test where the input is not transformed.
    msf3 = MySpecialFrame(rep1,
                          my_attr=1. * u.km,
                          representation_type='unitspherical')
    msf4 = msf3.realize_frame(rep2)
    assert msf3.data is rep1
    assert msf4.data is not rep2
    assert msf3.representation_type is UnitSphericalRepresentation
    assert msf4.representation_type is CartesianRepresentation
    assert msf4.my_attr == msf3.my_attr
def test_cirs_altaz_nodist(testframe):
    """
    Check that a UnitSphericalRepresentation coordinate round-trips for the
    CIRS<->AltAz transformation.
    """
    coo0 = CIRS(UnitSphericalRepresentation(10*u.deg, 20*u.deg), obstime=testframe.obstime)

    # check that it round-trips
    coo1 = coo0.transform_to(testframe).transform_to(coo0)
    assert_allclose(coo0.cartesian.xyz, coo1.cartesian.xyz)
Exemple #11
0
def skycoord_to_pixel_scale_angle(coords, wcs):
    """
    Convert a set of SkyCoord coordinates into pixel coordinates, pixel
    scales, and position angles.

    Parameters
    ----------
    coords : `~astropy.coordinates.SkyCoord`
        The coordinates to convert
    wcs : `~astropy.wcs.WCS`
        The WCS transformation to use

    Returns
    -------
    x, y : `~numpy.ndarray`
        The x and y pixel coordinates corresponding to the input coordinates
    scale : `~astropy.units.Quantity`
        The pixel scale at each location, in degrees/pixel
    angle : `~astropy.units.Quantity`
        The position angle of the celestial coordinate system in pixel space.
    """

    # Convert to pixel coordinates
    x, y = skycoord_to_pixel(coords, wcs, mode=skycoord_to_pixel_mode)

    # We take a point directly 'above' (in latitude) the position requested
    # and convert it to pixel coordinates, then we use that to figure out the
    # scale and position angle of the coordinate system at the location of
    # the points.

    # Find the coordinates as a representation object
    r_old = coords.represent_as('unitspherical')

    # Add a a small perturbation in the latitude direction (since longitude
    # is more difficult because it is not directly an angle).
    dlat = 1 * u.arcsec
    r_new = UnitSphericalRepresentation(r_old.lon, r_old.lat + dlat)
    coords_offset = coords.realize_frame(r_new)

    # Find pixel coordinates of offset coordinates
    x_offset, y_offset = skycoord_to_pixel(coords_offset,
                                           wcs,
                                           mode=skycoord_to_pixel_mode)

    # Find vector
    dx = x_offset - x
    dy = y_offset - y

    # Find the length of the vector
    scale = np.hypot(dx, dy) * u.pixel / dlat

    # Find the position angle
    angle = np.arctan2(dy, dx) * u.radian

    return x, y, scale, angle
Exemple #12
0
def _convert_wcs(lon_in, lat_in, frame_in, frame_out):
    """Convert (longitude, latitude) coordinates from the input frame to
    the specified output frame.

    Parameters
    ----------
    lon_in : 1D `~numpy.ndarray`
        The longitude to convert, unit degree, [0, 360)
    lat_in : 1D `~numpy.ndarray`
        The latitude to convert, unit degree, [-90, 90]
    frame_in, frame_out : tuple or `~astropy.wcs.WCS`
        The input and output frames, which can be passed either as a tuple of
        ``(frame, lon_unit, lat_unit)`` or as a `~astropy.wcs.WCS` instance.

    Returns
    -------
    lon_out, lat_out : 1D `~numpy.ndarray`
        Output longitude and latitude in the output frame

    References
    ----------
    [1] reproject - wcs_utils.convert_world_coordinates()
        https://github.com/astrofrog/reproject
    """
    if isinstance(frame_in, WCS):
        coordframe_in = wcs_to_celestial_frame(frame_in)
        lon_in_unit = au.Unit(frame_in.wcs.cunit[0])
        lat_in_unit = au.Unit(frame_in.wcs.cunit[1])
    else:
        coordframe_in, lon_in_unit, lat_in_unit = frame_in
    #
    if isinstance(frame_out, WCS):
        coordframe_out = wcs_to_celestial_frame(frame_out)
        lon_out_unit = au.Unit(frame_out.wcs.cunit[0])
        lat_out_unit = au.Unit(frame_out.wcs.cunit[1])
    else:
        coordframe_out, lon_out_unit, lat_out_unit = frame_out
    #
    logger.info("Convert coordinates from {0} to {1}".format(
        coordframe_in, coordframe_out))
    logger.info("Input coordinates units: "
                "{0} (longitude), {1} (latitude)".format(
                    lon_in_unit, lat_in_unit))
    logger.info("Output coordinates units: "
                "{0} (longitude), {1} (latitude)".format(
                    lon_out_unit, lat_out_unit))
    #
    data = UnitSphericalRepresentation(lon_in * lon_in_unit,
                                       lat_in * lat_in_unit)
    coords_in = coordframe_in.realize_frame(data)
    coords_out = coords_in.transform_to(coordframe_out)
    data_out = coords_out.represent_as("unitspherical")
    lon_out = data_out.lon.to(lon_out_unit).value
    lat_out = data_out.lat.to(lon_out_unit).value
    return lon_out, lat_out
Exemple #13
0
def pixel_scale_angle_at_skycoord(skycoord, wcs, offset=1. * u.arcsec):
    """
    Calculate the pixel scale and WCS rotation angle at the position of
    a SkyCoord coordinate.

    Parameters
    ----------
    skycoord : `~astropy.coordinates.SkyCoord`
        The SkyCoord coordinate.
    wcs : `~astropy.wcs.WCS`
        The world coordinate system (WCS) transformation to use.
    offset : `~astropy.units.Quantity`
        A small angular offset to use to compute the pixel scale and
        position angle.

    Returns
    -------
    scale : `~astropy.units.Quantity`
        The pixel scale in arcsec/pixel.
    angle : `~astropy.units.Quantity`
        The angle (in degrees) measured counterclockwise from the
        positive x axis to the "North" axis of the celestial coordinate
        system.

    Notes
    -----
    If distortions are present in the image, the x and y pixel scales
    likely differ.  This function computes a single pixel scale along
    the North/South axis.
    """

    # We take a point directly "above" (in latitude) the input position
    # and convert it to pixel coordinates, then we use the pixel deltas
    # between the input and offset point to calculate the pixel scale and
    # angle.

    # Find the coordinates as a representation object
    coord = skycoord.represent_as('unitspherical')

    # Add a a small perturbation in the latitude direction (since longitude
    # is more difficult because it is not directly an angle)
    coord_new = UnitSphericalRepresentation(coord.lon, coord.lat + offset)
    coord_offset = skycoord.realize_frame(coord_new)

    # Find pixel coordinates of offset coordinates and pixel deltas
    x_offset, y_offset = skycoord_to_pixel(coord_offset, wcs, mode='all')
    x, y = skycoord_to_pixel(skycoord, wcs, mode='all')
    dx = x_offset - x
    dy = y_offset - y

    scale = offset.to(u.arcsec) / (np.hypot(dx, dy) * u.pixel)
    angle = (np.arctan2(dy, dx) * u.radian).to(u.deg)

    return scale, angle
Exemple #14
0
def convert_world_coordinates(lon_in, lat_in, wcs_in, wcs_out):
    """
    Convert longitude/latitude coordinates from an input frame to an output
    frame.

    Parameters
    ----------
    lon_in, lat_in : `~numpy.ndarray`
        The longitude and latitude to convert
    wcs_in, wcs_out : tuple or `~astropy.wcs.WCS`
        The input and output frames, which can be passed either as a tuple of
        ``(frame, lon_unit, lat_unit)`` or as a `~astropy.wcs.WCS` instance.

    Returns
    -------
    lon_out, lat_out : `~numpy.ndarray`
        The output longitude and latitude
    """

    if isinstance(wcs_in, WCS):
        # Extract the celestial component of the WCS in (lon, lat) order
        wcs_in = wcs_in.celestial
        frame_in = wcs_to_celestial_frame(wcs_in)
        lon_in_unit = u.Unit(wcs_in.wcs.cunit[0])
        lat_in_unit = u.Unit(wcs_in.wcs.cunit[1])
    else:
        frame_in, lon_in_unit, lat_in_unit = wcs_in

    if isinstance(wcs_out, WCS):
        # Extract the celestial component of the WCS in (lon, lat) order
        wcs_out = wcs_out.celestial
        frame_out = wcs_to_celestial_frame(wcs_out)
        lon_out_unit = u.Unit(wcs_out.wcs.cunit[0])
        lat_out_unit = u.Unit(wcs_out.wcs.cunit[1])
    else:
        frame_out, lon_out_unit, lat_out_unit = wcs_out

    data = UnitSphericalRepresentation(lon_in * lon_in_unit,
                                       lat_in * lat_in_unit)

    coords_in = frame_in.realize_frame(data)
    coords_out = coords_in.transform_to(frame_out)

    lon_out = coords_out.represent_as('unitspherical').lon.to(
        lon_out_unit).value
    lat_out = coords_out.represent_as('unitspherical').lat.to(
        lat_out_unit).value

    return lon_out, lat_out
Exemple #15
0
    def setup(self):

        self.coord_scalar = SkyCoord(1, 2, unit='deg', frame='icrs')

        lon, lat = np.ones((2, 1000))
        self.coord_array_1e3 = SkyCoord(lon, lat, unit='deg', frame='icrs')

        self.lon_1e6, self.lat_1e6 = np.ones((2, int(1e6)))
        self.coord_array_1e6 = SkyCoord(self.lon_1e6,
                                        self.lat_1e6,
                                        unit='deg',
                                        frame='icrs')

        self.scalar_q_ra = 1 * u.deg
        self.scalar_q_dec = 2 * u.deg

        np.random.seed(12345)
        self.array_q_ra = np.random.rand(int(1e6)) * 360 * u.deg
        self.array_q_dec = (np.random.rand(int(1e6)) * 180 - 90) * u.deg

        self.scalar_repr = UnitSphericalRepresentation(lat=self.scalar_q_dec,
                                                       lon=self.scalar_q_ra)
        self.array_repr = UnitSphericalRepresentation(lat=self.array_q_dec,
                                                      lon=self.array_q_ra)
Exemple #16
0
def latlon_to_cartesian(latitude, longitude, stellar_inclination):
    """
    Convert coordinates in latitude/longitude for a star with a given
    stellar inclination into cartesian coordinates.

    The X-Y plane is the sky plane: x is aligned with the stellar equator, y is
    aligned with the stellar rotation axis.

    Parameters
    ----------
    latitude : float or `~astropy.units.Quantity`
        Spot latitude. Will assume unit=deg if none is specified.
    longitude : float or `~astropy.units.Quantity`
        Spot longitude. Will assume unit=deg if none is specified.
    stellar_inclination : float
        Stellar inclination angle, measured away from the line of sight,
        in [deg].

    Returns
    -------
    cartesian : `~astropy.coordinates.CartesianRepresentation`
        Cartesian representation in the frame described above.
    """

    if not hasattr(longitude, 'unit') and not hasattr(latitude, 'unit'):
        longitude *= u.deg
        latitude *= u.deg

    c = UnitSphericalRepresentation(longitude, latitude)
    cartesian = c.to_cartesian()

    rotate_about_z = rotation_matrix(90 * u.deg, axis='z')
    rotate_is = rotation_matrix(stellar_inclination * u.deg, axis='y')
    transform_matrix = matrix_product(rotate_about_z, rotate_is)
    cartesian = cartesian.transform(transform_matrix)
    return cartesian
Exemple #17
0
def _convert_world_coordinates(lon_in, lat_in, wcs_in, wcs_out):

    frame_in, lon_in_unit, lat_in_unit = wcs_in

    wcs_out = wcs_out.celestial
    frame_out = wcs_to_celestial_frame(wcs_out)
    lon_out_unit = u.Unit(wcs_out.wcs.cunit[0])
    lat_out_unit = u.Unit(wcs_out.wcs.cunit[1])

    data = UnitSphericalRepresentation(lon_in * lon_in_unit,
                                       lat_in * lat_in_unit)

    coords_in = frame_in.realize_frame(data)
    coords_out = coords_in.transform_to(frame_out)

    lon_out = coords_out.represent_as('unitspherical').lon.to(
        lon_out_unit).value
    lat_out = coords_out.represent_as('unitspherical').lat.to(
        lat_out_unit).value

    return lon_out, lat_out
Exemple #18
0
    def transform(self, input_coords):
        """
        Transform one set of coordinates to another
        """
        if self.same_frames:
            return input_coords

        input_coords = input_coords * u.deg
        x_in, y_in = input_coords[:, 0], input_coords[:, 1]

        c_in = SkyCoord(UnitSphericalRepresentation(x_in, y_in),
                        frame=self.input_system)

        # We often need to transform arrays that contain NaN values, and filtering
        # out the NaN values would have a performance hit, so instead we just pass
        # on all values and just ignore Numpy warnings
        with np.errstate(all='ignore'):
            c_out = c_in.transform_to(self.output_system)

        lon = c_out.spherical.lon.deg
        lat = c_out.spherical.lat.deg

        return np.concatenate((lon[:, np.newaxis], lat[:, np.newaxis]), axis=1)
Exemple #19
0
    def test_camera_tilt_unreachable(self):
        """Remove, then apply camera tilt for unreachable positions and check separation"""

        param_set = ModelParamSet(None, None, None, None)
        model = MountModel(param_set)

        for _ in range(20):

            tilt = Angle(np.random.uniform(-90.0, 90.0) * u.deg)

            params = ModelParameters(
                axis_0_offset=Angle(
                    0 * u.deg),  # does not affect the method under test
                axis_1_offset=Angle(
                    0 * u.deg),  # does not affect the method under test
                pole_rot_axis_az=Angle(
                    0 * u.deg),  # does not affect the method under test
                pole_rot_angle=Angle(
                    0 * u.deg),  # does not affect the method under test
                camera_tilt=tilt,
            )
            model.model_params = params

            coord = UnitSphericalRepresentation(
                np.random.uniform(0.0, 360.0) * u.deg,
                random.choice((-1, +1)) *
                np.random.uniform(90.0 - abs(tilt.deg), 90.0) * u.deg)
            meridian_side = random.choice(
                (MeridianSide.WEST, MeridianSide.EAST))
            untilted_coord = model.remove_camera_tilt(coord, meridian_side)
            final_coord = model.apply_camera_tilt(untilted_coord,
                                                  meridian_side)

            separation = SkyCoord(coord).separation(SkyCoord(final_coord))
            expected_separation = np.abs(tilt) - (90 * u.deg -
                                                  np.abs(coord.lat))
            assert_allclose(separation.deg, expected_separation.deg)
Exemple #20
0
def convert_world_coordinates(xw_in, yw_in, wcs_in, wcs_out):
    """
    Convert world coordinates from an input frame to an output frame.

    Parameters
    ----------
    xw_in, yw_in : `~numpy.ndarray`
        The input coordinates to convert
    wcs_in, wcs_out : tuple or `~astropy.wcs.WCS`
        The input and output frames, which can be passed either as a tuple of
        ``(frame, x_unit, y_unit)`` or as a `~astropy.wcs.WCS` instance.
    """

    if isinstance(wcs_in, WCS):
        frame_in = wcs_to_celestial_frame(wcs_in)
        xw_in_unit = u.Unit(wcs_in.wcs.cunit[0])
        yw_in_unit = u.Unit(wcs_in.wcs.cunit[1])
    else:
        frame_in, xw_in_unit, yw_in_unit = wcs_in

    if isinstance(wcs_out, WCS):
        frame_out = wcs_to_celestial_frame(wcs_out)
        xw_out_unit = u.Unit(wcs_out.wcs.cunit[0])
        yw_out_unit = u.Unit(wcs_out.wcs.cunit[1])
    else:
        frame_out, xw_out_unit, yw_out_unit = wcs_out

    data = UnitSphericalRepresentation(xw_in * xw_in_unit, yw_in * yw_in_unit)

    coords_in = frame_in.realize_frame(data)
    coords_out = coords_in.transform_to(frame_out)

    xw_out = coords_out.spherical.lon.to(xw_out_unit).value
    yw_out = coords_out.spherical.lat.to(yw_out_unit).value

    return xw_out, yw_out
Exemple #21
0
 def surface_normal(self, lat, lon):
     usr = UnitSphericalRepresentation(lon=lon+self.rotation_angle, lat=lat)
     rotm = matrix_utilities.rotation_matrix(self.obliquity, axis='x')
     return usr.to_cartesian().transform(rotm)
Exemple #22
0
def test_skycoord(frame):

    c = SkyCoord([[1, 2], [3, 4]], [[5, 6], [7, 8]],
                 unit='deg', frame=frame,
                 obstime=Time('2016-01-02'),
                 location=EarthLocation(1000, 2000, 3000, unit=u.km))
    cy = load(dump(c))
    compare_coord(c, cy)


@pytest.mark.parametrize('rep', [
    CartesianRepresentation(1*u.m, 2.*u.m, 3.*u.m),
    SphericalRepresentation([[1, 2], [3, 4]]*u.deg,
                            [[5, 6], [7, 8]]*u.deg,
                            10*u.pc),
    UnitSphericalRepresentation(0*u.deg, 10*u.deg),
    SphericalCosLatDifferential([[1.], [2.]]*u.mas/u.yr,
                                [4., 5.]*u.mas/u.yr,
                                [[[10]], [[20]]]*u.km/u.s),
    CartesianDifferential([10, 20, 30]*u.km/u.s),
    CartesianRepresentation(
        [1, 2, 3]*u.m,
        differentials=CartesianDifferential([10, 20, 30]*u.km/u.s)),
    SphericalRepresentation(
        [[1, 2], [3, 4]]*u.deg, [[5, 6], [7, 8]]*u.deg, 10*u.pc,
        differentials={
            's': SphericalDifferential([[0., 1.], [2., 3.]]*u.mas/u.yr,
                                       [[4., 5.], [6., 7.]]*u.mas/u.yr,
                                       10*u.km/u.s)})])
def test_representations(rep):
    rrep = load(dump(rep))
Exemple #23
0
    def spherical_to_cartesian(self,
                               spot_lons,
                               spot_lats,
                               inc_stellar,
                               times=None,
                               planet=None,
                               time_ref=None):
        """
        Convert spot parameter matrices in the original stellar coordinates to
        rotated and tilted cartesian coordinates.

        Parameters
        ----------
        spot_lons : `~astropy.units.Quantity`
            Spot longitudes
        spot_lats : `~astropy.units.Quantity`
            Spot latitudes
        inc_stellar : `~astropy.units.Quantity`
            Stellar inclination
        times : `~numpy.ndarray`
            Times at which evaluate the stellar rotation
        planet : `~batman.TransitParams`
            Planet parameters
        time_ref : float
            Reference time used as the initial rotational phase of the star,
            such that the sub-observer point is at zero longitude at
            ``time_ref``.

        Returns
        -------
        tilted_spots : `~numpy.ndarray`
            Rotated and tilted spot positions in cartesian coordinates
        """
        # Spots by default are given in unit spherical representation (lat, lon)
        usr = UnitSphericalRepresentation(spot_lons, spot_lats)

        # Represent those spots with cartesian coordinates (x, y, z)
        # In this coordinate system, the observer is at positive x->inf,
        # the star is at the origin, and (y, z) is the sky plane.
        cartesian = usr.represent_as(CartesianRepresentation)

        # Generate array of rotation matrices to rotate the spots about the
        # stellar rotation axis
        if times is None or (hasattr(times, '__len__') and times[0] is None):
            rotate = rotation_matrix(self.phases[:, np.newaxis, np.newaxis],
                                     axis='z')
        else:
            if time_ref is None:
                time_ref = 0
            rotational_phase = 2 * np.pi * (
                (times - time_ref) / self.rotation_period) * u.rad
            rotate = rotation_matrix(rotational_phase[:, np.newaxis,
                                                      np.newaxis],
                                     axis='z')

        rotated_spots = cartesian.transform(rotate)

        if planet is not None and hasattr(planet, 'lam'):
            lam = planet.lam * u.deg
        else:
            lam = 0 * u.deg

        # Generate array of rotation matrices to rotate the spots so that the
        # star is observed from the correct stellar inclination
        stellar_inclination = rotation_matrix(inc_stellar - 90 * u.deg,
                                              axis='y')
        inclined_spots = rotated_spots.transform(stellar_inclination)

        # Generate array of rotation matrices to rotate the spots so that the
        # planet's orbit normal is tilted with respect to stellar spin
        tilt = rotation_matrix(-lam, axis='x')
        tilted_spots = inclined_spots.transform(tilt)

        return tilted_spots
Exemple #24
0
class Star:

    mass = AffectsOmegaCrit('mass', u.kg)
    radius = AffectsOmegaCrit('radius', u.m)
    beta = ResetsGrid('beta')
    distortion = ResetsGrid('distortion')

    @u.quantity_input(mass=u.kg)
    @u.quantity_input(radius=u.m)
    @u.quantity_input(period=u.s)
    def __init__(self, mass, radius, period, beta=0.08, ulimb=0.9,
                 ntiles=3072, distortion=True):
        self.distortion = distortion
        self.mass = mass
        self.radius = radius  # will also set self.omega_crit
        self.beta = beta
        self.ulimb = ulimb
        self.ntiles = ntiles

        self.period = period
        self.clear_grid()

    def clear_grid(self):
        # now set up tile locations, velocities and directions.
        # These will be (nlon, nlat) CartesianRepresentations
        self.tile_locs = None
        self.tile_dirs = None
        self.tile_velocities = None

        # and arrays of tile properties - shape (nlon, nlat)
        self.tile_areas = None
        self.tile_fluxes = None

        # the next array is the main one that gets tweaked
        self.tile_scales = None

    """
    We define many of the attributes as properties, so we can wipe the grid when they are set,
    and also check for violation of critical rotation
    """
    @property
    def omega(self):
        """
        Ratio of angular velocity to critical number. read-only property
        """
        return (self.Omega/self.omega_crit).decompose()

    @property
    def period(self):
        return 2.0*np.pi/self.Omega

    @period.setter
    @u.quantity_input(value=u.s)
    def period(self, value):
        Omega = 2.0*np.pi/value
        if (Omega/self.omega_crit).decompose() > 1:
            raise ValueError('This rotation period exceeds critical value')
        self.Omega = Omega
        self.clear_grid()

    """
    ntiles is also a property, since we need to remap to an appropriate
    value for HEALPIX.
    """
    @property
    def ntiles(self):
        """
        Number of tiles.

        Is checked to see if appropriate for HEALPIX algorithm.
        """
        return self._ntiles

    @ntiles.setter
    def ntiles(self, value):
        allowed_values = [48, 192, 768, 3072, 12288, 49152, 196608]
        if int(value) not in allowed_values:
            raise ValueError('{} not one of allowed values: {!r}'.format(
                value, allowed_values
            ))
        self._ntiles = int(12*np.floor(np.sqrt(value/12.))**2)
        self.clear_grid()

    @u.quantity_input(wavelength=u.nm)
    def setup_grid(self, wavelength=656*u.nm):
        # use HEALPIX to get evenly sized tiles
        NSIDE = hp.npix2nside(self.ntiles)

        colat, lon = hp.pix2ang(NSIDE, np.arange(0, self.ntiles))
        # co-latitude
        theta_values = u.Quantity(colat, unit=u.rad)
        # longitude
        phi_values = u.Quantity(lon, unit=u.rad)

        # the following formulae use the Roche approximation and assume
        # solid body rotation
        # solve for radius of rotating star at these co-latitudes
        if self.distortion:
            radii = self.radius*np.array([newton(surface, 1.01, args=(self.omega, x)) for x in theta_values])
        else:
            radii = self.radius*np.ones(self.ntiles)

        # and effective gravities
        geff = np.sqrt((-const.G*self.mass/radii**2 + self.Omega**2 * radii * np.sin(theta_values)**2)**2 +
                       self.Omega**4 * radii**2 * np.sin(theta_values)**2 * np.cos(theta_values)**2)

        # now make a ntiles sized CartesianRepresentation of positions
        self.tile_locs = SphericalRepresentation(phi_values,
                                                 90*u.deg-theta_values,
                                                 radii).to_cartesian()

        # normal to tile is the direction of the derivate of the potential
        # this is the vector form of geff above
        # the easiest way to express it is that it differs from (r, theta, phi)
        # by a small amount in the theta direction epsilon
        x = radii/self.radius
        a = 1./x**2 - (8./27.)*self.omega**2 * x * np.sin(theta_values)**2
        b = np.sqrt(
                (-1./x**2 + (8./27)*self.omega**2 * x * np.sin(theta_values)**2)**2 +
                ((8./27)*self.omega**2 * x * np.sin(theta_values) * np.cos(theta_values))**2
            )
        epsilon = np.arccos(a/b)
        self.tile_dirs = UnitSphericalRepresentation(phi_values,
                                                     90*u.deg - theta_values - epsilon)
        self.tile_dirs = self.tile_dirs.to_cartesian()

        # and ntiles sized arrays of tile properties
        tile_temperatures = 2000.0 * u.K * (geff / geff.max())**self.beta

        # fluxes, not accounting for limb darkening
        self.tile_scales = np.ones(self.ntiles)
        self.tile_fluxes = blackbody_nu(wavelength, tile_temperatures)

        # tile areas
        spher = self.tile_locs.represent_as(SphericalRepresentation)
        self.tile_areas = spher.distance**2 * hp.nside2pixarea(NSIDE) * u.rad * u.rad

        omega_vec = CartesianRepresentation(
            u.Quantity([0.0, 0.0, self.Omega.value],
                       unit=self.Omega.unit)
        )
        # get velocities of tiles
        self.tile_velocities = cross(omega_vec, self.tile_locs)

    @u.quantity_input(inclination=u.deg)
    def plot(self, inclination=90*u.deg, phase=0.0, savefig=False, filename='star_surface.png',
             cmap='magma', what='fluxes', cstride=1, rstride=1, shade=False):
        ax = plt.axes(projection='3d')
        ax.view_init(90-inclination.to(u.deg).value, 360*phase)

        # get map values
        if what == 'fluxes':
            vals = self.tile_fluxes * self.tile_scales
            vals = vals / vals.max()
        elif what == 'vels':
            earth = set_earth(inclination.to(u.deg).value, phase)
            velocities = self.tile_velocities.xyz
            vals = dot(earth, velocities).to(u.km/u.s)
            # can't plot negative values, so rescale from 0 - 1
            vals = (vals - vals.min())/(vals.max()-vals.min())
        elif what == 'areas':
            vals = self.tile_areas / self.tile_areas.max()
        colors = getattr(cm, cmap)(vals.value)

        # project the map to a rectangular matrix
        nlat = nlon = int(np.floor(np.sqrt(self.ntiles)))
        theta = np.linspace(np.pi, 0, nlat)
        phi = np.linspace(-np.pi, np.pi, nlon)
        PHI, THETA = np.meshgrid(phi, theta)
        NSIDE = hp.npix2nside(self.ntiles)
        grid_pix = hp.ang2pix(NSIDE, THETA, PHI)
        grid_map = colors[grid_pix]

        # Create a sphere
        r = 0.3
        x = r*np.sin(THETA)*np.cos(PHI)
        y = r*np.sin(THETA)*np.sin(PHI)
        z = r*np.cos(THETA)

        ax.plot_surface(x, y, z, cstride=cstride, rstride=rstride, facecolors=grid_map,
                        shade=shade)
        if savefig:
            plt.savefig(filename)
        else:
            plt.show()

    @u.quantity_input(inclination=u.deg)
    def view(self, inclination=90*u.deg, phase=0.0, what='fluxes',
             projection='mollweide', cmap='magma',
             savefig=False, filename='star_surface.png',
             dlat=30, dlon=30, **kwargs):
        rot = (360*phase, 90-inclination.to(u.deg).value, 0)
        if what == 'fluxes':
            vals = self.tile_fluxes * self.tile_scales
            vals = vals / vals.max()
        elif what == 'areas':
            vals = self.tile_areas / self.tile_areas.max()

        if 'mollweide'.find(projection) == 0:
            hp.mollview(vals, rot=rot, cmap=cmap, **kwargs)
        elif 'cartesian'.find(projection) == 0:
            hp.cartview(vals, rot=rot, cmap=cmap, **kwargs)
        elif 'orthographic'.find(projection) == 0:
            hp.orthview(vals, rot=rot, cmap=cmap, **kwargs)
        else:
            raise ValueError('Unrecognised projection')
        hp.graticule(dlat, dlon)
        if savefig:
            plt.savefig(filename)
        else:
            plt.show()

    @u.quantity_input(inclination=u.deg)
    def _luminosity_array(self, phase, inclination):
        if self.tile_locs is None:
            self.setup_grid()

        # get CartesianRepresentation pointing to earth at these phases
        earth = set_earth(inclination, phase)

        mu = dot(earth, self.tile_dirs, normalise=True)
        # mask of visible elements
        mask = mu >= 0.0

        # broadcast and calculate
        phase = np.asarray(phase)
        new_shape = phase.shape + self.tile_fluxes.shape
        assert(new_shape == mu.shape), "Broadcasting has gone wrong"

        fluxes = np.tile(self.tile_fluxes, phase.size).reshape(new_shape)
        scales = np.tile(self.tile_scales, phase.size).reshape(new_shape)
        areas = np.tile(self.tile_areas, phase.size).reshape(new_shape)

        # limb darkened sum of all tile fluxes
        lum = (fluxes * scales * (1.0 - self.ulimb + np.fabs(mu)*self.ulimb) *
               areas * mu)
        # no contribution from invisible tiles
        lum[mask] = 0.0
        return lum

    @u.quantity_input(inclination=u.deg)
    def calc_luminosity(self, phase, inclination):
        lum = self._luminosity_array(phase, inclination)
        return np.sum(lum, axis=1)

    @u.quantity_input(inclination=u.deg)
    @u.quantity_input(v_macro=u.km/u.s)
    @u.quantity_input(v_inst=u.km/u.s)
    @u.quantity_input(v_min=u.km/u.s)
    @u.quantity_input(v_max=u.km/u.s)
    def calc_line_profile(self, phase, inclination, nbins=100,
                          v_macro=2*u.km/u.s, v_inst=4*u.km/u.s,
                          v_min=-40*u.km/u.s, v_max=40*u.km/u.s):

        # get CartesianRepresentation pointing to earth at these phases
        earth = set_earth(inclination, phase)
        # get CartesianRepresentation of projected velocities
        vproj = dot(earth, self.tile_velocities).to(u.km/u.s)

        # which tiles fall in which bin?
        bins = np.linspace(v_min, v_max, nbins)
        indices = np.digitize(vproj, bins)

        lum = self._luminosity_array(phase, inclination)
        phase = np.asarray(phase)
        trailed_spectrum = np.zeros((phase.size, nbins))

        for i in range(nbins):
            mask = (indices == i)
            trailed_spectrum[:, i] = np.sum(lum*mask, axis=1)

        # convolve with instrumental and local line profiles
        # TODO: realistic Line Profile Treatment
        # For now we assume every element has same intrinsic
        # line profile
        bin_width = (v_max-v_min)/(nbins-1)
        profile_width_in_bins = np.sqrt(v_macro**2 + v_inst**2) / bin_width
        gauss_kernel = Gaussian1DKernel(stddev=profile_width_in_bins, mode='linear_interp')
        for i in range(phase.size):
            trailed_spectrum[i, :] = convolve(trailed_spectrum[i, :], gauss_kernel, boundary='extend')

        return bins, trailed_spectrum
Exemple #25
0
    def __init__(self, center, length=None, angle=None, sample=2, width=None):

        # Check input types

        if not isinstance(center, (SkyCoord, BaseCoordinateFrame)):
            raise TypeError(
                "The central position should be given as a SkyCoord object")

        if not isinstance(length, u.Quantity) or not length.unit.is_equivalent(
                u.deg):
            raise TypeError(
                "The length should be given as an angular Quantity")

        if not isinstance(angle, u.Quantity) or not angle.unit.is_equivalent(
                u.deg):
            raise TypeError("The angle should be given as an angular Quantity")

        # We set up the path by adding and removing half the length along the
        # declination axis, then rotate the resulting two points around the
        # center.

        # Convert the central position to cartesian coordinates
        c1, c2, c3 = center.cartesian.xyz.value

        # Find the end points of the path
        clon, clat = center.spherical.lon, center.spherical.lat
        try:
            plat = clat + np.linspace(-length * 0.5, length * 0.5, sample)
        except ValueError:  # Numpy 1.10+
            plat = clat + np.linspace(-length.value * 0.5, length.value * 0.5,
                                      sample) * length.unit

        x, y, z = UnitSphericalRepresentation(clon,
                                              plat).to_cartesian().xyz.value

        # Rotate around central point

        # Because longitude increases to the left, we have to take -angle
        angle = -angle

        # We rotate (x,y,z) around (c1,c2,c3) by making use of the following
        # equations:
        #
        # x' = x cos a + (1 - cos a)(c1c1x + c1c2y + c1c3z) + (c2z - c3y)sin a
        # y' = y cos a + (1 - cos a)(c2c1x + c2c2y + c2c3z) + (c3x - c1z)sin a
        # z' = z cos a + (1 - cos a)(c3c1x + c3c2y + c3c3z) + (c1y - c2x)sin a
        #
        # Source: https://www.uwgb.edu/dutchs/MATHALGO/sphere0.htm

        cosa = np.cos(angle)
        sina = np.sin(angle)

        xd = x * cosa + (1 - cosa) * (c1 * c1 * x + c1 * c2 * y +
                                      c1 * c3 * z) + (c2 * z - c3 * y) * sina
        yd = y * cosa + (1 - cosa) * (c2 * c1 * x + c2 * c2 * y +
                                      c2 * c3 * z) + (c3 * x - c1 * z) * sina
        zd = z * cosa + (1 - cosa) * (c3 * c1 * x + c3 * c2 * y +
                                      c3 * c3 * z) + (c1 * y - c2 * x) * sina

        # Construct representations
        points = center.realize_frame(CartesianRepresentation(x=xd, y=yd,
                                                              z=zd))

        super(PathFromCenter, self).__init__(points, width=width)
Exemple #26
0
        return frame(*args)
    elif kwargs:
        return frame(**kwargs)


"""
These are common 2D params, kwargs are frame specific
"""
two_D_parameters = [([0 * u.deg, 0 * u.arcsec], None),
                    ([0 * u.deg, 0 * u.arcsec], {
                        'obstime': '2011/01/01T00:00:00'
                    }),
                    ([0 * u.deg, 0 * u.arcsec], {
                        'representation': 'unitspherical'
                    }),
                    ([UnitSphericalRepresentation(0 * u.deg,
                                                  0 * u.arcsec)], None),
                    ([UnitSphericalRepresentation(0 * u.deg,
                                                  0 * u.arcsec)], None),
                    ([UnitSphericalRepresentation(0 * u.deg, 0 * u.arcsec)], {
                        'obstime': '2011/01/01T00:00:00'
                    })]
"""
These are common 3D params, kwargs are frame specific
"""
three_D_parameters = [
    ([0 * u.deg, 0 * u.arcsec, 1 * u.Mm], None),
    ([0 * u.deg, 0 * u.arcsec, 1 * u.Mm], {
        'obstime': '2011/01/01T00:00:00'
    }), ([0 * u.deg, 0 * u.arcsec, 1 * u.Mm], {
        'representation': 'spherical'
    }), ([SphericalRepresentation(0 * u.deg, 0 * u.arcsec, 1 * u.Mm)], None),
Exemple #27
0
def init_frame(frame, args, kwargs):
    if args and kwargs:
        return frame(*args, **kwargs)
    elif args:
        return frame(*args)
    elif kwargs:
        return frame(**kwargs)


"""
These are common 2D params, kwargs are frame specific
"""
two_D_parameters = [
    ([0 * u.deg, 0 * u.arcsec], None),
    ([0 * u.deg, 0 * u.arcsec], {'obstime': '2011/01/01T00:00:00'}),
    ([UnitSphericalRepresentation(0 * u.deg, 0 * u.arcsec)], None),
    ([UnitSphericalRepresentation(0 * u.deg, 0 * u.arcsec)], {'obstime': '2011/01/01T00:00:00'}),
    ([0 * u.deg, 0 * u.arcsec], {'representation_type': 'unitspherical'})
]
"""
These are common 3D params, kwargs are frame specific
"""
three_D_parameters = [
    ([0 * u.deg, 0 * u.arcsec, 1 * u.Mm], None),
    ([0 * u.deg, 0 * u.arcsec, 1 * u.Mm], {'obstime': '2011/01/01T00:00:00'}),
    ([0 * u.deg, 0 * u.arcsec, 1 * u.Mm], {'representation_type': 'spherical'}),
    ([SphericalRepresentation(0 * u.deg, 0 * u.arcsec, 1 * u.Mm)],
     None),
    ([SphericalRepresentation(0 * u.deg, 0 * u.arcsec, 1 * u.Mm)], None), (
        [SphericalRepresentation(0 * u.deg, 0 * u.arcsec, 1 * u.Mm)],
        {'obstime': '2011/01/01T00:00:00'})
Exemple #28
0
def pixel_to_skycoord(xp, yp, wcs, origin=0, mode='all', cls=None):
    """
    Convert a set of pixel coordinates into a `~astropy.coordinates.SkyCoord`
    coordinate.

    Parameters
    ----------
    xp, yp : float or `numpy.ndarray`
        The coordinates to convert.
    wcs : `~astropy.wcs.WCS`
        The WCS transformation to use.
    origin : int
        Whether to return 0 or 1-based pixel coordinates.
    mode : 'all' or 'wcs'
        Whether to do the transformation including distortions (``'all'``) or
        only including only the core WCS transformation (``'wcs'``).
    cls : class or None
        The class of object to create.  Should be a
        `~astropy.coordinates.SkyCoord` subclass.  If None, defaults to
        `~astropy.coordinates.SkyCoord`.

    Returns
    -------
    coords : Whatever ``cls`` is (a subclass of `~astropy.coordinates.SkyCoord`)
        The celestial coordinates

    See Also
    --------
    astropy.coordinates.SkyCoord.from_pixel
    """

    # Import astropy.coordinates here to avoid circular imports
    from astropy.coordinates import SkyCoord, UnitSphericalRepresentation

    # we have to do this instead of actually setting the default to SkyCoord
    # because importing SkyCoord at the module-level leads to circular
    # dependencies.
    if cls is None:
        cls = SkyCoord

    if _has_distortion(wcs) and wcs.naxis != 2:
        raise ValueError(
            "Can only handle WCS with distortions for 2-dimensional WCS")

    # Keep only the celestial part of the axes, also re-orders lon/lat
    wcs = wcs.sub([WCSSUB_LONGITUDE, WCSSUB_LATITUDE])

    if wcs.naxis != 2:
        raise ValueError("WCS should contain celestial component")

    # Check which frame the WCS uses
    frame = wcs_to_celestial_frame(wcs)

    # Check what unit the WCS gives
    lon_unit = u.Unit(wcs.wcs.cunit[0])
    lat_unit = u.Unit(wcs.wcs.cunit[1])

    # Convert pixel coordinates to celestial coordinates
    if mode == 'all':
        lon, lat = wcs.all_pix2world(xp, yp, origin)
    elif mode == 'wcs':
        lon, lat = wcs.wcs_pix2world(xp, yp, origin)
    else:
        raise ValueError("mode should be either 'all' or 'wcs'")

    # Add units to longitude/latitude
    lon = lon * lon_unit
    lat = lat * lat_unit

    # Create a SkyCoord-like object
    data = UnitSphericalRepresentation(lon=lon, lat=lat)
    coords = cls(frame.realize_frame(data))

    return coords
Exemple #29
0
def plot_reachable_zone(
        ax: matplotlib.axes.Axes,
        mount_model: MountModel,
        axis_0_west_limit: float = 110,
        axis_0_east_limit: float = 110,
    ) -> None:
    """Plot area(s) of sky reachable by the mount.

    This only accounts for what area of the sky is reachable. It does not take into account whether
    the mount can keep up with a specific target's trajectory. Some trajectories, especially
    those that pass near the mount pole, may require slew rates that exceed what the mount is
    capable of.

    This function assumes an equatorial mount with limits on the right ascension axis.

    Args:
        ax: Axes object this function will plot on. This should be generated by `make_sky_plot()`.
        mount_model: Mount model from which this plot will be generated.
        axis_0_west_limit: Western limit on axis 0 in degrees from the meridian.
        axis_0_east_limit: Eastern limit on axis 0 in degrees from the meridian.
    """
    if axis_0_west_limit < 90 or axis_0_east_limit < 90:
        # current logic would not shade the correct regions of the polar plot
        raise ValueError('Axis limits less than 90 degrees from meridian are not supported')

    # convert from arg values to encoder position angles
    axis_0_west_limit = 180 - axis_0_west_limit
    axis_0_east_limit = 180 + axis_0_east_limit

    # place a dot at the position of the mount pole
    mount_pole_topo = mount_model.spherical_to_topocentric(
        UnitSphericalRepresentation(
            lon=0*u.deg,
            lat=90*u.deg,
        )
    )
    ax.plot(np.radians(mount_pole_topo.az.deg), 90.0 - mount_pole_topo.alt.deg, 'k.')

    for meridian_side in MeridianSide:
        if meridian_side == MeridianSide.EAST:
            color = 'blue'
            legend_label = 'east of mount meridian'
            axis_1_range = np.linspace(0, 180, 100) + mount_model.model_params.axis_1_offset.deg
            az = np.linspace(mount_pole_topo.az.deg, mount_pole_topo.az.deg + 180, 100)
        else:
            axis_1_range = np.linspace(180, 360, 100) + mount_model.model_params.axis_1_offset.deg
            color = 'red'
            legend_label = 'west of mount meridian'
            az = np.linspace(mount_pole_topo.az.deg - 180, mount_pole_topo.az.deg, 100)

        # add a circle patch outside the visible area of the plot purely for the purpose of
        # generating an entry in the legend for this region
        ax.add_patch(Circle((0, 100), radius=0, color=color, alpha=0.2, label=legend_label))

        alt = 90*np.ones_like(az)
        fill_to_horizon(ax, az, alt, color=color)

        for axis_0 in (axis_0_west_limit, axis_0_east_limit):
            az = []
            alt = []
            for axis_1 in axis_1_range:
                topo = mount_model.encoders_to_topocentric(
                    MountEncoderPositions(
                        Longitude(axis_0*u.deg),
                        Longitude(axis_1*u.deg),
                    )
                )
                az.append(topo.az.deg)
                alt.append(topo.alt.deg)
            az = np.array(az)
            alt = np.array(alt)
            ax.plot(np.radians(az), 90.0 - alt, ':', color=color)

            if axis_0 == axis_0_east_limit and meridian_side == MeridianSide.EAST:
                fill_to_horizon(ax, az, alt, color=color)
            elif axis_0 == axis_0_west_limit and meridian_side == MeridianSide.EAST:
                fill_to_zenith(ax, az, alt, color=color)
            elif axis_0 == axis_0_east_limit and meridian_side == MeridianSide.WEST:
                fill_to_zenith(ax, az, alt, color=color)
            elif axis_0 == axis_0_west_limit and meridian_side == MeridianSide.WEST:
                fill_to_horizon(ax, az, alt, color=color)