def test_hgs_hgc_roundtrip():
    obstime = "2011-01-01"

    hgsin = HeliographicStonyhurst(lat=0*u.deg, lon=0*u.deg, obstime=obstime)
    hgcout = hgsin.transform_to(HeliographicCarrington(obstime=obstime))

    assert_quantity_allclose(hgsin.lat, hgcout.lat)
    assert_quantity_allclose(hgcout.lon, get_sun_L0(obstime))

    hgsout = hgcout.transform_to(HeliographicStonyhurst(obstime=obstime))

    assert_quantity_allclose(hgsout.lat, hgsin.lat)
    assert_quantity_allclose(hgsout.lon, hgsin.lon)
示例#2
0
def test_hgc_loopback_self_observer():
    # Test the HGC loopback where only one end has observer='self'
    obstime = Time('2001-01-01')
    coord = HeliographicCarrington(10 * u.deg,
                                   20 * u.deg,
                                   3 * u.AU,
                                   observer='self',
                                   obstime=obstime)

    new_observer = HeliographicStonyhurst(40 * u.deg, 50 * u.deg, 6 * u.AU)
    new_frame = HeliographicCarrington(observer=new_observer, obstime=obstime)

    new_coord = coord.transform_to(new_frame)

    # Manually calculate the longitude shift due to the difference in Sun-observer distance
    lon = (6 * u.AU - 3 * u.AU) / speed_of_light * sidereal_rotation_rate

    assert_quantity_allclose(new_coord.lon, coord.lon + lon)
    assert_quantity_allclose(new_coord.lat, coord.lat)
    assert_quantity_allclose(new_coord.radius, coord.radius)
示例#3
0
def test_hgs_cartesian_rep_to_hgc():
    # This test checks transformation HGS->HCC when the coordinate is in a Cartesian
    # representation and that it is the same as a transformation from an HGS frame with a
    # spherical representation

    obstime = "2011-01-01"
    hgscoord_cart = SkyCoord(x=1 * u.km,
                             y=0. * u.km,
                             z=0. * u.km,
                             frame=HeliographicStonyhurst(obstime=obstime),
                             representation_type='cartesian')
    hgscoord_sph = hgscoord_cart.copy()
    hgscoord_sph.representation_type = 'spherical'
    # HGC
    hgcframe = HeliographicCarrington(observer='earth', obstime=obstime)
    hgccoord_cart = hgscoord_cart.transform_to(hgcframe)
    hgccoord_sph = hgscoord_sph.transform_to(hgcframe)
    assert_quantity_allclose(hgccoord_cart.lat, hgccoord_sph.lat)
    assert_quantity_allclose(hgccoord_cart.lon, hgccoord_sph.lon)
    assert_quantity_allclose(hgccoord_cart.radius, hgccoord_sph.radius)
def test_hcc_hgs_observer_mismatch():
    # Test whether the transformation gives the same answer regardless of what obstime the observer
    # coordinate is represented in
    observer1 = HeliographicStonyhurst(0*u.deg, 0*u.deg, 1*u.AU, obstime='2001-01-01')
    observer2 = observer1.transform_to(HeliographicStonyhurst(obstime='2001-03-31'))

    hcc1 = Heliocentric(0.2*u.AU, 0.3*u.AU, 0.4*u.AU, observer=observer1, obstime=observer1.obstime)
    hgs1 = hcc1.transform_to(HeliographicStonyhurst(obstime=hcc1.obstime))

    hcc2 = Heliocentric(0.2*u.AU, 0.3*u.AU, 0.4*u.AU, observer=observer2, obstime=observer1.obstime)
    hgs2 = hcc2.transform_to(HeliographicStonyhurst(obstime=hcc2.obstime))

    assert_quantity_allclose(hgs1.lon, hgs2.lon)
    assert_quantity_allclose(hgs1.lat, hgs2.lat)
    assert_quantity_allclose(hgs1.radius, hgs2.radius)
