1
2 """
3 Interface to the GATOR search engine
4 """
5 import os
6 import urllib
7 import logging
8 import ConfigParser
9 import numpy as np
10
11 from ivs.aux import loggers
12 from ivs.aux import numpy_ext
13 from ivs.inout import ascii
14 from ivs.sed import filters
15 from ivs.units import conversions
16
17
18 logger = logging.getLogger("CAT.GATOR")
19 logger.addHandler(loggers.NullHandler())
20
21
22 basedir = os.path.dirname(os.path.abspath(__file__))
23
24
25 cat_info = ConfigParser.ConfigParser()
26 cat_info.optionxform = str
27 cat_info.readfp(open(os.path.join(basedir,'gator_cats_phot.cfg')))
28
29
30
31
33 """
34 Search and retrieve information from a Gator catalog.
35
36 Two ways to search for data within a catalog C{name}:
37
38 1. You're looking for info on B{one target}, then give the target's
39 C{ID} or coordinates (C{ra} and C{dec}), and a search C{radius}.
40
41 2. You're looking for information of B{a whole field}, then give the
42 field's coordinates (C{ra} and C{dec}), and C{radius}.
43
44 If you have a list of targets, you need to loop this function.
45
46 If you supply a filename, the results will be saved to that path, and you
47 will get the filename back as received from urllib.URLopener (should be the
48 same as the input name, unless something went wrong).
49
50 If you don't supply a filename, you should leave C{filetype} to the default
51 C{tsv}, and the results will be saved to a temporary
52 file and deleted after the function is finished. The content of the file
53 will be read into a dictionary, as well as the units (two separate
54 dictionaries with the same keys, depending on the colum names in the
55 catalog). The entries in the dictionary are of type C{ndarray}, and will
56 be converted to a float-array if possible. If not, the array will consist
57 of strings. The comments are also returned as a list of strings.
58
59
60 @param catalog: name of a GATOR catalog (e.g. 'II/246/out')
61 @type catalog: str
62 @keyword filename: name of the file to write the results to (no extension)
63 @type filename: str
64 @return: filename / catalog data columns, units, comments
65 @rtype: str/ record array, dict, list of str
66 """
67 filename = kwargs.pop('filename',None)
68 filetype = kwargs.setdefault('filetype','1')
69 if filename is not None and '.' in os.path.basename(filename):
70 filetype = os.path.splitext(filename)[1][1:]
71 elif filename is not None:
72 filename = '%s.%s'%(filename,filetype)
73
74
75 base_url = _get_URI(catalog,**kwargs)
76
77 url = urllib.URLopener()
78 filen,msg = url.retrieve(base_url,filename=filename)
79
80 if filename is not None:
81 logger.info('Querying GATOR source %s and downloading to %s'%(catalog,filen))
82 url.close()
83 return filen
84
85
86 if filetype=='1':
87 try:
88 results,units,comms = txt2recarray(filen)
89
90 except ValueError:
91 raise ValueError, "failed to read %s, perhaps multiple catalogs specified (e.g. III/168 instead of III/168/catalog)"%(catalog)
92 url.close()
93 logger.info('Querying GATOR source %s (%d)'%(catalog,(results is not None and len(results) or 0)))
94 return results,units,comms
95
96
97
98
100 """
101 Return a list of all availabe GATOR catalogues.
102
103 @return: list of gator catalogs and discriptions
104 @rtype: list of string tuples
105 """
106 url = urllib.URLopener()
107 filen,msg = url.retrieve('http://irsa.ipac.caltech.edu/cgi-bin/Gator/nph-scan?mode=ascii')
108 results,units,comms = txt2recarray(filen)
109 cats = []
110 for i in range(len(results)):
111 cats += [(results['catname'][i],results['description'][i])]
112 logger.info('%-20s: %s'%cats[-1])
113 url.close()
114 return cats
115
116
117
118
119
120 -def get_photometry(ID=None,extra_fields=['dist','ra','dec'],**kwargs):
121 """
122 Download all available photometry from a star to a record array.
123
124 For extra kwargs, see L{_get_URI} and L{gator2phot}
125
126 Example usage:
127
128 >>> import pylab
129 >>> import vizier
130 >>> name = 'kr cam'
131 >>> master = vizier.get_photometry(name,to_units='erg/s/cm2/AA',extra_fields=[])
132 >>> master = get_photometry(name,to_units='erg/s/cm2/AA',extra_fields=[],master=master)
133 >>> p = pylab.figure()
134 >>> wise = np.array(['WISE' in photband and True or False for photband in master['photband']])
135 >>> p = pylab.errorbar(master['cwave'],master['cmeas'],yerr=master['e_cmeas'],fmt='ko')
136 >>> p = pylab.errorbar(master['cwave'][wise],master['cmeas'][wise],yerr=master['e_cmeas'][wise],fmt='ro',ms=8)
137 >>> p = pylab.gca().set_xscale('log')
138 >>> p = pylab.gca().set_yscale('log')
139 >>> p = pylab.show()
140
141 Other examples:
142 >>> master = get_photometry(ra=71.239527,dec=-70.589427,to_units='erg/s/cm2/AA',extra_fields=[],radius=1.)
143 >>> master = get_photometry(ID='J044458.39-703522.6',to_units='W/m2',extra_fields=[],radius=1.)
144 """
145 kwargs['ID'] = ID
146 to_units = kwargs.pop('to_units','erg/s/cm2/AA')
147 master_ = kwargs.get('master',None)
148 master = None
149
150 for source in cat_info.sections():
151 results,units,comms = search(source,**kwargs)
152 if results is not None:
153 master = gator2phot(source,results,units,master,extra_fields=extra_fields)
154
155
156 if to_units and master is not None:
157
158 dtypes = [('cwave','f8'),('cmeas','f8'),('e_cmeas','f8'),('cunit','a50')]
159 cols = [[],[],[],[]]
160
161 no_errors = np.isnan(master['e_meas'])
162 master['e_meas'][no_errors] = 0.
163
164 zp = filters.get_info(master['photband'])
165 for i in range(len(master)):
166 try:
167 value,e_value = conversions.convert(master['unit'][i],to_units,master['meas'][i],master['e_meas'][i],photband=master['photband'][i])
168 except ValueError:
169 value,e_value = np.nan,np.nan
170 except AssertionError:
171 value,e_value = np.nan,np.nan
172 try:
173 eff_wave = filters.eff_wave(master['photband'][i])
174 except IOError:
175 eff_wave = np.nan
176 cols[0].append(eff_wave)
177 cols[1].append(value)
178 cols[2].append(e_value)
179 cols[3].append(to_units)
180 master = numpy_ext.recarr_addcols(master,cols,dtypes)
181
182 master['e_meas'][no_errors] = np.nan
183 master['e_cmeas'][no_errors] = np.nan
184
185 if master_ is not None and master is not None:
186 master = numpy_ext.recarr_addrows(master_,master.tolist())
187 elif master_ is not None:
188 master = master_
189
190
191 return master
192
193
194
195
196
197
198
200 """
201 Read a GATOR outfmt=1__ (ASCII) file into a record array.
202
203 @param filename: name of the TSV file
204 @type filename: str
205 @return: catalog data columns, units, comments
206 @rtype: record array, dict, list of str
207 """
208 ff = open(filename,'r')
209 data = []
210 comms = []
211 indices = None
212 while 1:
213 line = ff.readline()
214 if not line: break
215
216
217 if line.isspace():
218 continue
219
220
221 line = line.replace('\n','')
222
223 if line[0] =='\\':
224 continue
225 if line[0] == '|':
226 comms.append(line)
227 indices = []
228 for i in range(len(line)):
229 if line[i]=='|': indices.append(i)
230 continue
231 if indices is None: break
232 data.append([])
233 for i in range(len(indices)-1):
234 data[-1].append(line[indices[i]:indices[i+1]].strip())
235 results = None
236 units = {}
237
238 if comms:
239
240 data = np.array(data)
241
242
243
244
245 names = [head.strip() for head in comms[0].split('|')[1:-1]]
246 formats = comms[1]
247 formats = formats.replace('double','f8')
248 formats = formats.replace('char','a100')
249 formats = formats.replace('int','f8')
250 formats = formats.replace('long','f8')
251 formats = [head.strip() for head in formats.split('|')[1:-1]]
252 units_ = [head.strip() for head in comms[2].split('|')[1:-1]]
253
254 dtypes = np.dtype([(i,j) for i,j in zip(names,formats)])
255
256 cols = []
257 for i,key in enumerate(names):
258 units[key] = units_[i]
259 if len(data)==0: break
260 col = data[:,i]
261
262 col = [(row.isspace() or not row or row=='null') and 'nan' or row for row in col]
263 cols.append(col)
264
265
266 if len(data)==0:
267 results = None
268 else:
269 cols = np.array(cols)
270 cols = [np.cast[dtypes[i]](cols[i]) for i in range(len(cols))]
271 results = np.rec.array(cols, dtype=dtypes)
272 return results,units,comms
273
274
275
276 -def gator2phot(source,results,units,master=None,extra_fields=['_r','_RAJ2000','_DEJ2000']):
277 """
278 Convert/combine Gator record arrays to measurement record arrays.
279
280 Every line in the combined array represents a measurement in a certain band.
281
282 This is probably only useful if C{results} contains only information on
283 one target (or you have to give 'ID' as an extra field, maybe).
284
285 The standard columns are:
286
287 1. C{meas}: containing the photometric measurement
288 2. C{e_meas}: the error on the photometric measurement
289 3. C{flag}: an optional quality flag
290 4. C{unit}: the unit of the measurement
291 5. C{photband}: the photometric passband (FILTER.BAND)
292 6. C{source}: name of the source catalog
293
294 You can add extra information from the Gator catalog via the list of keys
295 C{extra_fields}.
296
297 If you give a C{master}, the information will be added to a previous
298 record array. If not, a new master will be created.
299
300 The result is a record array with each row a measurement.
301
302 @param source: name of the Gator source
303 @type source: str
304 @param results: results from Gator C{search}
305 @type results: record array
306 @param units: header of Gator catalog with key name the column name and
307 key value the units of that column
308 @type units: dict
309 @param master: master record array to add information to
310 @type master: record array
311 @param extra_fields: any extra columns you want to add information from
312 @type extra_fields: list of str
313 @return: array with each row a measurement
314 @rtype: record array
315 """
316 e_flag = 'e_'
317 q_flag = 'q_'
318
319 dtypes = [('meas','f8'),('e_meas','f8'),('flag','a20'),
320 ('unit','a30'),('photband','a30'),('source','a50')]
321
322
323 names = list(results.dtype.names)
324 translation = {'_r':'dist','_RAJ2000':'ra','_DEJ2000':'dec'}
325 if extra_fields is not None:
326 for e_dtype in extra_fields:
327 dtypes.append((e_dtype,results.dtype[names.index(translation[e_dtype])].str))
328
329
330 newmaster = False
331 if master is None or len(master)==0:
332 master = np.rec.array([tuple([('f' in dt[1]) and np.nan or 'none' for dt in dtypes])],dtype=dtypes)
333 newmaster = True
334
335
336 cols_added = 0
337 for key in cat_info.options(source):
338 if key[:2] in [e_flag,q_flag] or key =='bibcode':
339 continue
340 photband = cat_info.get(source,key)
341
342
343 cols = [results[key][:1],
344 (e_flag+key in cat_info.options(source) and results[cat_info.get(source,e_flag+key)][:1] or np.ones(len(results[:1]))*np.nan),
345 (q_flag+key in cat_info.options(source) and results[cat_info.get(source,q_flag+key)][:1] or np.ones(len(results[:1]))*np.nan),
346 np.array(len(results[:1])*[units[key]],str),
347 np.array(len(results[:1])*[photband],str),
348 np.array(len(results[:1])*[source],str)]
349
350 if extra_fields is not None:
351 for e_dtype in extra_fields:
352 cols += [translation[e_dtype] in results.dtype.names and results[translation[e_dtype]][:1] or np.ones(len(results[:1]))*np.nan]
353
354 rows = []
355 for i in range(len(cols[0])):
356 rows.append(tuple([col[i] for col in cols]))
357 master = np.core.records.fromrecords(master.tolist()+rows,dtype=dtypes)
358 cols_added += 1
359
360
361 if newmaster: master = master[1:]
362 return master
363
364
365
366
367
368 -def _get_URI(name,ID=None,ra=None,dec=None,radius=1.,filetype='1',spatial='cone',**kwargs):
369 """
370 Build GATOR URI from available options.
371
372 Filetype should be one of:
373
374 6___ returns a program interface in XML
375 3___ returns a VO Table (XML)
376 2___ returns SVC (Software handshaking structure) message
377 1___ returns an ASCII table
378 0___ returns Gator Status Page in HTML (default)
379
380 kwargs are to catch unused arguments.
381
382 @param name: name of a GATOR catalog (e.g. 'wise_prelim_p3as_psd')
383 @type name: str
384 @keyword ID: target name
385 @type ID: str
386 @param filetype: type of the retrieved file
387 @type filetype: str (one of '0','1','2','3','6'... see GATOR site)
388 @keyword radius: search radius (arcseconds)
389 @type radius: float
390 @param ra: target's right ascension
391 @type ra: float
392 @param dec: target's declination
393 @type dec: float
394 @param spatial: type of spatial query
395 @type spatial: str (one of Cone, Box (NIY), Polygon (NIY), NONE (NIY))
396 @return: url
397 @rtype: str
398 """
399 base_url = 'http://irsa.ipac.caltech.edu/cgi-bin/Gator/nph-query?'
400 base_url += 'catalog=%s'%(name)
401
402
403
404
405 objstr = None
406
407
408 if ID is not None:
409
410
411 if ID[0]=='J':
412 ra = int(ID[1:3]),int(ID[3:5]),float(ID[5:10])
413 dec = int(ID[10:13]),int(ID[13:15]),float(ID[15:])
414 ra = '%02d+%02d+%.2f'%ra
415 dec = '+%+02d+%02d+%.2f'%dec
416 objstr = ra+dec
417 else:
418 objstr = ID
419
420
421 if ra is not None and dec is not None:
422 ra = str(ra)
423 dec = str(dec)
424 ra = ra.replace(' ','+').replace(':','+')
425 dec = dec.replace(' ','+').replace(':','+')
426 objstr = '+'.join([ra,dec])
427
428
429 if 'objstr' is not None: base_url += '&objstr=%s'%(objstr)
430 if 'filetype' is not None: base_url += '&outfmt=%s'%(filetype)
431 if 'radius' is not None: base_url += '&radius=%s'%(radius)
432
433 logger.debug(base_url)
434
435 return base_url
436
437
438
439
440 if __name__=="__main__":
441 import vizier
442 logger = loggers.get_basic_logger("")
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460 master = get_photometry(ID='RR Aql',to_units='erg/s/cm2/AA',extra_fields=[],radius=1.)
461 master = vizier.get_photometry(ID='RR Aql',to_units='erg/s/cm2/AA',extra_fields=[],radius=30.,master=master)
462
463
464 print master
465
466 from pylab import *
467 figure()
468
469
470 plot(np.log10(master['cwave']),np.log10(master['cmeas']),'ko')
471
472
473
474
475
476
477
478
479
480 show()
481
482 sys.exit()
483 import doctest
484 doctest.testmod()
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502