Esempio n. 1
0
    def __init__(self, *args, **kwargs):
        kwargs.get('representation_type', None)

        super().__init__(*args, **kwargs)

        # Make 3D if specified as 2D
        if (self._data is not None and self._data.norm().unit is u.one
                and u.allclose(self._data.norm(), 1 * u.one)):

            self._data *= _RSUN.to(u.km)
Esempio n. 2
0
    def __init__(self, *args, **kwargs):
        _rep_kwarg = kwargs.get('representation_type', None)

        if ('radius' in kwargs and kwargs['radius'].unit is u.one
                and u.allclose(kwargs['radius'], 1 * u.one)):
            kwargs['radius'] = _RSUN.to(u.km)

        super().__init__(*args, **kwargs)

        # Make 3D if specified as 2D
        # If representation was explicitly passed, do not change the rep.
        if not _rep_kwarg:
            # If we were passed a 3D rep extract the distance, otherwise
            # calculate it from _RSUN.
            if isinstance(self._data, UnitSphericalRepresentation):
                distance = _RSUN.to(u.km)
                self._data = SphericalRepresentation(lat=self._data.lat,
                                                     lon=self._data.lon,
                                                     distance=distance)
Esempio n. 3
0
 def represent_as(self, base, s='base', in_frame_units=False):
     """
     Unless the requested representation is UnitSphericalRepresentation, scale a coordinate with
     dimensionless length so that it has the length of the solar radius.
     """
     data = super().represent_as(base, s, in_frame_units=in_frame_units)
     if not isinstance(data, UnitSphericalRepresentation) and \
        data.norm().unit is u.one and u.allclose(data.norm(), 1*u.one):
         data *= _RSUN.to(u.km)
     return data
