<reponame>xiaomi1122/astropy
# Licensed under a 3-clause BSD style license - see LICENSE.rst

"""
This module contains convenience functions for getting a coordinate object
for a named object by querying SESAME and getting the first returned result.
Note that this is intended to be a convenience, and is very simple. If you
need precise coordinates for an object you should find the appropriate
reference for that measurement and input the coordinates manually.
"""
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

# Standard library
import os
import re
import socket

# Astropy
from ..config import ConfigurationItem
from ..extern.six.moves import urllib
from .. import units as u
from .builtin_systems import ICRS

__all__ = ["get_icrs_coordinates"]

SESAME_URL = ConfigurationItem("sesame_url",
                        ["http://cdsweb.u-strasbg.fr/cgi-bin/nph-sesame/",
                         "http://vizier.cfa.harvard.edu/viz-bin/nph-sesame/"],
                        "The URL to Sesame's web-queryable database.",
                        cfgtype='string_list')

SESAME_DATABASE = ConfigurationItem("sesame_database", ['all', 'simbad', 'ned',
                                    'vizier'],
                                    "This specifies the default database that "
                                    "SESAME will query when using the name "
                                    "resolve mechanism in the coordinates "
                                    "subpackage. Default is to search all "
                                    "databases, but this can be 'all', "
                                    "'simbad', 'ned', or 'vizier'.")

NAME_RESOLVE_TIMEOUT = ConfigurationItem('name_resolve_timeout', 5,
                                         "This is the maximum time to wait "
                                         "for a response from a name resolve "
                                         "query to SESAME in seconds.")

class NameResolveError(Exception):
    pass

def _parse_response(resp_data):
    """
    Given a string response from SESAME, parse out the coordinates by looking
    for a line starting with a J, meaning ICRS J2000 coordinates.

    Parameters
    ----------
    resp_data : str
        The string HTTP response from SESAME.

    Returns
    -------
    ra : str
        The string Right Ascension parsed from the HTTP response.
    dec : str
        The string Declination parsed from the HTTP response.
    """

    pattr = re.compile(r"%J\s*([0-9\.]+)\s*([\+\-\.0-9]+)")
    matched = pattr.search(resp_data.decode('utf-8'))

    if matched == None:
        return None, None
    else:
        ra,dec = matched.groups()
        return ra,dec

def get_icrs_coordinates(name):
    """
    Retrieve an ICRS object by using an online name resolving
    service to retrieve coordinates for the specified name. By default,
    this will search all available databases until a match is found. If
    you would like to specify the database, use the configuration item
    `name_resolve.SESAME_DATABASE` . You can also specify a list of servers
    to use for querying Sesame using the configuration item
    `name_resolve.SESAME_URL`. This will try each one in order until a valid
    response is returned. By default, this list includes the main Sesame
    host and a mirror at vizier. A final configuration item,
    `name_resolve.NAME_RESOLVE_TIMEOUT`, is the number of seconds to wait
    for a response from the server before giving up. By default this is
    5 seconds.

    Parameters
    ----------
    name : str
        The name of the object to get coordinates for, e.g. M42.

    Returns
    -------
    coord : SphericalCoordinatesBase
        An `ICRS` instance for the object name specified.

    """

    database = SESAME_DATABASE()
    # The web API just takes the first letter of the database name
    db = database.upper()[0]

    # Make sure we don't have duplicates in the url list
    urls = []
    domains = []
    for url in SESAME_URL():
        domain = urllib.parse.urlparse(url).netloc

        # Check for duplicates
        if domain not in domains:
            domains.append(domain)

            # Add the query to the end of the url, add to url list
            fmt_url = os.path.join(url, "{db}?{name}")
            fmt_url = fmt_url.format(name=urllib.parse.quote(name), db=db)
            urls.append(fmt_url)

    for url in urls:
        try:
            # Retrieve ascii name resolve data from CDS
            resp = urllib.request.urlopen(url, timeout=NAME_RESOLVE_TIMEOUT())
            break
        except urllib.error.URLError as e:
            # This catches a timeout error, see:
            #   http://stackoverflow.com/questions/2712524/handling-urllib2s-timeout-python
            if isinstance(e.reason, socket.timeout):
                # If it was a timeout, try with the next URL
                continue
            else:
                raise NameResolveError("Unable to retrieve coordinates for name "
                                       "'{0}'".format(name))

    # All Sesame URL's timed out...
    else:
        raise NameResolveError("All Sesame queries timed out. Unable to "
                               "retrieve coordinates.")

    resp_data = resp.read()

    ra,dec = _parse_response(resp_data)

    if ra == None and dec == None:
        if db == "A":
            err = "Unable to find coordinates for name '{0}'".format(name)
        else:
            err = "Unable to find coordinates for name '{0}' in database {1}"\
                  .format(name, database)

        raise NameResolveError(err)

    return ICRS(ra, dec, unit=(u.degree, u.degree))

