1
# Licensed under a 3-clause BSD style license - see LICENSE.rst
2 1
import re
3 1
import os
4 1
import io
5 1
import requests
6 1
import numpy as np
7 1
from astropy.table import Table
8 1
import astropy.io.fits as fits
9

10

11 1
__all__ = ['query', 'save_file', 'get_file']
12 1
id_parse = re.compile(r'ID\=(\d+)')
13

14
# should skip only if remote_data = False
15 1
__doctest_skip__ = ['query', 'save_file', 'get_file']
16

17 1
uri = 'http://sha.ipac.caltech.edu/applications/Spitzer/SHA/servlet/DataService?'
18

19

20 1
def query(coord=None, ra=None, dec=None, size=None, naifid=None, pid=None,
21
          reqkey=None, dataset=2, verbosity=3, return_response=False,
22
          return_payload=False):
23
    """
24
    Query the Spitzer Heritage Archive (SHA).
25

26
    Four query types are valid to search by position, NAIFID, PID, and ReqKey::
27

28
        position -> search a region
29
        naifid   -> NAIF ID, which is a unique number allocated to solar
30
                    system objects (e.g. planets, asteroids, comets,
31
                    spacecraft) by the NAIF at JPL.
32
        pid      -> program ID
33
        reqkey   -> AOR ID: Astronomical Observation Request ID
34

35

36
    For a valid query, enter only parameters related to a single query type::
37

38
        position -> ra, dec, size
39
        naifid   -> naifid
40
        pid      -> pid
41
        reqkey   -> reqkey
42

43
    Parameters
44
    ----------
45
    coord : astropy.coordinates.builtin_systems
46
        Astropy coordinate object. (query_type = 'position')
47
    ra : number
48
        Right ascension in degrees, alternative to using ``coord``.
49
        (query_type = 'position')
50
    dec : number
51
        Declination in degrees, alternative to using ``coord``.
52
        (query_type = 'position')
53
    size : number
54
        Region size in degrees. (query_type = 'position')
55
    naifid : number
56
        NAIF ID. (query_type = 'naifid')
57
    pid : number
58
        Program ID. (query_type = 'pid')
59
    reqkey : number
60
        Astronomical Observation Request ID. (query_type = 'reqkey')
61
    dataset : number, default 2
62
        Data set. Valid options::
63

64
            1 -> BCD data
65
            2 -> PBCD data
66

67
    verbosity : number, default 3
68
        Verbosity level, controls the number of columns to output.
69

70
    Returns
71
    -------
72
    table : `~astropy.table.Table`
73

74
    Examples
75
    --------
76
    Position query using an astropy coordinate object
77

78
    >>> import astropy.coordinates as coord
79
    >>> import astropy.units as u
80
    >>> from astroquery import sha
81
    >>> pos_t = sha.query(coord=coord.SkyCoord(ra=163.6136, dec=-11.784,
82
    ... unit=(u.degree, u.degree)), size=0.5)
83

84
    Position query with optional ``ra`` and ``dec`` parameters
85

86
    >>> pos_t = sha.query(ra=163.6136, dec=-11.784, size=0.5)
87

88
    NAIFID query
89

90
    >>> nid_t = sha.query(naifid=2003226)
91

92
    PID query
93

94
    >>> pid_t = sha.query(pid=30080)
95

96
    ReqKey query
97

98
    >>> rqk_t = sha.query(reqkey=21641216)
99

100
    Notes
101
    -----
102
    For column descriptions, metadata, and other information visit the SHA
103
    query API_ help page
104

105
    .. _API: http://sha.ipac.caltech.edu/applications/Spitzer/SHA/help/doc/api.html
106
    """
107
    # Use Coordinate instance if supplied
108 1
    if coord is not None:
109 0
        try:
110 0
            ra = coord.transform_to('fk5').ra.degree
111 0
            dec = coord.transform_to('fk5').dec.degree
112 0
        except ValueError:
113 0
            raise ValueError('Cannot parse `coord` variable.')
114
    # Query parameters
115 1
    payload = {'RA': ra,
116
               'DEC': dec,
117
               'SIZE': size,
118
               'NAIFID': naifid,
119
               'PID': pid,
120
               'REQKEY': reqkey,
121
               'VERB': verbosity,
122
               'DATASET': 'ivo://irsa.ipac.spitzer.level{0}'.format(dataset)}
123 1
    if return_payload:
124 0
        return payload
125
    # Make request
126 1
    response = requests.get(uri, params=payload)
127 1
    if return_response:
128 0
        return response
129 1
    response.raise_for_status()
130
    # Parse output
131
    # requests returns unicode strings, encode back to ascii
132
    # because of '|foo|bar|' delimiters, remove first and last empty columns
133 1
    raw_data = [line for line in response.text.split('\n')]