Esempio n. 4
0
class Helioprojective(SunPyBaseCoordinateFrame):
    """
    A coordinate or frame in the Helioprojective Cartesian (HPC) system, which is observer-based.

    - The origin is the location of the observer.
    - ``theta_x`` is the angle relative to the plane containing the Sun-observer line and the Sun's
      rotation axis, with positive values in the direction of the Sun's west limb.
    - ``theta_y`` is the angle relative to the Sun's equatorial plane, with positive values in the
      direction of the Sun's north pole.
    - ``distance`` is the Sun-observer distance.

    This system is frequently used in a projective form without ``distance`` specified.  For
    observations looking very close to the center of the Sun, where the small-angle approximation
    is appropriate, ``theta_x`` and ``theta_y`` can be approximated as Cartesian components.

    A new instance can be created using the following signatures
    (note that if supplied, ``obstime`` and ``observer`` must be keyword arguments)::

        Helioprojective(theta_x, theta_y, obstime=obstime, observer=observer)
        Helioprojective(theta_x, theta_y, distance, obstime=obstime, observer=observer)

    Parameters
    ----------
    {data}
    Tx : `~astropy.coordinates.Angle` or `~astropy.units.Quantity`
        The theta_x coordinate for this object. Not needed if ``data`` is given.
    Ty : `~astropy.coordinates.Angle` or `~astropy.units.Quantity`
        The theta_y coordinate for this object. Not needed if ``data`` is given.
    distance : `~astropy.units.Quantity`
        The distance coordinate from the observer for this object.
        Not needed if ``data`` is given.
    {observer}
    rsun : `~astropy.units.Quantity`
        The physical (length) radius of the Sun. Used to calculate the position
        of the limb for calculating distance from the observer to the
        coordinate. Defaults to the solar radius.
    {common}

    Examples
    --------
    >>> from astropy.coordinates import SkyCoord
    >>> import sunpy.coordinates
    >>> import astropy.units as u
    >>> sc = SkyCoord(0*u.deg, 0*u.deg, 5*u.km,
    ...               obstime="2010/01/01T00:00:00", observer="earth", frame="helioprojective")
    >>> sc
    <SkyCoord (Helioprojective: obstime=2010-01-01T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty, distance) in (arcsec, arcsec, km)
        (0., 0., 5.)>
    >>> sc = SkyCoord(0*u.deg, 0*u.deg,
    ...               obstime="2010/01/01T00:00:00", observer="earth", frame="helioprojective")
    >>> sc
    <SkyCoord (Helioprojective: obstime=2010-01-01T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty) in arcsec
        (0., 0.)>
    >>> sc = SkyCoord(CartesianRepresentation(1*u.AU, 1e5*u.km, -2e5*u.km),
    ...               obstime="2011/01/05T00:00:50", observer="earth", frame="helioprojective")
    >>> sc
    <SkyCoord (Helioprojective: obstime=2011-01-05T00:00:50.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty, distance) in (arcsec, arcsec, AU)
        (137.87948623, -275.75878762, 1.00000112)>
    """
    default_representation = SphericalRepresentation

    frame_specific_representation_info = {
        SphericalRepresentation: [
            RepresentationMapping(reprname='lon',
                                  framename='Tx',
                                  defaultunit=u.arcsec),
            RepresentationMapping(reprname='lat',
                                  framename='Ty',
                                  defaultunit=u.arcsec),
            RepresentationMapping(reprname='distance',
                                  framename='distance',
                                  defaultunit=None)
        ],
        UnitSphericalRepresentation: [
            RepresentationMapping(reprname='lon',
                                  framename='Tx',
                                  defaultunit=u.arcsec),
            RepresentationMapping(reprname='lat',
                                  framename='Ty',
                                  defaultunit=u.arcsec)
        ],
    }

    rsun = Attribute(default=_RSUN.to(u.km))
    observer = ObserverCoordinateAttribute(HeliographicStonyhurst)

    def make_3d(self):
        """
        This method calculates the third coordinate of the Helioprojective
        frame. It assumes that the coordinate point is on the surface of the Sun.

        If a point in the frame is off limb then NaN will be returned.

        Returns
        -------
        new_frame : `~sunpy.coordinates.frames.Helioprojective`
            A new frame instance with all the attributes of the original but
            now with a third coordinate.
        """
        # Skip if we already are 3D
        distance = self.spherical.distance
        if not (distance.unit is u.one and u.allclose(distance, 1 * u.one)):
            return self

        if not isinstance(self.observer, BaseCoordinateFrame):
            raise ConvertError("Cannot calculate distance to the Sun "
                               f"for observer '{self.observer}' "
                               "without `obstime` being specified.")

        rep = self.represent_as(UnitSphericalRepresentation)
        lat, lon = rep.lat, rep.lon
        alpha = np.arccos(np.cos(lat) * np.cos(lon)).to(lat.unit)
        c = self.observer.radius**2 - self.rsun**2
        b = -2 * self.observer.radius * np.cos(alpha)
        # Ingore sqrt of NaNs
        with np.errstate(invalid='ignore'):
            d = ((-1 * b) - np.sqrt(b**2 - 4 * c)) / 2

        return self.realize_frame(
            SphericalRepresentation(lon=lon, lat=lat, distance=d))

    # Support the previous name for make_3d for now
    calculate_distance = deprecated('1.1',
                                    name="calculate_distance",
                                    alternative="make_3d")(make_3d)