# Licensed under a 3-clause BSD style license - see LICENSE.rst

"""
This module contains convenience functions for getting a coordinate object
for a named object by querying SESAME and getting the first returned result.
Note that this is intended to be a convenience, and is very simple. If you
need precise coordinates for an object you should find the appropriate
reference for that measurement and input the coordinates manually.
"""
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

# Standard library
import os
import re
import socket

# Astropy
from ..config import ConfigurationItem
from ..extern.six.moves import urllib
from .. import units as u
from .builtin_systems import ICRS

__all__ = ["get_icrs_coordinates"]

SESAME_URL = ConfigurationItem("sesame_url",
                        ["http://cdsweb.u-strasbg.fr/cgi-bin/nph-sesame/",
                         "http://vizier.cfa.harvard.edu/viz-bin/nph-sesame/"],
                        "The URL to Sesame's web-queryable database.",
                        cfgtype='string_list')

SESAME_DATABASE = ConfigurationItem("sesame_database", ['all', 'simbad', 'ned',
                                    'vizier'],
                                    "This specifies the default database that "
                                    "SESAME will query when using the name "
                                    "resolve mechanism in the coordinates "
                                    "subpackage. Default is to search all "
                                    "databases, but this can be 'all', "
                                    "'simbad', 'ned', or 'vizier'.")

NAME_RESOLVE_TIMEOUT = ConfigurationItem('name_resolve_timeout', 5,
                                         "This is the maximum time to wait "
                                         "for a response from a name resolve "
                                         "query to SESAME in seconds.")

class NameResolveError(Exception):
    pass

def _parse_response(resp_data):
    """
    Given a string response from SESAME, parse out the coordinates by looking
    for a line starting with a J, meaning ICRS J2000 coordinates.

    Parameters
    ----------
    resp_data : str
        The string HTTP response from SESAME.

    Returns
    -------
    ra : str
        The string Right Ascension parsed from the HTTP response.
    dec : str
        The string Declination parsed from the HTTP response.
    """

    pattr = re.compile(r"%J\s*([0-9\.]+)\s*([\+\-\.0-9]+)")
    matched = pattr.search(resp_data.decode('utf-8'))

    if matched == None:
        return None, None
    else:
        ra,dec = matched.groups()
        return ra,dec