134 1
    field_widths = [len(s) + 1 for s in raw_data[1].split('|')][1:-1]
135 1
    col_names = [s.strip() for s in raw_data[1].split('|')][1:-1]
136 1
    type_names = [s.strip() for s in raw_data[2].split('|')][1:-1]
137 1
    cs = [0] + np.cumsum(field_widths).tolist()
138

139 1
    def parse_line(line, cs=cs):
140 1
        return [line[a:b] for a, b in zip(cs[:-1], cs[1:])]
141

142 1
    data = [parse_line(row) for row in raw_data[4:-1]]
143
    # Parse type names
144 1
    dtypes = _map_dtypes(type_names, field_widths)
145
    # To table
146
    # transpose data for appropriate table instance handling
147 1
    t = Table(list(zip(*data)), names=col_names, dtype=dtypes)
148 1
    return t
149

150

151 1
def save_file(url, out_dir='sha_tmp/', out_name=None):
152
    """
153
    Download image to output directory given a URL from a SHA query.
154

155
    Parameters
156
    ----------
157
    url : str
158
        Access URL from SHA query. Requires complete URL, valid URLs from the
159
        SHA query include columns::
160

161
            accessUrl -> The URL to be used to retrieve an image or table
162
            withAnc1  -> The URL to be used to retrieve the image or spectra
163
                         with important ancillary products (mask, uncertainty,
164
                         etc.) as a zip archive
165

166
    out_dir : str
167
        Path for output table or image
168
    out_name : str
169
        Name for output table or image, if None use the file ID as name.
170

171
    Examples
172
    --------
173
    >>> from astroquery import sha
174
    >>> url = sha.query(pid=30080)['accessUrl'][0]
175
    >>> sha.save_file(url)
176
    """
177 0
    exten_types = {'image/fits': '.fits',
178
                   'text/plain; charset=UTF-8': '.tbl',
179
                   'application/zip': '.zip',
180
                   }
181
    # Make request
182 0
    response = requests.get(url, stream=True)
183 0
    response.raise_for_status()
184
    # Name file using ID at end
185 0
    if out_name is None:
186 0
        out_name = 'shaID_' + id_parse.findall(url)[0]
187
    # Determine extension
188 0
    exten = exten_types[response.headers['Content-Type']]
189
    # Check if path exists
190 0
    if not os.path.exists(out_dir):
191 0
        os.makedirs(out_dir)
192
    # Write file
193 0
    with open(out_dir + out_name + exten, 'wb') as f:
194 0
        for block in response.iter_content(1024):
195 0
            f.write(block)
196

197

198 1
def get_file(url):
199
    """
200
    Return object from SHA query URL.
201

202
    Currently only supports FITS files.
203

204
    Parameters
205
    ----------
206
    url : str
207
        Access URL from SHA query. Requires complete URL, valid URLs from the
208
        SHA query include columns::
209

210
            accessUrl -> The URL to be used to retrieve an image or table
211
            withAnc1  -> The URL to be used to retrieve the image or spectra
212
                         with important ancillary products (mask, uncertainty,
213
                         etc.) as a zip archive
214

215
    Returns
216
    -------
217
    obj : `~astropy.table.Table`, `astropy.io.fits`, list
218
        Return object depending if link points to a table, fits image, or zip
219
        file of products.
220

221
    Examples
222
    --------
223
    >>> from astroquery import sha
224
    >>> url = sha.query(pid=30080)['accessUrl'][0]
225
    >>> img = sha.get_file(url)
226
    """
227
    # Make request
228 1
    response = requests.get(url, stream=True)
229 1
    response.raise_for_status()
230
    # Read fits
231 1
    iofile = io.BytesIO(response.content)
232 1
    content_type = response.headers['Content-Type']
233 1
    if content_type == 'image/fits':
234 1
        obj = fits.open(iofile)
235
    else:
236 0
        raise Exception('Unknown content type: {0}.'.format(content_type))
237 1
    return obj
238

239

240 1
def _map_dtypes(type_names, field_widths):
241
    """
242
    Create dtype string based on column lengths and field type names.
243

244
    Parameters
245
    ----------
246
    type_names : list
247
        List of type names from file header
248
    field_widths : list
249
        List of field width values
250

251
    Returns
252
    -------
253
    dtypes : list
254
        List of dtype for each column in data
255
    """
256 1
    dtypes = []
257 1
    for i, name in enumerate(type_names):
258 1
        if name == 'int':
259 1
            dtypes.append('i8')
260 1
        elif name == 'double':
261 1
            dtypes.append('f8')
262 1
        elif name == 'char':
263 1
            dtypes.append('a{0}'.format(field_widths[i]))
264
        else:
265 0
            raise ValueError('Unexpected type name: {0}.'.format(name))
266 1
    return dtypes

Read our documentation on viewing source code .

Loading