示例#5
0
def test_hgs_hci_sunspice():
    # Compare our HGS->HCI transformation against SunSPICE
    # "HEQ" is another name for HEEQ, which is equivalent to Heliographic Stonyhurst
    #
    # IDL> coord = [1.d, 120.d, 10.d]
    # IDL> convert_sunspice_lonlat, '2019-06-01', coord, 'HEQ', 'HCI', /au, /degrees
    # IDL> print, coord
    #        1.0000000      -65.736793       10.000000

    old = SkyCoord(120 * u.deg,
                   10 * u.deg,
                   1 * u.AU,
                   frame=HeliographicStonyhurst(obstime='2019-06-01'))
    new = old.transform_to(HeliocentricInertial)

    assert_quantity_allclose(new.lon,
                             -65.736793 * u.deg,
                             atol=0.5 * u.arcsec,
                             rtol=0)
    assert_quantity_allclose(new.lat, old.lat)
    assert_quantity_allclose(new.distance, old.radius)
示例#6
0
def test_velocity_hgs_hgc():
    # Construct a simple HGS coordinate with zero velocity
    obstime = Time(['2019-01-01', '2019-04-01', '2019-07-01', '2019-10-01'])
    pos = CartesianRepresentation(1, 0, 0) * u.AU
    vel = CartesianDifferential(0, 0, 0) * u.km / u.s
    loc = (pos.with_differentials(vel))._apply('repeat', obstime.size)
    coord = SkyCoord(HeliographicStonyhurst(loc, obstime=obstime))

    # The induced velocity in HGC should be entirely longitudinal, and approximately equal to one
    # full rotation every mean synodic period (27.2753 days)
    hgc_frame = HeliographicCarrington(observer='earth', obstime=obstime)
    new = coord.transform_to(hgc_frame)
    new_vel = new.data.differentials['s'].represent_as(SphericalDifferential,
                                                       new.data)
    assert_quantity_allclose(new_vel.d_lon,
                             -360 * u.deg / (27.27253 * u.day),
                             rtol=1e-2)
    assert_quantity_allclose(new_vel.d_lat, 0 * u.deg / u.s)
    assert_quantity_allclose(new_vel.d_distance,
                             0 * u.km / u.s,
                             atol=1e-7 * u.km / u.s)
示例#7
0
def test_hpc_hgs_implicit_hcc():
    # An HPC->HGS transformation should give the same answer whether the transformation step
    #   through HCC is implicit or explicit
    start = SkyCoord(0 * u.arcsec,
                     0 * u.arcsec,
                     0.5 * u.AU,
                     frame=Helioprojective(obstime='2019-06-01',
                                           observer='earth'))
    frame = HeliographicStonyhurst(obstime='2019-12-01')

    implicit = start.transform_to(frame)
    explicit1 = start.transform_to(Heliocentric(obstime=start.obstime, observer='earth')).\
        transform_to(frame)
    explicit2 = start.transform_to(Heliocentric(obstime=frame.obstime, observer='earth')).\
        transform_to(frame)

    assert_quantity_allclose(implicit.separation_3d(explicit1),
                             0 * u.AU,
                             atol=1e-10 * u.AU)
    assert_quantity_allclose(implicit.separation_3d(explicit2),
                             0 * u.AU,
                             atol=1e-10 * u.AU)
示例#8
0
def test_hgc_hgc_different_observers():
    obstime = Time('2001-01-01')
    hgc_earth = HeliographicCarrington(observer='earth', obstime=obstime)
    hgc_mars = HeliographicCarrington(observer='mars', obstime=obstime)
    hgc_sun = HeliographicCarrington(observer='sun', obstime=obstime)

    sc = SkyCoord(10 * u.deg,
                  20 * u.deg,
                  1 * u.AU,
                  frame=HeliographicStonyhurst(obstime=obstime))
    sc_hgc_earth = sc.transform_to(hgc_earth)

    sc_hgc_mars = sc_hgc_earth.transform_to(hgc_mars)

    sc_hgc_sun = sc_hgc_mars.transform_to(hgc_sun)

    ltt_earth = hgc_earth.observer.radius / speed_of_light
    assert_quantity_allclose(sc_hgc_earth.lon - sc_hgc_sun.lon,
                             ltt_earth * sidereal_rotation_rate)

    ltt_mars = hgc_mars.observer.radius / speed_of_light
    assert_quantity_allclose(sc_hgc_mars.lon - sc_hgc_sun.lon,
                             ltt_mars * sidereal_rotation_rate)