Esempio n. 5
0
class Helioprojective(SunPyBaseCoordinateFrame):
    """
    A coordinate or frame in the Helioprojective Cartesian (HPC) system, which is observer-based.

    - The origin is the location of the observer.
    - ``Tx`` (aka "theta_x") is the angle relative to the plane containing the Sun-observer line
      and the Sun's rotation axis, with positive values in the direction of the Sun's west limb.
    - ``Ty`` (aka "theta_y") is the angle relative to the Sun's equatorial plane, with positive
      values in the direction of the Sun's north pole.
    - ``distance`` is the Sun-observer distance.

    This system is frequently used in a projective form without ``distance`` specified.  For
    observations looking very close to the center of the Sun, where the small-angle approximation
    is appropriate, ``Tx`` and ``Ty`` can be approximated as Cartesian components.

    A new instance can be created using the following signatures
    (note that if supplied, ``obstime`` and ``observer`` must be keyword arguments)::

        Helioprojective(Tx, Ty, obstime=obstime, observer=observer)
        Helioprojective(Tx, Ty, distance, obstime=obstime, observer=observer)

    Parameters
    ----------
    {data}
    Tx : `~astropy.coordinates.Angle` or `~astropy.units.Quantity`
        The theta_x coordinate for this object. Not needed if ``data`` is given.
    Ty : `~astropy.coordinates.Angle` or `~astropy.units.Quantity`
        The theta_y coordinate for this object. Not needed if ``data`` is given.
    distance : `~astropy.units.Quantity`
        The distance coordinate from the observer for this object.
        Not needed if ``data`` is given.
    {observer}
    rsun : `~astropy.units.Quantity`
        The physical (length) radius of the Sun. Used to calculate the position
        of the limb for calculating distance from the observer to the
        coordinate. Defaults to the solar radius.
    {common}

    Examples
    --------
    >>> from astropy.coordinates import SkyCoord
    >>> import sunpy.coordinates
    >>> import astropy.units as u
    >>> sc = SkyCoord(0*u.deg, 0*u.deg, 5*u.km,
    ...               obstime="2010/01/01T00:00:00", observer="earth", frame="helioprojective")
    >>> sc
    <SkyCoord (Helioprojective: obstime=2010-01-01T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty, distance) in (arcsec, arcsec, km)
        (0., 0., 5.)>
    >>> sc = SkyCoord(0*u.deg, 0*u.deg,
    ...               obstime="2010/01/01T00:00:00", observer="earth", frame="helioprojective")
    >>> sc
    <SkyCoord (Helioprojective: obstime=2010-01-01T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty) in arcsec
        (0., 0.)>
    >>> sc = SkyCoord(CartesianRepresentation(1*u.AU, 1e5*u.km, -2e5*u.km),
    ...               obstime="2011/01/05T00:00:50", observer="earth", frame="helioprojective")
    >>> sc
    <SkyCoord (Helioprojective: obstime=2011-01-05T00:00:50.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty, distance) in (arcsec, arcsec, AU)
        (137.87948623, -275.75878762, 1.00000112)>
    """
    frame_specific_representation_info = {
        SphericalRepresentation: [RepresentationMapping('lon', 'Tx', u.arcsec),
                                  RepresentationMapping('lat', 'Ty', u.arcsec),
                                  RepresentationMapping('distance', 'distance', None)],
        SphericalDifferential: [RepresentationMapping('d_lon', 'd_Tx', u.arcsec/u.s),
                                RepresentationMapping('d_lat', 'd_Ty', u.arcsec/u.s),
                                RepresentationMapping('d_distance', 'd_distance', u.km/u.s)],
        UnitSphericalRepresentation: [RepresentationMapping('lon', 'Tx', u.arcsec),
                                      RepresentationMapping('lat', 'Ty', u.arcsec)],
    }

    rsun = Attribute(default=_RSUN.to(u.km))
    observer = ObserverCoordinateAttribute(HeliographicStonyhurst)

    def make_3d(self):
        """
        This method calculates the third coordinate of the Helioprojective
        frame. It assumes that the coordinate point is on the surface of the Sun.

        If a point in the frame is off limb then NaN will be returned.

        Returns
        -------
        new_frame : `~sunpy.coordinates.frames.Helioprojective`
            A new frame instance with all the attributes of the original but
            now with a third coordinate.
        """
        # Skip if we already are 3D
        distance = self.spherical.distance
        if not (distance.unit is u.one and u.allclose(distance, 1*u.one)):
            return self

        if not isinstance(self.observer, BaseCoordinateFrame):
            raise ConvertError("Cannot calculate distance to the Sun "
                               f"for observer '{self.observer}' "
                               "without `obstime` being specified.")

        rep = self.represent_as(UnitSphericalRepresentation)
        lat, lon = rep.lat, rep.lon

        # Check for the use of floats with lower precision than the native Python float
        if not set([lon.dtype.type, lat.dtype.type]).issubset([float, np.float64, np.longdouble]):
            raise SunpyUserWarning("The Helioprojective component values appear to be lower "
                                   "precision than the native Python float: "
                                   f"Tx is {lon.dtype.name}, and Ty is {lat.dtype.name}. "
                                   "To minimize precision loss, you may want to cast the values to "
                                   "`float` or `numpy.float64` via the NumPy method `.astype()`.")

        # Calculate the distance to the surface of the Sun using the law of cosines
        cos_alpha = np.cos(lat) * np.cos(lon)
        c = self.observer.radius**2 - self.rsun**2
        b = -2 * self.observer.radius * cos_alpha
        # Ignore sqrt of NaNs
        with np.errstate(invalid='ignore'):
            d = ((-1*b) - np.sqrt(b**2 - 4*c)) / 2  # use the "near" solution

        if self._spherical_screen:
            sphere_center = self._spherical_screen['center'].transform_to(self).cartesian
            c = sphere_center.norm()**2 - self._spherical_screen['radius']**2
            b = -2 * sphere_center.dot(rep)
            # Ignore sqrt of NaNs
            with np.errstate(invalid='ignore'):
                dd = ((-1*b) + np.sqrt(b**2 - 4*c)) / 2  # use the "far" solution

            d = np.fmin(d, dd) if self._spherical_screen['only_off_disk'] else dd

        return self.realize_frame(SphericalRepresentation(lon=lon,
                                                          lat=lat,
                                                          distance=d))

    _spherical_screen = None

    @classmethod
    @contextmanager
    def assume_spherical_screen(cls, center, only_off_disk=False):
        """
        Context manager to interpret 2D coordinates as being on the inside of a spherical screen.

        The radius of the screen is the distance between the specified ``center`` and Sun center.
        This ``center`` does not have to be the same as the observer location for the coordinate
        frame.  If they are the same, then this context manager is equivalent to assuming that the
        helioprojective "zeta" component is zero.

        This replaces the default assumption where 2D coordinates are mapped onto the surface of the
        Sun.

        Parameters
        ----------
        center : `~astropy.coordinates.SkyCoord`
            The center of the spherical screen
        only_off_disk : `bool`, optional
            If `True`, apply this assumption only to off-disk coordinates, with on-disk coordinates
            still mapped onto the surface of the Sun.  Defaults to `False`.

        Examples
        --------

        .. minigallery:: sunpy.coordinates.Helioprojective.assume_spherical_screen

        >>> import astropy.units as u
        >>> from sunpy.coordinates import Helioprojective
        >>> h = Helioprojective(range(7)*u.arcsec*319, [0]*7*u.arcsec,
        ...                     observer='earth', obstime='2020-04-08')
        >>> print(h.make_3d())
        <Helioprojective Coordinate (obstime=2020-04-08T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty, distance) in (arcsec, arcsec, AU)
            [(   0., 0., 0.99660825), ( 319., 0., 0.99687244),
             ( 638., 0., 0.99778472), ( 957., 0., 1.00103285),
             (1276., 0.,        nan), (1595., 0.,        nan),
             (1914., 0.,        nan)]>

        >>> with Helioprojective.assume_spherical_screen(h.observer):
        ...     print(h.make_3d())
        <Helioprojective Coordinate (obstime=2020-04-08T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty, distance) in (arcsec, arcsec, AU)
            [(   0., 0., 1.00125872), ( 319., 0., 1.00125872),
             ( 638., 0., 1.00125872), ( 957., 0., 1.00125872),
             (1276., 0., 1.00125872), (1595., 0., 1.00125872),
             (1914., 0., 1.00125872)]>

        >>> with Helioprojective.assume_spherical_screen(h.observer, only_off_disk=True):
        ...     print(h.make_3d())
        <Helioprojective Coordinate (obstime=2020-04-08T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty, distance) in (arcsec, arcsec, AU)
            [(   0., 0., 0.99660825), ( 319., 0., 0.99687244),
             ( 638., 0., 0.99778472), ( 957., 0., 1.00103285),
             (1276., 0., 1.00125872), (1595., 0., 1.00125872),
             (1914., 0., 1.00125872)]>
        """
        try:
            old_spherical_screen = cls._spherical_screen  # nominally None

            center_hgs = center.transform_to(HeliographicStonyhurst(obstime=center.obstime))
            cls._spherical_screen = {
                'center': center,
                'radius': center_hgs.radius,
                'only_off_disk': only_off_disk
            }
            yield
        finally:
            cls._spherical_screen = old_spherical_screen
