<commit_before>from __future__ import division
import datetime
from copy import deepcopy
import warnings
from itertools import product

import numpy as np
from skimage import transform
from astropy import units as u
from astropy.coordinates import SkyCoord, Longitude, BaseCoordinateFrame

import sunpy.map
from sunpy.time import parse_time
from sunpy.coordinates import frames, HeliographicStonyhurst
from sunpy.image.util import to_norm, un_norm

__all__ = ['diff_rot', 'solar_rotate_coordinate', 'diffrot_map']


@u.quantity_input(duration=u.s, latitude=u.degree)
def diff_rot(duration, latitude, rot_type='howard', frame_time='sidereal'):
    """
    This function computes the change in longitude over days in degrees.

    Parameters
    -----------
    duration : `~astropy.units.Quantity`
        Number of seconds to rotate over.
    latitude : `~astropy.units.Quantity`
        heliographic coordinate latitude in Degrees.
    rot_type : `str`
        The differential rotation model to use.

        One of:

        | ``howard`` : Use values for small magnetic features from Howard et al.
        | ``snodgrass`` : Use Values from Snodgrass et. al
        | ``allen`` : Use values from Allen's Astrophysical Quantities, and simpler equation.

    frame_time : `str`
        One of : ``'sidereal'`` or  ``'synodic'``. Choose 'type of day' time reference frame.

    Returns
    -------
    longitude_delta : `~astropy.units.Quantity`
        The change in longitude over days (units=degrees)

    References
    ----------

    * `IDL code equivalent <https://hesperia.gsfc.nasa.gov/ssw/gen/idl/solar/diff_rot.pro>`__
    * `Howard rotation <http://adsabs.harvard.edu/abs/1990SoPh..130..295H>`__
    * `A review of rotation parameters (including Snodgrass values) <https://doi.org/10.1023/A:1005226402796>`__

    Examples
    --------

    Default rotation calculation over two days at 30 degrees latitude:

    >>> import numpy as np
    >>> import astropy.units as u
    >>> from sunpy.physics.differential_rotation import diff_rot
    >>> rotation = diff_rot(2 * u.day, 30 * u.deg)

    Default rotation over two days for a number of latitudes:

    >>> rotation = diff_rot(2 * u.day, np.linspace(-70, 70, 20) * u.deg)

    With rotation type 'allen':

    >>> rotation = diff_rot(2 * u.day, np.linspace(-70, 70, 20) * u.deg, 'allen')
    """

    latitude = latitude.to(u.deg)

    sin2l = (np.sin(latitude))**2
    sin4l = sin2l**2

    rot_params = {'howard': [2.894, -0.428, -0.370] * u.urad / u.second,
                  'snodgrass': [2.851, -0.343, -0.474] * u.urad / u.second,
                  'allen': [14.44, -3.0, 0] * u.deg / u.day
                  }

    if rot_type not in ['howard', 'allen', 'snodgrass']:
        raise ValueError(("rot_type must equal one of "
                          "{{ {} }}".format(" | ".join(rot_params.keys()))))

    A, B, C = rot_params[rot_type]

    rotation = (A + B * sin2l + C * sin4l) * duration

    if frame_time == 'synodic':
        rotation -= 0.9856 * u.deg / u.day * duration

    return Longitude(rotation.to(u.deg))


