Exemplo n.º 1
0
def test_hgs_hcrs():
    # This test checks the HGS->HCRS transformation by transforming from HGS to
    # HeliocentricTrueEcliptic (HTE).  It will fail if there are errors in Astropy's
    # HCRS->ICRS or ICRS->HTE transformations.

    # Use published HGS coordinates in the Astronomical Almanac (2013), pages C6-C7
    obstime = Time('2013-01-28')
    earth_hgs = SkyCoord(0 * u.deg,
                         -5.73 * u.deg,
                         0.9848139 * u.AU,
                         frame=HeliographicStonyhurst,
                         obstime=obstime)

    # Transform to HTE at observation-time equinox
    earth_hte = earth_hgs.transform_to(
        HeliocentricTrueEcliptic(equinox=obstime))

    # Validate against published values from the Astronomical Almanac (2013), page C6 per page E2
    # The dominant source of inaccuracy is the limited precision of the published B0 used above
    assert quantity_allclose(earth_hte.lon,
                             Angle('308d13m30.51s') - 180 * u.deg,
                             atol=5 * u.arcsec)
    assert quantity_allclose(earth_hte.lat,
                             -Angle('-0.27s'),
                             atol=10 * u.arcsec)
    assert quantity_allclose(earth_hte.distance,
                             0.9848139 * u.AU,
                             atol=5e-7 * u.AU)
def test_ephemerides():
    """
    We test that using different ephemerides gives very similar results
    for transformations
    """
    t = Time("2014-12-25T07:00")
    moon = SkyCoord(
        GCRS(318.10579159 * u.deg,
             -11.65281165 * u.deg,
             365042.64880308 * u.km,
             obstime=t))

    icrs_frame = ICRS()
    hcrs_frame = HCRS(obstime=t)
    ecl_frame = HeliocentricTrueEcliptic(equinox=t)
    cirs_frame = CIRS(obstime=t)

    moon_icrs_builtin = moon.transform_to(icrs_frame)
    moon_hcrs_builtin = moon.transform_to(hcrs_frame)
    moon_helioecl_builtin = moon.transform_to(ecl_frame)
    moon_cirs_builtin = moon.transform_to(cirs_frame)

    with solar_system_ephemeris.set('jpl'):
        moon_icrs_jpl = moon.transform_to(icrs_frame)
        moon_hcrs_jpl = moon.transform_to(hcrs_frame)
        moon_helioecl_jpl = moon.transform_to(ecl_frame)
        moon_cirs_jpl = moon.transform_to(cirs_frame)

    # most transformations should differ by an amount which is
    # non-zero but of order milliarcsecs
    sep_icrs = moon_icrs_builtin.separation(moon_icrs_jpl)
    sep_hcrs = moon_hcrs_builtin.separation(moon_hcrs_jpl)
    sep_helioecl = moon_helioecl_builtin.separation(moon_helioecl_jpl)
    sep_cirs = moon_cirs_builtin.separation(moon_cirs_jpl)

    assert_allclose([sep_icrs, sep_hcrs, sep_helioecl],
                    0.0 * u.deg,
                    atol=10 * u.mas)
    assert all(sep > 10 * u.microarcsecond
               for sep in (sep_icrs, sep_hcrs, sep_helioecl))

    # CIRS should be the same
    assert_allclose(sep_cirs, 0.0 * u.deg, atol=1 * u.microarcsecond)