Esempio n. 6
0
    def _make_3d(self):
        # Make 3D if specified as 2D
        if (self._data is not None and self._data.norm().unit is u.one
                and u.allclose(self._data.norm(), 1*u.one)):

            self._data *= _RSUN.to(u.km)
Esempio n. 7
0
class Helioprojective(SunPyBaseCoordinateFrame):
    """
    A coordinate or frame in the Helioprojective (Cartesian) system.

    This is a projective coordinate system centered around the observer.
    It is a full spherical coordinate system with position given as longitude
    theta_x and latitude theta_y.

    Parameters
    ----------
    representation: `~astropy.coordinates.BaseRepresentation` or None.
        A representation object. If specified, other parameters must
        be in keyword form.
    Tx: `~astropy.coordinates.Angle`  or `~astropy.units.Quantity`
        X-axis coordinate.
    Ty: `~astropy.coordinates.Angle`  or `~astropy.units.Quantity`
        Y-axis coordinate.
    distance: `~astropy.units.Quantity`
        The radial distance from the observer to the coordinate point.
    obstime: SunPy Time
        The date and time of the observation, used to convert to heliographic
        carrington coordinates.
    observer: `~sunpy.coordinates.frames.HeliographicStonyhurst`, str
        The coordinate of the observer in the solar system. If you supply a string,
        it must be a solar system body that can be parsed by
        `~sunpy.coordinates.ephemeris.get_body_heliographic_stonyhurst`.
    rsun: `~astropy.units.Quantity`
        The physical (length) radius of the Sun. Used to calculate the position
        of the limb for calculating distance from the observer to the
        coordinate.

    Examples
    --------
    >>> from astropy.coordinates import SkyCoord
    >>> import sunpy.coordinates
    >>> import astropy.units as u
    >>> sc = SkyCoord(0*u.deg, 0*u.deg, 5*u.km, obstime="2010/01/01T00:00:00",
    ...               frame="helioprojective")
    >>> sc
    <SkyCoord (Helioprojective: obstime=2010-01-01T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty, distance) in (arcsec, arcsec, km)
        (0., 0., 5.)>
    >>> sc = SkyCoord(0*u.deg, 0*u.deg, obstime="2010/01/01T00:00:00", frame="helioprojective")
    >>> sc
    <SkyCoord (Helioprojective: obstime=2010-01-01T00:00:00.000, rsun=695700.0 km, observer=<HeliographicStonyhurst Coordinate for 'earth'>): (Tx, Ty) in arcsec
        (0., 0.)>
    """

    default_representation = SphericalRepresentation

    frame_specific_representation_info = {
        SphericalRepresentation: [
            RepresentationMapping(reprname='lon',
                                  framename='Tx',
                                  defaultunit=u.arcsec),
            RepresentationMapping(reprname='lat',
                                  framename='Ty',
                                  defaultunit=u.arcsec),
            RepresentationMapping(reprname='distance',
                                  framename='distance',
                                  defaultunit=None)
        ],
        UnitSphericalRepresentation: [
            RepresentationMapping(reprname='lon',
                                  framename='Tx',
                                  defaultunit=u.arcsec),
            RepresentationMapping(reprname='lat',
                                  framename='Ty',
                                  defaultunit=u.arcsec)
        ],
    }

    obstime = TimeFrameAttributeSunPy()
    rsun = Attribute(default=_RSUN.to(u.km))
    observer = ObserverCoordinateAttribute(HeliographicStonyhurst,
                                           default="earth")

    def calculate_distance(self):
        """
        This method calculates the third coordinate of the Helioprojective
        frame. It assumes that the coordinate point is on the disk of the Sun
        at the rsun radius.

        If a point in the frame is off limb then NaN will be returned.

        Returns
        -------
        new_frame : `~sunpy.coordinates.frames.HelioProjective`
            A new frame instance with all the attributes of the original but
            now with a third coordinate.
        """
        # Skip if we already are 3D
        distance = self.spherical.distance
        if not (distance.unit is u.one and u.allclose(distance, 1 * u.one)):
            return self

        if not isinstance(self.observer, BaseCoordinateFrame):
            raise ConvertError("Cannot calculate distance to the solar disk "
                               "for observer '{}' "
                               "without `obstime` being specified.".format(
                                   self.observer))

        rep = self.represent_as(UnitSphericalRepresentation)
        lat, lon = rep.lat, rep.lon
        alpha = np.arccos(np.cos(lat) * np.cos(lon)).to(lat.unit)
        c = self.observer.radius**2 - self.rsun**2
        b = -2 * self.observer.radius * np.cos(alpha)
        # Ingore sqrt of NaNs
        with np.errstate(invalid='ignore'):
            d = ((-1 * b) - np.sqrt(b**2 - 4 * c)) / 2

        return self.realize_frame(
            SphericalRepresentation(lon=lon, lat=lat, distance=d))