示例#9
0
def test_hgs_hgc_sunspice():
    # Compare our HGS->HGC transformation against SunSPICE
    # "HEQ" is another name for HEEQ, which is equivalent to Heliographic Stonyhurst
    # "Carrington" is offset by 0.076 degrees in longitude from our Heliographic Carrington (HGC)
    #   because "Carrington" does not include light travel time to the observer, while our
    #   HGC includes the light travel time to Earth (see Seidelmann et al. 2007).
    #
    # IDL> coord = [1.d, 0.d, 10.d]
    # IDL> convert_sunspice_lonlat, '2019-06-01', coord, 'HEQ', 'Carrington', /au, /degrees
    # IDL> print, coord
    #        1.0000000       16.688242       10.000000

    old = SkyCoord(0 * u.deg,
                   10 * u.deg,
                   1 * u.AU,
                   frame=HeliographicStonyhurst(obstime='2019-06-01'))
    new = old.heliographic_carrington

    assert_quantity_allclose(new.lon,
                             16.688242 * u.deg + 0.076 * u.deg,
                             atol=1e-2 * u.arcsec,
                             rtol=0)
    assert_quantity_allclose(new.lat, old.lat)
    assert_quantity_allclose(new.radius, old.radius)
示例#10
0
def test_transform_with_sun_center_reset():
    # This test sequence ensures that the context manager resets propoerly

    sun_center = SkyCoord(0 * u.deg,
                          0 * u.deg,
                          0 * u.AU,
                          frame=HeliographicStonyhurst(obstime="2001-01-01"))
    end_frame = HeliocentricInertial(obstime="2001-02-01")

    # Without the context manager, the coordinate should not point at Sun center
    result1 = sun_center.transform_to(end_frame)
    assert result1.lon != sun_center.lon
    assert result1.lat != sun_center.lat
    assert result1.distance != sun_center.radius

    # Using the context manager, the coordinate should point at Sun center
    with transform_with_sun_center():
        result2 = sun_center.transform_to(end_frame)
    assert_quantity_allclose(result2.lon, sun_center.lon)
    assert_quantity_allclose(result2.lat, sun_center.lat)
    assert_quantity_allclose(result2.distance, sun_center.radius)

    # Exiting a nested context manager should not affect the outer context manager
    with transform_with_sun_center():
        with transform_with_sun_center():
            pass
        result2a = sun_center.transform_to(end_frame)
    assert_quantity_allclose(result2a.lon, result2.lon)
    assert_quantity_allclose(result2a.lat, result2.lat)
    assert_quantity_allclose(result2a.distance, result2.distance)

    # After the context manager, the coordinate should have the same result as the first transform
    result3 = sun_center.transform_to(end_frame)
    assert_quantity_allclose(result3.lon, result1.lon)
    assert_quantity_allclose(result3.lat, result1.lat)
    assert_quantity_allclose(result3.distance, result1.distance)