Exemplo n.º 3
0
def init_sky(projection='eck4', ra_center=120, galactic_plane_color='red',
             ecliptic_color='red', ra_labels=np.arange(0, 360, 60),
             dec_labels=np.arange(-60, 90, 30), ax=None):
    """Initialize a basemap projection of the full sky.

    The returned Basemap object is augmented with an ``ellipse()`` method to
    support drawing ellipses or circles on the sky, which is useful for
    representing DESI tiles.

    Note that the projection uses the geographic convention that RA increases
    from left to right rather than the opposite celestial convention because
    otherwise RA labels are drawn incorrectly (see
    https://github.com/matplotlib/basemap/issues/283 for details).

    The DESI footprint would look better with a projection centered at DEC ~ 15,
    which should be possible with basemap but is not current working (see
    https://github.com/matplotlib/basemap/issues/192).

    Requires that matplotlib and basemap are installed.

    Parameters
    ----------
    projection : :class: `string`, optional
        All-sky projection used for coordinate transformations. The default
        'eck4' is recommended for the reasons given `here
        <http://usersguidetotheuniverse.com/index.php/2011/03/03/
        whats-the-best-map-projection/>`__.  Other good choices are
        kav7' and 'moll'.
    ra_center : float
        Map is centered at this RA in degrees. Default is +120, which
        avoids splitting the DESI northern and southern regions.
    galactic_plane_color : color name or None
        Draw a curve representing the galactic plane using the specified
        color, or do nothing when None.
    ecliptic_color : color name or None
        Draw a dotted curve representing the ecliptic plane using the specified
        color, or do nothing when None.
    ra_labels : iterable or None
        List of RA values in degrees that will be labeled on the map.
        If None, there will be no RA labels or grid lines.
    dec_labels : iterable or None
        List of DEC values in degrees that will be labeled on the map.
        If None, there will be no DEC labels or grid lines.
    ax : matplotlib axis objet or None
        Axes to use for drawing this map, or use current axes if None.

    Returns
    -------
    :class:`mpl_toolkits.basemap.Basemap`
       The Basemap object created for this plot, which can be used for
       additional projection and plotting operations.
    """
    import matplotlib
    if 'TRAVIS_JOB_ID' in os.environ:
        matplotlib.use('agg')
    from matplotlib.patches import Polygon
    from mpl_toolkits.basemap import pyproj
    from mpl_toolkits.basemap import Basemap
    from astropy.coordinates import SkyCoord, HeliocentricTrueEcliptic, ICRS
    import astropy.units as u

    # Define a Basemap subclass with an ellipse() method.
    class BasemapWithEllipse(Basemap):
        """Code from http://stackoverflow.com/questions/8161144/
        drawing-ellipses-on-matplotlib-basemap-projections
        It adds ellipses to the class Basemap.
        """
        def ellipse(self, x0, y0, a, b, n, ax=None, **kwargs):
            """Extension to Basemap class from `basemap` to draw ellipses.

            Parameters
            ----------
            x0 : :class: `float`
                Centroid of the ellipse in the X axis.
            y0 : :class: `float`
                Centroid of the ellipse in the Y axis.
            a : :class: `float`
                Semi-major axis of the ellipse.
            b : :class: `float`
                Semi-minor axis of the ellipse.
            n : :class: `int`
                Number of points to draw the ellipse.

            Returns
            -------
            :class: `Basemap`
                It returns one Basemap ellipse at a time.
            """
            ax = kwargs.pop('ax', None) or self._check_ax()
            g = pyproj.Geod(a=self.rmajor, b=self.rminor)
            azf, azb, dist = g.inv([x0, x0], [y0, y0], [x0+a, x0], [y0, y0+b])
            tsid = dist[0] * dist[1]  # a * b
            seg = [self(x0+a, y0)]
            AZ = np.linspace(azf[0], 360. + azf[0], n)
            for i, az in enumerate(AZ):
                # Skips segments along equator (Geod can't handle equatorial arcs).
                if np.allclose(0., y0) and (np.allclose(90., az) or
                                            np.allclose(270., az)):
                    continue

                # In polar coordinates, with the origin at the center of the
                # ellipse and with the angular coordinate ``az`` measured from the
                # major axis, the ellipse's equation  is [1]:
                #
                #                           a * b
                # r(az) = ------------------------------------------
                #         ((b * cos(az))**2 + (a * sin(az))**2)**0.5
                #
                # Azymuth angle in radial coordinates and corrected for reference
                # angle.
                azr = 2. * np.pi / 360. * (az + 90.)
                A = dist[0] * np.sin(azr)
                B = dist[1] * np.cos(azr)
                r = tsid / (B**2. + A**2.)**0.5
                lon, lat, azb = g.fwd(x0, y0, az, r)
                x, y = self(lon, lat)

                # Add segment if it is in the map projection region.
                if x < 1e20 and y < 1e20:
                    seg.append((x, y))

            poly = Polygon(seg, **kwargs)
            ax.add_patch(poly)

            # Set axes limits to fit map region.
            self.set_axes_limits(ax=ax)

            return poly

    # Create an instance of our custom Basemap.
    m = BasemapWithEllipse(
        projection=projection, lon_0=ra_center, resolution=None,
        celestial=False, ax=ax)
    if ra_labels is not None:
        if projection in ('hammer', 'moll'):
            labels = [0, 0, 0, 0]
        else:
            labels = [0, 0, 1, 0]
        m.drawmeridians(
            ra_labels, labels=labels, labelstyle='+/-')
    if dec_labels is not None:
        m.drawparallels(
            dec_labels, labels=[1, 1, 0, 0], labelstyle='+/-')
    m.drawmapboundary()

    # Draw the optional galactic plane.
    if galactic_plane_color is not None:
        # Generate coordinates of a line in galactic coordinates and convert
        # to equatorial coordinates.
        galactic_l = np.linspace(0, 2 * np.pi, 1000)
        galactic_plane = SkyCoord(l=galactic_l*u.radian,
                                  b=np.zeros_like(galactic_l)*u.radian,
                                  frame='galactic').fk5
        # Project to map coordinates and display.  Use a scatter plot to
        # avoid wrap-around complications.
        galactic_x, galactic_y = m(galactic_plane.ra.degree,
                                   galactic_plane.dec.degree)

        paths = m.scatter(
            galactic_x, galactic_y, marker='.', s=20, lw=0, alpha=0.75,
            c=galactic_plane_color)
        # Make sure the galactic plane stays above other displayed objects.
        paths.set_zorder(20)

    # Draw the optional ecliptic plane.
    if ecliptic_color is not None:
        lon = np.linspace(0, 2 * np.pi, 50) * u.rad
        ecliptic = HeliocentricTrueEcliptic(
            lon=lon, lat=0 * u.radian, distance=1 * u.Mpc).transform_to(ICRS)
        ecliptic_x, ecliptic_y = m(ecliptic.ra.degree, ecliptic.dec.degree)
        paths = m.scatter(
            ecliptic_x, ecliptic_y, marker='.', s=20, lw=0, alpha=0.75,
            c=ecliptic_color)
        paths.set_zorder(20)

    return m