def solar_rotate_coordinate(coordinate,
                            new_observer_time,
                            new_observer_location="earth",
                            **diff_rot_kwargs):
    """
    Given a coordinate on the Sun, calculate where that coordinate maps to
    at some later or earlier time, given the solar rotation profile.

    Note that if the new observer location is defined using a
    BaseCoordinateFrame or SkyCoord, then it is assumed that the new observer
    location is correct for the new observer time that was also passed in.

    Parameters
    ----------
    coordinate : `~astropy.coordinates.SkyCoord`
        Any valid coordinate which is transformable to Heliographic Stonyhurst.

    new_observer_time : sunpy-compatible time
        date/time at which the input co-ordinate will be rotated to.

    new_observer_location : `str`, `~astropy.coordinates.BaseCoordinateFrame`, `~astropy.coordinates.SkyCoord`
        The solar-system body for which to calculate observer locations.  Note
        that spacecraft are not explicitly supported as yet.  Instruments in
        Earth orbit can be approximated by using the default setting.  If a
        BaseCoordinateFrame or SkyCoord are passed in, it is assumed that
        this observer location is correct for the observer time that was also
        passed in.

    **diff_rot_kwargs : keyword arguments
        Keyword arguments are passed on as keyword arguments to `~sunpy.physics.differential_rotation.diff_rot`.
        Note that the keyword "frame_time" is always set to the value "sidereal".

    Returns
    -------
    coordinate : `~astropy.coordinates.SkyCoord``
        The locations of the input coordinates after the application of
        solar rotation in the input coordinate frame.

    Examples
    --------
    >>> import astropy.units as u
    >>> from astropy.coordinates import SkyCoord
    >>> from sunpy.coordinates import frames
    >>> from sunpy.physics.differential_rotation import solar_rotate_coordinate
    >>> from sunpy.coordinates.ephemeris import get_earth
    >>> obstime = '2010-09-10 12:34:56'
    >>> c = SkyCoord(-570*u.arcsec, 120*u.arcsec, obstime=obstime, observer=get_earth(obstime), frame=frames.Helioprojective)
    >>> solar_rotate_coordinate(c, '2010-09-10 13:34:56')
    <SkyCoord (Helioprojective: obstime=2010-09-10 13:34:56, rsun=695508.0 km, observer=<HeliographicStonyhurst Coordinate (obstime=2010-09-10 13:34:56): (lon, lat, radius) in (deg, deg, AU)
        (0., 7.24822784, 1.00695436)>): (Tx, Ty, distance) in (arcsec, arcsec, km)
        (-562.37689548, 119.26840368, 1.50083152e+08)>

    """
    # The keyword "frame_time" must be explicitly set to "sidereal"
    # when using this function.
    diff_rot_kwargs.update({"frame_time": "sidereal"})

    # Calculate the interval between the start and end time
    interval = (
        parse_time(new_observer_time) - parse_time(coordinate.obstime)).total_seconds() * u.s

    # Compute Stonyhurst Heliographic co-ordinates - returns (longitude,
    # latitude). Points off the limb are returned as nan.
    heliographic_coordinate = coordinate.transform_to('heliographic_stonyhurst')

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

    # Rotate the input co-ordinate as seen by the input observer
    heliographic_rotated = SkyCoord(heliographic_coordinate.lon + drot, heliographic_coordinate.lat,
                                    obstime=coordinate.obstime, observer=coordinate.observer,
                                    frame=frames.HeliographicStonyhurst)

    # Get the location of the observer at the new observer time
    if isinstance(new_observer_location, SkyCoord) or isinstance(new_observer_location, BaseCoordinateFrame):
        location_at_new_observer_time = deepcopy(new_observer_location)
    else:
        location_at_new_observer_time = get_body(new_observer_location, new_observer_time)

    # Transform the co-ordinate to that as seen from new location
    # and transform back in to the co-ordinate system of the input
    # co-o
    return heliographic_rotated.transform_to(location_at_new_observer_time).transform_to(coordinate.frame.name)