示例#11
0
def _warp_sun_coordinates(xy, smap, new_observer, **diff_rot_kwargs):
    """
    This function takes pixel coordinates in the warped image (`xy`) and
    calculates the pixel locations of those pixels in the map.

    To do this it converts the input pixel coordinates to helioprojective
    coordinates as seen by new_observer, then transforms them to heliographic
    Stonyhurst, adds the differential rotation correction and then transforms
    them back to helioprojective coordinates as seen by the map observer and
    then calculates their corresponding pixel coordinates in the input map.

    This is an inverse function needed by `skimage.transform.warp`.

    Parameters
    ----------
    xy : `numpy.ndarray`
        Pixel coordinates in the warped image.
    smap : `~sunpy.map.GenericMap`
        Original map that we want to transform.

    Returns
    -------
    xy2 : `numpy.ndarray`
        Pixel coordinates in the map corresponding to the input pixels in the
        warped image.
    """
    # Suppress NaN warnings in coordinate transforms
    with warnings.catch_warnings():
        warnings.simplefilter('ignore')

        # The time interval between the new observer time and the map observation time.
        interval = (parse_time(new_observer.obstime) -
                    parse_time(smap.date)).to(u.s)

        # We need to get the input pixel coordinates into the OUTPUT HPC frame.
        # To save us having to construct a WCS etc, we do the transformation
        # using the output map, and then replace the observer in place before
        # transforming to HGS. This is acceptable because the pixel -> world
        # transformation is independent of the observer.
        input_pixels = xy.T * u.pix
        map_coord = smap.pixel_to_world(*input_pixels)
        output_hpc_coords = SkyCoord(map_coord.Tx,
                                     map_coord.Ty,
                                     map_coord.distance,
                                     obstime=new_observer.obstime,
                                     observer=new_observer,
                                     frame=Helioprojective)

        heliographic_coordinate = output_hpc_coords.transform_to(
            HeliographicStonyhurst)
        # Now transform the HGS coordinates to the obstime of the input map (to account for movement of Earth)
        heliographic_coordinate = heliographic_coordinate.transform_to(
            HeliographicStonyhurst(obstime=smap.date))

        # Compute the differential rotation.
        drot = diff_rot(interval, heliographic_coordinate.lat.to(u.degree),
                        **diff_rot_kwargs)

        # The change in longitude is negative because we are mapping from the
        # new coordinates to the old.
        rotated_coord = SkyCoord(heliographic_coordinate.lon - drot,
                                 heliographic_coordinate.lat,
                                 heliographic_coordinate.radius,
                                 obstime=heliographic_coordinate.obstime,
                                 frame=HeliographicStonyhurst)

        # As seen from the map observer, which coordinates are on disk and which are behind the Sun.
        where_off_disk_from_map_observer = rotated_coord.transform_to(
            Heliocentric(observer=smap.observer_coordinate)).z.value < 0

        # Re-project the pixels which are on disk back to location of the original observer
        coordinates_at_map_observer = rotated_coord.transform_to(
            smap.coordinate_frame)

        # Go back to pixel co-ordinates
        x2, y2 = smap.world_to_pixel(coordinates_at_map_observer)

    # Re-stack the data to make it correct output form
    xy2 = np.dstack([x2.T.value.flat, y2.T.value.flat])[0]
    # Set the off disk coordinates to NaN so they are not included in the output image.
    xy2[where_off_disk_from_map_observer.flat] = np.nan

    return xy2
