-
-
Notifications
You must be signed in to change notification settings - Fork 390
/
core.py
451 lines (394 loc) · 13.8 KB
/
core.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
"""
Search functionality for the Gemini archive of observations.
For questions, contact ooberdorf@gemini.edu
"""
from datetime import date
from astropy import units
from astropy.table import Table, MaskedColumn
from astroquery.gemini.urlhelper import URLHelper
import numpy as np
from ..query import BaseQuery
from ..utils.class_or_instance import class_or_instance
from . import conf
__all__ = ['Observations', 'ObservationsClass'] # specifies what to import
__valid_instruments__ = [
'GMOS',
'GMOS-N',
'GMOS-S',
'GNIRS',
'GRACES',
'NIRI',
'NIFS',
'GSAOI',
'F2',
'GPI',
'NICI',
'MICHELLE',
'TRECS',
'BHROS',
'HRWFS',
'OSCIR',
'FLAMINGOS',
'HOKUPAA+QUIRC',
'PHOENIX',
'TEXES',
'ABU',
'CIRPASS'
]
__valid_observation_class__ = [
'science',
'acq',
'progCal',
'dayCal',
'partnerCal',
'acqCal',
]
__valid_observation_types__ = [
'OBJECT',
'BIAS',
'DARK',
'FLAT',
'ARC',
'PINHOLE',
'RONCHI',
'CAL',
'FRINGE',
'MASK'
]
__valid_modes__ = [
'imaging',
'spectroscopy',
'LS',
'MOS',
'IFS'
]
__valid_adaptive_optics__ = [
'NOTAO',
'AO',
'NGS',
'LGS'
]
__valid_raw_reduced__ = [
'RAW',
'PREPARED',
'PROCESSED_BIAS',
'PROCESSED_FLAT',
'PROCESSED_FRINGE',
'PROCESSED_ARC'
]
class ObservationsClass(BaseQuery):
server = conf.server
url_helper = URLHelper(server)
def __init__(self, *args):
"""
Query class for observations in the Gemini archive.
This class provides query capabilities against the gemini archive. Queries
can be done by cone search, by name, or by a set of criteria.
"""
super().__init__()
@class_or_instance
def query_region(self, coordinates, radius=0.3*units.deg):
"""
search for Gemini observations by target on the sky.
Given a sky position and radius, returns a `~astropy.table.Table` of Gemini observations.
Parameters
----------
coordinates : str or `~astropy.coordinates` object
The target around which to search. It may be specified as a
string or as the appropriate `~astropy.coordinates` object.
radius : str or `~astropy.units.Quantity` object, optional
Default 0.3 degrees.
The string must be parsable by `~astropy.coordinates.Angle`. The
appropriate `~astropy.units.Quantity` object from
`~astropy.units` may also be used. Defaults to 0.3 deg.
Returns
-------
response : `~astropy.table.Table`
"""
return self.query_criteria(coordinates=coordinates, radius=radius)
@class_or_instance
def query_object(self, objectname, radius=0.3*units.deg):
"""
search for Gemini observations by target on the sky.
Given an object name and optional radius, returns a `~astropy.table.Table` of Gemini observations.
Parameters
----------
objectname : str
The name of an object to search for. This attempts to resolve the object
by name and do a search on that area of the sky. This does not handle
moving targets.
radius : str or `~astropy.units.Quantity` object, optional
Default 0.3 degrees.
The string must be parsable by `~astropy.coordinates.Angle`. The
appropriate `~astropy.units.Quantity` object from
`~astropy.units` may also be used. Defaults to 0.3 deg.
Returns
-------
response : `~astropy.table.Table`
"""
return self.query_criteria(objectname=objectname, radius=radius)
@class_or_instance
def query_criteria(self, coordinates=None, radius=0.3*units.deg, pi_name=None, program_id=None, utc_date=None,
instrument=None, observation_class=None, observation_type=None, mode=None,
adaptive_optics=None, program_text=None, objectname=None, raw_reduced=None):
"""
search a variety of known parameters against the Gemini observations.
Given various criteria, search the Gemini archive for matching observations.
Parameters
----------
coordinates : str or `~astropy.coordinates` object
The target around which to search. It may be specified as a
string or as the appropriate `~astropy.coordinates` object.
radius : str or `~astropy.units.Quantity` object, optional
Default 0.3 degrees.
The string must be parsable by `~astropy.coordinates.Angle`. The
appropriate `~astropy.units.Quantity` object from
`~astropy.units` may also be used. Defaults to 0.3 deg.
pi_name : str, optional
Default None.
Can be used to search for data by the PI's name.
program_id : str, optional
Default None.
Can be used to match on program ID
utc_date : date or (date,date) tuple, optional
Default None.
Can be used to search for observations on a particular day or range of days (inclusive).
instrument : str, optional
Can be used to search for a particular instrument. Valid values are:
'GMOS',
'GMOS-N',
'GMOS-S',
'GNIRS',
'GRACES',
'NIRI',
'NIFS',
'GSAOI',
'F2',
'GPI',
'NICI',
'MICHELLE',
'TRECS',
'BHROS',
'HRWFS',
'OSCIR',
'FLAMINGOS',
'HOKUPAA+QUIRC',
'PHOENIX',
'TEXES',
'ABU',
'CIRPASS'
observation_class : str, optional
Specifies the class of observations to search for. Valid values are:
'science',
'acq',
'progCal',
'dayCal',
'partnerCal',
'acqCal'
observation_type : str, optional
Search for a particular type of observation. Valid values are:
'OBJECT',
'BIAS',
'DARK',
'FLAT',
'ARC',
'PINHOLE',
'RONCHI',
'CAL',
'FRINGE',
'MASK'
mode : str, optional
The mode of the observation. Valid values are:
'imaging',
'spectroscopy',
'LS',
'MOS',
'IFS'
adaptive_optics : str, optional
Specify the presence of adaptive optics. Valid values are:
'NOTAO',
'AO',
'NGS',
'LGS'
program_text : str, optional
Specify text in the information about the program. This is free form text.
objectname : str, optional
Give the name of the target.
raw_reduced : str, optional
Indicate the raw or reduced status of the observations to search for. Valid values are:
'RAW',
'PREPARED',
'PROCESSED_BIAS',
'PROCESSED_FLAT',
'PROCESSED_FRINGE',
'PROCESSED_ARC'
Returns
-------
response : `~astropy.table.Table`
"""
# Build parameters into raw query
#
# This consists of a set of unnamed arguments, args, and key/value pairs, kwargs
args = list()
kwargs = dict()
if radius is not None:
kwargs["radius"] = radius
if coordinates is not None:
kwargs["coordinates"] = coordinates
if pi_name is not None:
kwargs["PIname"] = pi_name
if program_id is not None:
kwargs["progid"] = program_id.upper()
if utc_date is not None:
if isinstance(utc_date, date):
args.append(utc_date.strftime("YYYYMMdd"))
elif isinstance(utc_date, tuple):
if len(utc_date) != 2:
raise ValueError("utc_date tuple should have two values")
if not isinstance(utc_date[0], date) or not isinstance(utc_date[1], date):
raise ValueError("utc_date tuple should have date values in it")
args.append("%s-%s" % utc_date[0].strftime("YYYYMMdd"), utc_date[1].strftime("YYYYMMdd"))
if instrument is not None:
if instrument.upper() not in __valid_instruments__:
raise ValueError("Unrecognized instrument: %s" % instrument)
args.append(instrument)
if observation_class is not None:
if observation_class not in __valid_observation_class__:
raise ValueError("Unrecognized observation class: %s" % observation_class)
args.append(observation_class)
if observation_type is not None:
if observation_type not in __valid_observation_types__:
raise ValueError("Unrecognized observation type: %s" % observation_type)
args.append(observation_type)
if mode is not None:
if mode not in __valid_modes__:
raise ValueError("Unrecognized mode: %s" % mode)
args.append(mode)
if adaptive_optics is not None:
if adaptive_optics not in __valid_adaptive_optics__:
raise ValueError("Unrecognized adaptive optics: %s" % adaptive_optics)
args.append(adaptive_optics)
if program_text is not None:
kwargs["ProgramText"] = program_text
if objectname is not None:
kwargs["object"] = objectname
if raw_reduced is not None:
if raw_reduced not in __valid_raw_reduced__:
raise ValueError("Unrecognized raw/reduced setting: %s" % raw_reduced)
args.append(raw_reduced)
return self.query_raw(*args, **kwargs)
@class_or_instance
def query_raw(self, *args, **kwargs):
"""
perform flexible query against Gemini observations
This is a more flexible query method. This method will do special handling for
coordinates and radius if present in kwargs. However, for the remaining arguments
it assumes all of args are useable as query path elements. For kwargs, it assumes
all of the elements can be passed as name=value within the query path to Gemini.
This method does not do any validation checking or attempt to interperet the
values being passed, aside from coordinates and radius.
This method is most useful when the query_criteria and query_region do not
meet your needs and you can build the appropriate search in the website. When
you see the URL that is generated by the archive, you can translate that into
an equivalent python call with this method. For example, if the URL in the
website is:
https://archive.gemini.edu/searchform/RAW/cols=CTOWEQ/notengineering/GMOS-N/PIname=Hirst/NotFail
You can disregard NotFail, cols=x and notengineering. You would run this query as
query_raw('GMOS-N', PIname='Hirst')
Parameters
----------
args :
The list of parameters to be passed via the query path to the webserver
kwargs :
The dictionary of parameters to be passed by name=value within the query
path to the webserver
Returns
-------
response : `~astropy.table.Table`
"""
url = self.url_helper.build_url(*args, **kwargs)
response = self._request(method="GET", url=url, data={}, timeout=180, cache=False)
js = response.json()
return _gemini_json_to_table(js)
def _gemini_json_to_table(json):
"""
takes a JSON object as returned from the Gemini archive webserver and turns it into an `~astropy.table.Table`
Parameters
----------
json : dict
A JSON object from the Gemini archive webserver
Returns
-------
response : `~astropy.table.Table`
"""
data_table = Table(masked=True)
for key in __keys__:
col_data = np.array([obj.get(key, None) for obj in json])
atype = str
col_mask = np.equal(col_data, None)
data_table.add_column(MaskedColumn(col_data.astype(atype), name=key, mask=col_mask))
return data_table
__keys__ = ["exposure_time",
"detector_roi_setting",
"detector_welldepth_setting",
"telescope",
"mdready",
"requested_bg",
"engineering",
"cass_rotator_pa",
"ut_datetime",
"file_size",
"types",
"requested_wv",
"detector_readspeed_setting",
"size",
"laser_guide_star",
"observation_id",
"science_verification",
"raw_cc",
"filename",
"instrument",
"reduction",
"camera",
"ra",
"detector_binning",
"lastmod",
"wavelength_band",
"data_size",
"mode",
"raw_iq",
"airmass",
"elevation",
"data_label",
"requested_iq",
"object",
"requested_cc",
"program_id",
"file_md5",
"central_wavelength",
"raw_wv",
"compressed",
"filter_name",
"detector_gain_setting",
"path",
"observation_class",
"qa_state",
"observation_type",
"calibration_program",
"md5",
"adaptive_optics",
"name",
"focal_plane_mask",
"data_md5",
"raw_bg",
"disperser",
"wavefront_sensor",
"gcal_lamp",
"detector_readmode_setting",
"phot_standard",
"local_time",
"spectroscopy",
"azimuth",
"release",
"dec"]
Observations = ObservationsClass()