@u.quantity_input(dt=u.s)
def _warp_sun_coordinates(xy, smap, dt, **diffrot_kwargs):
    """
    Function that returns a new list of coordinates for each input coord.
    This is an inverse function needed by the scikit-image `transform.warp`
    function.<commit_msg>changing what is required by solar rotate coordinate<commit_after>from __future__ import division
import datetime
from copy import deepcopy
import warnings
from itertools import product

import numpy as np
from skimage import transform
from astropy import units as u
from astropy.coordinates import SkyCoord, Longitude, BaseCoordinateFrame

import sunpy.map
from sunpy.time import parse_time
from sunpy.coordinates import frames, HeliographicStonyhurst
from sunpy.image.util import to_norm, un_norm

__all__ = ['diff_rot', 'solar_rotate_coordinate', 'diffrot_map']


@u.quantity_input(duration=u.s, latitude=u.degree)
def diff_rot(duration, latitude, rot_type='howard', frame_time='sidereal'):
    """
    This function computes the change in longitude over days in degrees.

    Parameters
    -----------
    duration : `~astropy.units.Quantity`
        Number of seconds to rotate over.
    latitude : `~astropy.units.Quantity`
        heliographic coordinate latitude in Degrees.
    rot_type : `str`
        The differential rotation model to use.

        One of:

        | ``howard`` : Use values for small magnetic features from Howard et al.
        | ``snodgrass`` : Use Values from Snodgrass et. al
        | ``allen`` : Use values from Allen's Astrophysical Quantities, and simpler equation.

    frame_time : `str`
        One of : ``'sidereal'`` or  ``'synodic'``. Choose 'type of day' time reference frame.

    Returns
    -------
    longitude_delta : `~astropy.units.Quantity`
        The change in longitude over days (units=degrees)

    References
    ----------

    * `IDL code equivalent <https://hesperia.gsfc.nasa.gov/ssw/gen/idl/solar/diff_rot.pro>`__
    * `Howard rotation <http://adsabs.harvard.edu/abs/1990SoPh..130..295H>`__
    * `A review of rotation parameters (including Snodgrass values) <https://doi.org/10.1023/A:1005226402796>`__

    Examples
    --------

    Default rotation calculation over two days at 30 degrees latitude:

    >>> import numpy as np
    >>> import astropy.units as u
    >>> from sunpy.physics.differential_rotation import diff_rot
    >>> rotation = diff_rot(2 * u.day, 30 * u.deg)

    Default rotation over two days for a number of latitudes:

    >>> rotation = diff_rot(2 * u.day, np.linspace(-70, 70, 20) * u.deg)

    With rotation type 'allen':

    >>> rotation = diff_rot(2 * u.day, np.linspace(-70, 70, 20) * u.deg, 'allen')
    """

    latitude = latitude.to(u.deg)

    sin2l = (np.sin(latitude))**2
    sin4l = sin2l**2

    rot_params = {'howard': [2.894, -0.428, -0.370] * u.urad / u.second,
                  'snodgrass': [2.851, -0.343, -0.474] * u.urad / u.second,
                  'allen': [14.44, -3.0, 0] * u.deg / u.day
                  }

    if rot_type not in ['howard', 'allen', 'snodgrass']:
        raise ValueError(("rot_type must equal one of "
                          "{{ {} }}".format(" | ".join(rot_params.keys()))))

    A, B, C = rot_params[rot_type]

    rotation = (A + B * sin2l + C * sin4l) * duration

    if frame_time == 'synodic':
        rotation -= 0.9856 * u.deg / u.day * duration

    return Longitude(rotation.to(u.deg))


def solar_rotate_coordinate(coordinate,
                            new_observer_location,
                            new_observer_time=None,
                            **diff_rot_kwargs):
    """
    Given a coordinate on the Sun, calculate where that coordinate maps to
    at some later or earlier time, given the solar rotation profile.

    The amount of solar rotation is based on the amount of time between the
    observation time of the input coordinate and the observation time of the
    new observer.

    If the new observer location is specified as a string, the new observer
    time must also be specified.  If the new observer location is specified
    as a BaseCoordinateFrame or a SkyCoord, then those objects must have an
    obstime property.  In this case the obstime property is used to set the
    value of the new observer time.

    Parameters
    ----------
    coordinate : `~astropy.coordinates.SkyCoord`
        Any valid coordinate which is transformable to Heliographic Stonyhurst.

    new_observer_location : `str`, `~astropy.coordinates.BaseCoordinateFrame`, `~astropy.coordinates.SkyCoord`
        The solar-system body for which to calculate observer locations.
        Instruments in Earth orbit can be approximated by setting
        new_observer_location='earth' and specifying the new_observer_time keyword.

    new_observer_time : sunpy-compatible time
        date/time at which the input co-ordinate will be rotated to. This
        keyword is required when the new observer location is defined using a string.

    **diff_rot_kwargs : keyword arguments
        Keyword arguments are passed on as keyword arguments to `~sunpy.physics.differential_rotation.diff_rot`.
        Note that the keyword "frame_time" is always set to the value "sidereal".

    Returns
    -------
    coordinate : `~astropy.coordinates.SkyCoord``
        The locations of the input coordinates after the application of
        solar rotation in the input coordinate frame.

    Examples
    --------
    >>> import astropy.units as u
    >>> from astropy.coordinates import SkyCoord
    >>> from sunpy.coordinates import frames
    >>> from sunpy.physics.differential_rotation import solar_rotate_coordinate
    >>> from sunpy.coordinates.ephemeris import get_earth
    >>> obstime = '2010-09-10 12:34:56'
    >>> c = SkyCoord(-570*u.arcsec, 120*u.arcsec, obstime=obstime, observer=get_earth(obstime), frame=frames.Helioprojective)
    >>> solar_rotate_coordinate(c, '2010-09-10 13:34:56')
    <SkyCoord (Helioprojective: obstime=2010-09-10 13:34:56, rsun=695508.0 km, observer=<HeliographicStonyhurst Coordinate (obstime=2010-09-10 13:34:56): (lon, lat, radius) in (deg, deg, AU)
        (0., 7.24822784, 1.00695436)>): (Tx, Ty, distance) in (arcsec, arcsec, km)
        (-562.37689548, 119.26840368, 1.50083152e+08)>

    """
    # The keyword "frame_time" must be explicitly set to "sidereal"
    # when using this function.
    diff_rot_kwargs.update({"frame_time": "sidereal"})

    # Get the location of the observer at the new observer time
    if isinstance(new_observer_location, str):
        if new_observer_time is None:
            raise ValueError('')
        new_observer = get_body(new_observer_location, new_observer_time)
    elif isinstance(new_observer_location, SkyCoord) or isinstance(new_observer_location, BaseCoordinateFrame):
        new_observer = deepcopy(new_observer_location)
        try:
            new_observer_time = new_observer_location.obstime
        except AttributeError:
            print("New observer location must have the 'obstime' attribute.")
    else:
        raise ValueError('The new observer location must be an instance of'
                         '`str`, `~astropy.coordinates.BaseCoordinateFrame`, or `~astropy.coordinates.SkyCoord`.')

    # Calculate the interval between the start and end time
    interval = (parse_time(new_observer_time) - parse_time(coordinate.obstime)).total_seconds() * u.s

    # Compute Stonyhurst Heliographic co-ordinates - returns (longitude,
    # latitude). Points off the limb are returned as nan.
    heliographic_coordinate = coordinate.transform_to('heliographic_stonyhurst')

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

    # Rotate the input co-ordinate as seen by the original observer
    heliographic_rotated = SkyCoord(heliographic_coordinate.lon + drot, heliographic_coordinate.lat,
                                    obstime=coordinate.obstime, observer=coordinate.observer,
                                    frame=frames.HeliographicStonyhurst)

    # Calculate where the rotated co-ordinate appears as seen by new observer,
    # and then transform it into the co-ordinate system of the input
    # co-ordinate.
    return heliographic_rotated.transform_to(new_observer).transform_to(coordinate.frame.name)


@u.quantity_input(dt=u.s)
def _warp_sun_coordinates(xy, smap, dt, **diffrot_kwargs):
    """
    Function that returns a new list of coordinates for each input coord.
    This is an inverse function needed by the scikit-image `transform.warp`
    function.
{'fraction_non_alphanumeric': 0.09883976168077767, 'fraction_numerical': 0.02659140796487927, 'mean_word_length': 3.0115723270440253, 'pattern_counts': {'":': 2, '<': 13, '<?xml version=': 0, '>': 97, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3297771', 'n_tokens_mistral': 5098, 'n_tokens_neox': 4574, 'n_words': 1452}