示例#12
0
def wcsaxes_heliographic_overlay(axes,
                                 grid_spacing: u.deg = 10 * u.deg,
                                 annotate=True,
                                 obstime=None,
                                 rsun=None,
                                 **kwargs):
    """
    Create a heliographic overlay using
    `~astropy.visualization.wcsaxes.WCSAxes`.

    Will draw a grid and label the top axes.

    Parameters
    ----------
    axes : `~astropy.visualization.wcsaxes.WCSAxes`
        The `~astropy.visualization.wcsaxes.WCSAxes` object to create the HGS overlay on.
    grid_spacing: `~astropy.units.Quantity`
        Spacing for longitude and latitude grid in degrees.
    annotate : `bool`
        Passing `False` disables the axes labels and the ticks on the top and right axes.
    obstime : `~astropy.time.Time`
        The ``obstime`` to use for the `~sunpy.coordinates.HeliographicStonyhurst` grid.
    rsun : `~astropy.units.Quantity`
        The ``rsun`` to use for the `~sunpy.coordinates.HeliographicStonyhurst` grid.

    Returns
    -------
    `~astropy.visualization.wcsaxes.WCSAxes`
        The overlay object.

    Notes
    -----
    Keywords are passed to `~astropy.visualization.wcsaxes.coordinates_map.CoordinatesMap.grid`.
    """
    # Unpack spacing
    if isinstance(grid_spacing, u.Quantity) and grid_spacing.size == 1:
        lon_space = lat_space = grid_spacing
    elif grid_spacing.size == 2:
        lon_space, lat_space = grid_spacing
    else:
        raise ValueError(
            "grid_spacing must be a Quantity of length one or two.")

    # Set the native coordinates to be bottom and left only so they don't share
    # axes with the overlay.
    c1, c2 = axes.coords
    c1.set_ticks_position('bl')
    c2.set_ticks_position('bl')

    overlay = axes.get_coords_overlay(
        HeliographicStonyhurst(obstime=obstime, rsun=rsun))

    lon = overlay[0]
    lat = overlay[1]

    lon.coord_wrap = 180
    lon.set_major_formatter('dd')

    if annotate:
        lon.set_axislabel('Solar Longitude', minpad=0.8)
        lat.set_axislabel('Solar Latitude', minpad=0.9)
        lon.set_ticks_position('tr')
        lat.set_ticks_position('tr')
    else:
        lat.set_ticks_visible(False)
        lon.set_ticks_visible(False)
        lat.set_ticklabel_visible(False)
        lon.set_ticklabel_visible(False)

    grid_kw = {'color': 'white', 'zorder': 100, 'alpha': 0.5}
    grid_kw.update(kwargs)

    # Don't plot white ticks by default (only if explicitly asked)
    tick_color = grid_kw['color'] if 'color' in kwargs else 'k'
    lon.set_ticks(spacing=lon_space, color=tick_color)
    lat.set_ticks(spacing=lat_space, color=tick_color)

    overlay.grid(**grid_kw)

    if axes.title:
        x, y = axes.title.get_position()
        axes.title.set_position([x, y + 0.08])

    return overlay
示例#13
0
aiamap = sunpy.map.Map(AIA_171_IMAGE)
fig = plt.figure()
ax = plt.subplot(projection=aiamap)
aiamap.plot(clip_interval=(1., 99.95) * u.percent)

##############################################################################
# Lines of constant longitude prior to differential rotation

# sphinx_gallery_defer_figures

overlay1 = ax.get_coords_overlay('heliographic_stonyhurst')
overlay1[0].set_ticks(spacing=15. * u.deg)
overlay1[1].set_ticks(spacing=90. * u.deg)
overlay1.grid(ls='-', color='white')

##############################################################################
# Differentially rotating the lines of constant longitude by 27 days
# Be aware that the differentially rotated lines are plotted in the
# original coordinate frame, so it doesn't account for any motion of the
# observer over 27 days.

rs_hgs = RotatedSunFrame(base=HeliographicStonyhurst(obstime=aiamap.date),
                         duration=27 * u.day)
overlay2 = ax.get_coords_overlay(rs_hgs)
overlay2[0].set_ticks(spacing=15. * u.deg)
overlay2[1].set_ticks(spacing=90. * u.deg)
overlay2.grid(ls='-', color='blue')

plt.show()
fig = plt.figure()
ax = fig.add_subplot(121, projection=eui_map)
eui_map.plot(axes=ax, norm=aia_map.plot_settings['norm'], vmin=0)
ax = fig.add_subplot(122, projection=aia_map)
aia_map.plot(axes=ax, vmin=0)

###############################################################################
# Next we will reproject both maps on to a Carrington frame of reference.
# To do this we start by creating a FITS world coordinat system (WCS) header
# corresponding to the output coordinate frame.

# This is set deliberately low, and (at the cost of memory and processing time)
# can be increased to increase the output resolution
shape_out = (360, 720)
ref_coord_observer = HeliographicStonyhurst(0 * u.deg,
                                            0 * u.deg,
                                            sunpy.sun.constants.radius,
                                            obstime=eui_map.date)
ref_coord = HeliographicCarrington(0 * u.deg,
                                   0 * u.deg,
                                   sunpy.sun.constants.radius,
                                   obstime=eui_map.date,
                                   observer=ref_coord_observer)

header = sunpy.map.make_fitswcs_header(
    shape_out,
    ref_coord,
    scale=[180 / shape_out[0], 360 / shape_out[1]] * u.deg / u.pix,
    projection_code="CAR")
out_wcs = WCS(header)

###############################################################################