def get_icrs_coordinates(name):
    """
    Retrieve an ICRS object by using an online name resolving
    service to retrieve coordinates for the specified name. By default,
    this will search all available databases until a match is found. If
    you would like to specify the database, use the configuration item
    `name_resolve.SESAME_DATABASE` . You can also specify a list of servers
    to use for querying Sesame using the configuration item
    `name_resolve.SESAME_URL`. This will try each one in order until a valid
    response is returned. By default, this list includes the main Sesame
    host and a mirror at vizier. A final configuration item,
    `name_resolve.NAME_RESOLVE_TIMEOUT`, is the number of seconds to wait
    for a response from the server before giving up. By default this is
    5 seconds.

    Parameters
    ----------
    name : str
        The name of the object to get coordinates for, e.g. M42.

    Returns
    -------
    coord : SphericalCoordinatesBase
        An `ICRS` instance for the object name specified.

    """

    database = SESAME_DATABASE()
    # The web API just takes the first letter of the database name
    db = database.upper()[0]

    # Make sure we don't have duplicates in the url list
    urls = []
    domains = []
    for url in SESAME_URL():
        domain = urllib.parse.urlparse(url).netloc

        # Check for duplicates
        if domain not in domains:
            domains.append(domain)

            # Add the query to the end of the url, add to url list
            fmt_url = os.path.join(url, "{db}?{name}")
            fmt_url = fmt_url.format(name=urllib.parse.quote(name), db=db)
            urls.append(fmt_url)

    for url in urls:
        try:
            # Retrieve ascii name resolve data from CDS
            resp = urllib.request.urlopen(url, timeout=NAME_RESOLVE_TIMEOUT())
            break
        except urllib.error.URLError as e:
            # This catches a timeout error, see:
            #   http://stackoverflow.com/questions/2712524/handling-urllib2s-timeout-python
            if isinstance(e.reason, socket.timeout):
                # If it was a timeout, try with the next URL
                continue
            else:
                raise NameResolveError("Unable to retrieve coordinates for name "
                                       "'{0}'".format(name))

    # All Sesame URL's timed out...
    else:
        raise NameResolveError("All Sesame queries timed out. Unable to "
                               "retrieve coordinates.")

    resp_data = resp.read()

    ra,dec = _parse_response(resp_data)

    if ra == None and dec == None:
        if db == "A":
            err = "Unable to find coordinates for name '{0}'".format(name)
        else:
            err = "Unable to find coordinates for name '{0}' in database {1}"\
                  .format(name, database)

        raise NameResolveError(err)

    return ICRS(ra, dec, unit=(u.degree, u.degree))
# Licensed under a 3-clause BSD style license - see LICENSE.rst This module contains convenience functions for getting a coordinate object for a named object by querying SESAME and getting the first returned result. Note that this is intended to be a convenience, and is very simple. If you need precise coordinates for an object you should find the appropriate reference for that measurement and input the coordinates manually. # Standard library # Astropy Given a string response from SESAME, parse out the coordinates by looking     for a line starting with a J, meaning ICRS J2000 coordinates.      Parameters     ----------     resp_data : str         The string HTTP response from SESAME.      Returns     -------     ra : str         The string Right Ascension parsed from the HTTP response.     dec : str         The string Declination parsed from the HTTP response. Retrieve an ICRS object by using an online name resolving     service to retrieve coordinates for the specified name. By default,     this will search all available databases until a match is found. If     you would like to specify the database, use the configuration item     `name_resolve.SESAME_DATABASE` . You can also specify a list of servers     to use for querying Sesame using the configuration item     `name_resolve.SESAME_URL`. This will try each one in order until a valid     response is returned. By default, this list includes the main Sesame     host and a mirror at vizier. A final configuration item,     `name_resolve.NAME_RESOLVE_TIMEOUT`, is the number of seconds to wait     for a response from the server before giving up. By default this is     5 seconds.      Parameters     ----------     name : str         The name of the object to get coordinates for, e.g. M42.      Returns     -------     coord : SphericalCoordinatesBase         An `ICRS` instance for the object name specified. # The web API just takes the first letter of the database name # Make sure we don't have duplicates in the url list # Check for duplicates # Add the query to the end of the url, add to url list # Retrieve ascii name resolve data from CDS # This catches a timeout error, see: #   http://stackoverflow.com/questions/2712524/handling-urllib2s-timeout-python # If it was a timeout, try with the next URL # All Sesame URL's timed out...