astro py:docs

- Index
- Modules

### Navigation

- Astropy v0.3 »
- Module code »

# Source code for astropy.coordinates.name_resolve

    # 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))