skycalc-cli 1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- skycalc_cli/__init__.py +0 -0
- skycalc_cli/skycalc.py +398 -0
- skycalc_cli/skycalc_cli.py +180 -0
- skycalc_cli-1.5.dist-info/LICENSE.txt +19 -0
- skycalc_cli-1.5.dist-info/METADATA +40 -0
- skycalc_cli-1.5.dist-info/RECORD +9 -0
- skycalc_cli-1.5.dist-info/WHEEL +5 -0
- skycalc_cli-1.5.dist-info/entry_points.txt +2 -0
- skycalc_cli-1.5.dist-info/top_level.txt +1 -0
skycalc_cli/__init__.py
ADDED
|
File without changes
|
skycalc_cli/skycalc.py
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""skycalc Module"""
|
|
3
|
+
|
|
4
|
+
from __future__ import print_function
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import json
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
# Base URL of the SkyCalc service (an ETC web installation). The CLI uses its
|
|
12
|
+
# CGI scripts under bin/script and the result directory tmp. This is the only
|
|
13
|
+
# place a server name appears; the backend is planned to move to a stable
|
|
14
|
+
# name under etc.eso.org, at which point only this constant changes.
|
|
15
|
+
# Override with the environment variable SKYCALC_CLI_BASE_URL or the base_url
|
|
16
|
+
# argument of SkyModel and AlmanacQuery, e.g.
|
|
17
|
+
# 'http://localhost:8080/observing/etc' for a local etc1 container.
|
|
18
|
+
DEFAULT_BASE_URL = 'https://etimecalret-002.eso.org/observing/etc'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_base_url(base_url=None):
|
|
22
|
+
"""Return the base URL to use, without a trailing slash."""
|
|
23
|
+
url = base_url or os.environ.get('SKYCALC_CLI_BASE_URL') or DEFAULT_BASE_URL
|
|
24
|
+
return url.rstrip('/')
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AlmanacQuery:
|
|
28
|
+
"""AlmanacQuery for Querying the SkyCalc Almanac"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, indic, base_url=None):
|
|
31
|
+
|
|
32
|
+
self.almdata = None
|
|
33
|
+
self.almurl = get_base_url(base_url) + '/bin/script/skycalc_almanac.py'
|
|
34
|
+
|
|
35
|
+
# Left: users keyword (skycalc_cli),
|
|
36
|
+
# Right: skycalc Almanac output keywords
|
|
37
|
+
self.alm_parameters = {}
|
|
38
|
+
self.alm_parameters['airmass'] = 'target_airmass'
|
|
39
|
+
self.alm_parameters['msolflux'] = 'sun_aveflux'
|
|
40
|
+
self.alm_parameters['moon_sun_sep'] = 'moon_sun_sep'
|
|
41
|
+
self.alm_parameters['moon_target_sep'] = 'moon_target_sep'
|
|
42
|
+
self.alm_parameters['moon_alt'] = 'moon_alt'
|
|
43
|
+
self.alm_parameters['moon_earth_dist'] = 'moon_earth_dist'
|
|
44
|
+
self.alm_parameters['ecl_lon'] = 'ecl_lon'
|
|
45
|
+
self.alm_parameters['ecl_lat'] = 'ecl_lat'
|
|
46
|
+
self.alm_parameters['observatory'] = 'observatory'
|
|
47
|
+
|
|
48
|
+
self.almindic = {}
|
|
49
|
+
# The Almanac needs:
|
|
50
|
+
# coord_ra : float [deg]
|
|
51
|
+
# coord_dec : float [deg]
|
|
52
|
+
# input_type : ut_time | local_civil_time | mjd
|
|
53
|
+
# mjd : float
|
|
54
|
+
# coord_year : int
|
|
55
|
+
# coord_month : int
|
|
56
|
+
# coord_day : int
|
|
57
|
+
# coord_ut_hour : int
|
|
58
|
+
# coord_ut_min : int
|
|
59
|
+
# coord_ut_sec : float
|
|
60
|
+
|
|
61
|
+
if 'date' in indic:
|
|
62
|
+
self.almindic['input_type'] = 'ut_time'
|
|
63
|
+
isotime = None
|
|
64
|
+
try:
|
|
65
|
+
isotime = datetime.strptime(indic['date'],
|
|
66
|
+
'%Y-%m-%dT%H:%M:%S')
|
|
67
|
+
except ValueError:
|
|
68
|
+
print('Error: wrong date format for the Almanac.')
|
|
69
|
+
raise
|
|
70
|
+
self.almindic['coord_year'] = isotime.year
|
|
71
|
+
self.almindic['coord_month'] = isotime.month
|
|
72
|
+
self.almindic['coord_day'] = isotime.day
|
|
73
|
+
self.almindic['coord_ut_hour'] = isotime.hour
|
|
74
|
+
self.almindic['coord_ut_min'] = isotime.minute
|
|
75
|
+
self.almindic['coord_ut_sec'] = isotime.second
|
|
76
|
+
|
|
77
|
+
elif 'mjd' in indic:
|
|
78
|
+
self.almindic['input_type'] = 'mjd'
|
|
79
|
+
mjd = None
|
|
80
|
+
try:
|
|
81
|
+
mjd = float(indic['mjd'])
|
|
82
|
+
except ValueError:
|
|
83
|
+
print('Error: wrong mjd format for the Almanac.')
|
|
84
|
+
raise
|
|
85
|
+
self.almindic['mjd'] = mjd
|
|
86
|
+
|
|
87
|
+
else:
|
|
88
|
+
raise ValueError('Error: no date or mjd given for the Almanac')
|
|
89
|
+
|
|
90
|
+
if 'ra' not in indic:
|
|
91
|
+
raise ValueError('Error: ra coordinate not given for the Almanac.')
|
|
92
|
+
|
|
93
|
+
if 'dec' not in indic:
|
|
94
|
+
raise ValueError('Error: dec coordinate '
|
|
95
|
+
'not given for the Almanac.')
|
|
96
|
+
|
|
97
|
+
ra = None
|
|
98
|
+
try:
|
|
99
|
+
ra = float(indic['ra'])
|
|
100
|
+
except ValueError:
|
|
101
|
+
print('Error: wrong ra format for the Almanac.')
|
|
102
|
+
raise
|
|
103
|
+
self.almindic['coord_ra'] = ra
|
|
104
|
+
|
|
105
|
+
dec = None
|
|
106
|
+
try:
|
|
107
|
+
dec = float(indic['dec'])
|
|
108
|
+
except ValueError:
|
|
109
|
+
print('Error: wrong dec format for the Almanac.')
|
|
110
|
+
raise
|
|
111
|
+
self.almindic['coord_dec'] = dec
|
|
112
|
+
|
|
113
|
+
if 'observatory' in indic:
|
|
114
|
+
self.almindic['observatory'] = indic['observatory']
|
|
115
|
+
|
|
116
|
+
def query(self):
|
|
117
|
+
|
|
118
|
+
rawdata = None
|
|
119
|
+
try:
|
|
120
|
+
response = requests.post(self.almurl, data=json.dumps(self.almindic))
|
|
121
|
+
rawdata = response.text
|
|
122
|
+
except requests.exceptions.RequestException as e:
|
|
123
|
+
print('Error: Almanac query failed.')
|
|
124
|
+
raise
|
|
125
|
+
|
|
126
|
+
# Process rawdata
|
|
127
|
+
jsondata = None
|
|
128
|
+
try:
|
|
129
|
+
jsondata = json.loads(rawdata)
|
|
130
|
+
jsondata = jsondata['output']
|
|
131
|
+
except (KeyError, ValueError):
|
|
132
|
+
print('Error: invalid Almanac response.')
|
|
133
|
+
raise
|
|
134
|
+
|
|
135
|
+
# Find the relevant (key, value)
|
|
136
|
+
almdata = {}
|
|
137
|
+
for key, value in self.alm_parameters.items():
|
|
138
|
+
subsection = 'nothing'
|
|
139
|
+
prefix = value.split('_')[0]
|
|
140
|
+
if prefix == 'sun' or prefix == 'moon' or prefix == 'target':
|
|
141
|
+
subsection = prefix
|
|
142
|
+
elif prefix == 'ecl':
|
|
143
|
+
subsection = 'target'
|
|
144
|
+
else:
|
|
145
|
+
subsection = 'observation'
|
|
146
|
+
try:
|
|
147
|
+
almdata[key] = jsondata[subsection][value]
|
|
148
|
+
except (KeyError, ValueError):
|
|
149
|
+
print('Warning: key "' + subsection + '/' + value +
|
|
150
|
+
'" not found in the Almanac response.')
|
|
151
|
+
|
|
152
|
+
return almdata
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class bcolors:
|
|
156
|
+
HEADER = '\033[95m'
|
|
157
|
+
OKBLUE = '\033[94m'
|
|
158
|
+
OKGREEN = '\033[92m'
|
|
159
|
+
WARNING = '\033[93m'
|
|
160
|
+
FAIL = '\033[91m'
|
|
161
|
+
ENDC = '\033[0m'
|
|
162
|
+
BOLD = '\033[1m'
|
|
163
|
+
UNDERLINE = '\033[4m'
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class SkyModel:
|
|
167
|
+
"""SkyModel for querying the Advanced SkyModel"""
|
|
168
|
+
|
|
169
|
+
def __init__(self, base_url=None):
|
|
170
|
+
|
|
171
|
+
self.stop_on_errors_and_exceptions = True
|
|
172
|
+
self.data = None
|
|
173
|
+
self.base_url = get_base_url(base_url)
|
|
174
|
+
self.url = self.base_url + '/bin/script/skycalc_api.py'
|
|
175
|
+
self.deleter_script_url = self.base_url + '/bin/script/rmtmp.py'
|
|
176
|
+
self.bugreport_text = ''
|
|
177
|
+
self.tmpdir = ''
|
|
178
|
+
self.params = {
|
|
179
|
+
# Airmass. Alt and airmass are coupled through the plane parallel
|
|
180
|
+
# approximation airmass=sec(z), z being the zenith distance
|
|
181
|
+
# z=90°−Alt
|
|
182
|
+
'airmass': 1.0, # float range [1.0,3.0]
|
|
183
|
+
|
|
184
|
+
# Season and Period of Night
|
|
185
|
+
'pwv_mode': 'pwv', # string grid ['pwv','season']
|
|
186
|
+
# integer grid [0,1,2,3,4,5,6] (0=all year, 1=dec/jan,2=feb/mar...)
|
|
187
|
+
'season': 0,
|
|
188
|
+
# third of night integer grid [0,1,2,3] (0=all year, 1,2,3 = third
|
|
189
|
+
# of night)
|
|
190
|
+
'time': 0,
|
|
191
|
+
|
|
192
|
+
# Precipitable Water Vapor PWV
|
|
193
|
+
# mm float grid [-1.0,0.5,1.0,1.5,2.5,3.5,5.0,7.5,10.0,20.0]
|
|
194
|
+
'pwv': 3.5,
|
|
195
|
+
|
|
196
|
+
# Monthly Averaged Solar Flux
|
|
197
|
+
'msolflux': 130.0, # s.f.u float > 0
|
|
198
|
+
|
|
199
|
+
# Scattered Moon Light
|
|
200
|
+
# Moon coordinate constraints: |z – zmoon| ≤ ρ ≤ |z + zmoon| where
|
|
201
|
+
# ρ=moon/target separation, z=90°−target altitude and
|
|
202
|
+
# zmoon=90°−moon altitude.
|
|
203
|
+
# string grid ['Y','N'] flag for inclusion of scattered moonlight.
|
|
204
|
+
'incl_moon': 'Y',
|
|
205
|
+
# degrees float range [0.0,360.0] Separation of Sun and Moon as
|
|
206
|
+
# seen from Earth ("moon phase")
|
|
207
|
+
'moon_sun_sep': 90.0,
|
|
208
|
+
# degrees float range [0.0,180.0] Moon-Target Separation ( ρ )
|
|
209
|
+
'moon_target_sep': 45.0,
|
|
210
|
+
# degrees float range [-90.0,90.0] Moon Altitude over Horizon
|
|
211
|
+
'moon_alt': 45.0,
|
|
212
|
+
# float range [0.91,1.08] Moon-Earth Distance (mean=1)
|
|
213
|
+
'moon_earth_dist': 1.0,
|
|
214
|
+
|
|
215
|
+
# Starlight
|
|
216
|
+
# string grid ['Y','N'] flag for inclusion of scattered starlight
|
|
217
|
+
'incl_starlight': 'Y',
|
|
218
|
+
|
|
219
|
+
# Zodiacal light
|
|
220
|
+
# string grid ['Y','N'] flag for inclusion of zodiacal light
|
|
221
|
+
'incl_zodiacal': 'Y',
|
|
222
|
+
# degrees float range [-180.0,180.0] Heliocentric ecliptic
|
|
223
|
+
# longitude
|
|
224
|
+
'ecl_lon': 135.0,
|
|
225
|
+
# degrees float range [-90.0,90.0] Ecliptic latitude
|
|
226
|
+
'ecl_lat': 90.0,
|
|
227
|
+
|
|
228
|
+
# Molecular Emission of Lower Atmosphere
|
|
229
|
+
# string grid ['Y','N'] flag for inclusion of lower atmosphere
|
|
230
|
+
'incl_loweratm': 'Y',
|
|
231
|
+
# Emission Lines of Upper Atmosphere
|
|
232
|
+
# string grid ['Y','N'] flag for inclusion of upper stmosphere
|
|
233
|
+
'incl_upperatm': 'Y',
|
|
234
|
+
# Airglow Continuum (Residual Continuum)
|
|
235
|
+
# string grid ['Y','N'] flag for inclusion of airglow
|
|
236
|
+
'incl_airglow': 'Y',
|
|
237
|
+
|
|
238
|
+
# Instrumental Thermal Emission This radiance component represents
|
|
239
|
+
# an instrumental effect. The emission is provided relative to the
|
|
240
|
+
# other model components. To obtain the correct absolute flux, an
|
|
241
|
+
# instrumental response curve must be applied to the resulting
|
|
242
|
+
# model spectrum See section 6.2.4 in the documentation
|
|
243
|
+
# http://localhost/observing/etc/doc/skycalc/
|
|
244
|
+
# The_Cerro_Paranal_Advanced_Sky_Model.pdf
|
|
245
|
+
# string grid ['Y','N'] flag for inclusion of instrumental thermal
|
|
246
|
+
# radiation
|
|
247
|
+
'incl_therm': 'N',
|
|
248
|
+
'therm_t1': 0.0, # K float > 0
|
|
249
|
+
'therm_e1': 0.0, # float range [0,1]
|
|
250
|
+
'therm_t2': 0.0, # K float > 0
|
|
251
|
+
'therm_e2': 0.0, # float range [0,1]
|
|
252
|
+
'therm_t3': 0.0, # float > 0
|
|
253
|
+
'therm_e3': 0.0, # K float range [0,1]
|
|
254
|
+
|
|
255
|
+
# Wavelength Grid
|
|
256
|
+
'vacair': 'vac', # vac or air
|
|
257
|
+
'wmin': 300.0, # nm float range [300.0,30000.0] < wmax
|
|
258
|
+
'wmax': 2000.0, # nm float range [300.0,30000.0] > wmin
|
|
259
|
+
# string grid ['fixed_spectral_resolution','fixed_wavelength_step',
|
|
260
|
+
# 'user']
|
|
261
|
+
'wgrid_mode': 'fixed_wavelength_step',
|
|
262
|
+
# nm/step float range [0,30000.0] wavelength sampling step dlam
|
|
263
|
+
# (not the res.element)
|
|
264
|
+
'wdelta': 0.1,
|
|
265
|
+
# float range [0,1.0e6] RESOLUTION is misleading, it is rather
|
|
266
|
+
# lam/dlam where dlam is wavelength step (not the res.element)
|
|
267
|
+
'wres': 20000,
|
|
268
|
+
'wgrid_user': [500.0, 510.0, 520.0, 530.0, 540.0, 550.0],
|
|
269
|
+
# convolve by Line Spread Function
|
|
270
|
+
'lsf_type': 'none', # string grid ['none','Gaussian','Boxcar']
|
|
271
|
+
'lsf_gauss_fwhm': 5.0, # wavelength bins float > 0
|
|
272
|
+
'lsf_boxcar_fwhm': 5.0, # wavelength bins float > 0
|
|
273
|
+
'observatory': 'paranal', # paranal
|
|
274
|
+
# compute temperature and bolometric radiance
|
|
275
|
+
'temp_flag': 1
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
def handle_exception(self, e, msg):
|
|
279
|
+
print(msg)
|
|
280
|
+
print(e)
|
|
281
|
+
print(self.bugreport_text)
|
|
282
|
+
if(self.stop_on_errors_and_exceptions):
|
|
283
|
+
sys.exit()
|
|
284
|
+
|
|
285
|
+
# handle the kind of errors we issue ourselves.
|
|
286
|
+
def handle_error(self, msg, stop=True):
|
|
287
|
+
print(msg)
|
|
288
|
+
print(self.bugreport_text)
|
|
289
|
+
if(self.stop_on_errors_and_exceptions):
|
|
290
|
+
sys.exit()
|
|
291
|
+
|
|
292
|
+
def retrieve_data(self, url):
|
|
293
|
+
try:
|
|
294
|
+
response = requests.get(url, stream=True)
|
|
295
|
+
self.data = response.content
|
|
296
|
+
except requests.exceptions.RequestException as e:
|
|
297
|
+
self.handle_exception(
|
|
298
|
+
e, 'Exception raised trying to get FITS data from ' + url)
|
|
299
|
+
|
|
300
|
+
def write(self, local_filename):
|
|
301
|
+
try:
|
|
302
|
+
with open(local_filename, 'wb') as f:
|
|
303
|
+
f.write(self.data)
|
|
304
|
+
except IOError as e:
|
|
305
|
+
self.handle_exception(
|
|
306
|
+
e, 'Exception raised trying to write fits file ')
|
|
307
|
+
|
|
308
|
+
def getdata(self):
|
|
309
|
+
return self.data
|
|
310
|
+
|
|
311
|
+
def delete_server_tmpdir(self, tmpdir):
|
|
312
|
+
try:
|
|
313
|
+
response = requests.get(self.deleter_script_url + '?d=' + tmpdir)
|
|
314
|
+
deleter_response = response.text.strip()
|
|
315
|
+
if(deleter_response != 'ok'):
|
|
316
|
+
self.handle_error('Could not delete server tmpdir ' + tmpdir)
|
|
317
|
+
except requests.exceptions.RequestException as e:
|
|
318
|
+
pass # ignore the exception
|
|
319
|
+
|
|
320
|
+
def call(self, test=False):
|
|
321
|
+
# print 'self.url=',self.url
|
|
322
|
+
# print 'self.params=',self.params
|
|
323
|
+
try:
|
|
324
|
+
response = requests.post(self.url, data=json.dumps(self.params))
|
|
325
|
+
except requests.exceptions.RequestException as e:
|
|
326
|
+
self.handle_exception(
|
|
327
|
+
e, 'Exception raised trying to POST request ' + self.url)
|
|
328
|
+
try:
|
|
329
|
+
res = json.loads(response.text)
|
|
330
|
+
status = res['status']
|
|
331
|
+
tmpdir = res['tmpdir']
|
|
332
|
+
except (KeyError, ValueError) as e:
|
|
333
|
+
self.handle_exception(
|
|
334
|
+
e, 'Exception raised trying to decode server response ')
|
|
335
|
+
|
|
336
|
+
tmpurl = self.base_url + '/tmp/' + tmpdir + '/skytable.fits'
|
|
337
|
+
|
|
338
|
+
if(status == 'success'):
|
|
339
|
+
try:
|
|
340
|
+
# retrive and save FITS data (in memory)
|
|
341
|
+
self.retrieve_data(tmpurl)
|
|
342
|
+
except requests.exceptions.RequestException as e:
|
|
343
|
+
self.handle_exception(
|
|
344
|
+
e, 'could not retrieve FITS data from server')
|
|
345
|
+
|
|
346
|
+
self.delete_server_tmpdir(tmpdir)
|
|
347
|
+
|
|
348
|
+
else: # print why validation failed
|
|
349
|
+
self.handle_error('parameter validation error: ' +
|
|
350
|
+
res['error'])
|
|
351
|
+
|
|
352
|
+
if(test):
|
|
353
|
+
# print 'call() returning status:',status
|
|
354
|
+
return status
|
|
355
|
+
|
|
356
|
+
def callwith(self, newparams):
|
|
357
|
+
for key, val in newparams.items():
|
|
358
|
+
self.params[key] = val
|
|
359
|
+
self.call()
|
|
360
|
+
|
|
361
|
+
def printparams(self, keys=None):
|
|
362
|
+
if keys is None:
|
|
363
|
+
p = self.params
|
|
364
|
+
else:
|
|
365
|
+
p = dict((k, self.params[k]) for k in keys)
|
|
366
|
+
for k in p:
|
|
367
|
+
print(k, p[k])
|
|
368
|
+
|
|
369
|
+
def test(self, label, overwite_params):
|
|
370
|
+
from cStringIO import StringIO
|
|
371
|
+
|
|
372
|
+
# capture stdout for a while
|
|
373
|
+
old_stdout = sys.stdout
|
|
374
|
+
sys.stdout = mystdout = StringIO()
|
|
375
|
+
|
|
376
|
+
sm = SkyModel()
|
|
377
|
+
str_params = ''
|
|
378
|
+
# replace all matching keywords with the values in the newdict
|
|
379
|
+
for key, val in overwite_params.items():
|
|
380
|
+
if key in sm.params:
|
|
381
|
+
sm.params[key] = val
|
|
382
|
+
str_params = str_params + str(key) + ' : ' + str(val) + ', '
|
|
383
|
+
status = sm.call(True) # set True for test
|
|
384
|
+
|
|
385
|
+
# restore original stdout
|
|
386
|
+
sys.stdout = old_stdout
|
|
387
|
+
toprint = label + '\t'
|
|
388
|
+
# print str_params
|
|
389
|
+
if(status == 'success'):
|
|
390
|
+
toprint += bcolors.OKGREEN + ' *** pass *** ' + bcolors.ENDC
|
|
391
|
+
else:
|
|
392
|
+
toprint += bcolors.FAIL + ' *** fail *** ' + bcolors.ENDC
|
|
393
|
+
print(toprint)
|
|
394
|
+
|
|
395
|
+
print(mystdout.getvalue())
|
|
396
|
+
|
|
397
|
+
def reset(self):
|
|
398
|
+
self.__init__()
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
# European Southern Observatory
|
|
5
|
+
# For any scientific or technical question,
|
|
6
|
+
# please contact usd-help@eso.org
|
|
7
|
+
|
|
8
|
+
from __future__ import print_function
|
|
9
|
+
import sys
|
|
10
|
+
import json
|
|
11
|
+
import getopt
|
|
12
|
+
from .skycalc import SkyModel
|
|
13
|
+
from .skycalc import AlmanacQuery
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def loadTxt(inputFile):
|
|
17
|
+
|
|
18
|
+
result = {}
|
|
19
|
+
|
|
20
|
+
lineN = 0
|
|
21
|
+
for line in inputFile.readlines():
|
|
22
|
+
|
|
23
|
+
lineN += 1
|
|
24
|
+
|
|
25
|
+
originalLine = line
|
|
26
|
+
line = line.strip()
|
|
27
|
+
if len(line) == 0 or line[0] == '#':
|
|
28
|
+
continue
|
|
29
|
+
line = line.split(':')
|
|
30
|
+
if len(line) > 2 and line[0].strip() == 'date':
|
|
31
|
+
line = [line[0], ':'.join(line[1::])]
|
|
32
|
+
elif len(line) != 2 or (len(line) == 2 and (line[0].strip() == '' or
|
|
33
|
+
line[1].strip() == '')):
|
|
34
|
+
print('WARNING: input file line ' + str(lineN) +
|
|
35
|
+
': wrong format: ignored.')
|
|
36
|
+
print(originalLine.rstrip())
|
|
37
|
+
continue
|
|
38
|
+
key = line[0].strip()
|
|
39
|
+
value = line[1].strip()
|
|
40
|
+
|
|
41
|
+
# Is it an integer?
|
|
42
|
+
try:
|
|
43
|
+
value = int(value)
|
|
44
|
+
except ValueError:
|
|
45
|
+
# Is it an float?
|
|
46
|
+
try:
|
|
47
|
+
value = float(value)
|
|
48
|
+
except ValueError:
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
result[key] = value
|
|
52
|
+
|
|
53
|
+
return result
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def fixObservatory(indic):
|
|
57
|
+
|
|
58
|
+
if 'observatory' in indic:
|
|
59
|
+
if indic['observatory'] == 'lasilla':
|
|
60
|
+
indic['observatory'] = '2400'
|
|
61
|
+
elif indic['observatory'] == 'paranal':
|
|
62
|
+
indic['observatory'] = '2640'
|
|
63
|
+
elif (indic['observatory'] == '3060m' or
|
|
64
|
+
indic['observatory'] == 'armazones'):
|
|
65
|
+
indic['observatory'] = '3060'
|
|
66
|
+
# elif indic['observatory'] == '5000m':
|
|
67
|
+
# indic['observatory'] = '5000'
|
|
68
|
+
else:
|
|
69
|
+
raise ValueError('Wrong Observatory name, please refer to the '
|
|
70
|
+
'documentation.')
|
|
71
|
+
return indic
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def usage():
|
|
75
|
+
|
|
76
|
+
print('usage: skycalc_cli -i|--in inputfile -o|--out file.fits ' +
|
|
77
|
+
'[-a|--alm almanacparameterfile] [-v|--verbose] [--version]')
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def main():
|
|
81
|
+
|
|
82
|
+
argv = sys.argv[1:]
|
|
83
|
+
|
|
84
|
+
almFilename = None
|
|
85
|
+
inputFilename = None
|
|
86
|
+
outputFilename = None
|
|
87
|
+
isVerbose = False
|
|
88
|
+
|
|
89
|
+
# Read command line arguments
|
|
90
|
+
try:
|
|
91
|
+
opts, args = getopt.getopt(argv, "hva:i:o:", ["help", "verbose",
|
|
92
|
+
"version",
|
|
93
|
+
"alm=", "in=", "out="])
|
|
94
|
+
except getopt.GetoptError:
|
|
95
|
+
usage()
|
|
96
|
+
sys.exit(1)
|
|
97
|
+
|
|
98
|
+
for opt, arg in opts:
|
|
99
|
+
if opt in ("-h", "--help"):
|
|
100
|
+
usage()
|
|
101
|
+
sys.exit()
|
|
102
|
+
elif opt == "--version":
|
|
103
|
+
from importlib.metadata import version as pkg_version
|
|
104
|
+
print('skycalc_cli version ' + pkg_version('skycalc_cli'))
|
|
105
|
+
sys.exit(0)
|
|
106
|
+
elif opt in ("-v", "--verbose"):
|
|
107
|
+
isVerbose = True
|
|
108
|
+
elif opt in ("-a", "--alm"):
|
|
109
|
+
almFilename = arg
|
|
110
|
+
elif opt in ("-i", "--in"):
|
|
111
|
+
inputFilename = arg
|
|
112
|
+
elif opt in ("-o", "--out"):
|
|
113
|
+
outputFilename = arg
|
|
114
|
+
|
|
115
|
+
if not ((inputFilename or almFilename) and outputFilename):
|
|
116
|
+
usage()
|
|
117
|
+
sys.exit(1)
|
|
118
|
+
|
|
119
|
+
dic = {}
|
|
120
|
+
|
|
121
|
+
# Query the Almanac if alm option is enabled
|
|
122
|
+
if almFilename:
|
|
123
|
+
|
|
124
|
+
# Read the input parameters
|
|
125
|
+
inputalmdic = None
|
|
126
|
+
try:
|
|
127
|
+
with open(almFilename, 'r') as f:
|
|
128
|
+
inputalmdic = json.load(f)
|
|
129
|
+
except ValueError:
|
|
130
|
+
with open(almFilename, 'r') as f:
|
|
131
|
+
inputalmdic = loadTxt(f)
|
|
132
|
+
|
|
133
|
+
if not inputalmdic:
|
|
134
|
+
raise ValueError('Error: cannot read' + almFilename)
|
|
135
|
+
|
|
136
|
+
alm = AlmanacQuery(inputalmdic)
|
|
137
|
+
dic = alm.query()
|
|
138
|
+
|
|
139
|
+
if isVerbose:
|
|
140
|
+
print('Data retrieved from the Almanac:')
|
|
141
|
+
for key, value in dic.items():
|
|
142
|
+
print('\t' + str(key) + ': ' + str(value))
|
|
143
|
+
|
|
144
|
+
if inputFilename:
|
|
145
|
+
|
|
146
|
+
# Read input parameters
|
|
147
|
+
inputdic = None
|
|
148
|
+
try:
|
|
149
|
+
with open(inputFilename, 'r') as f:
|
|
150
|
+
inputdic = json.load(f)
|
|
151
|
+
except ValueError:
|
|
152
|
+
with open(inputFilename, 'r') as f:
|
|
153
|
+
inputdic = loadTxt(f)
|
|
154
|
+
|
|
155
|
+
if not inputdic:
|
|
156
|
+
raise ValueError('Error: cannot read ' + inputFilename)
|
|
157
|
+
|
|
158
|
+
# Override input parameters
|
|
159
|
+
if isVerbose:
|
|
160
|
+
print('Data overridden by the user\'s input file:')
|
|
161
|
+
for key, value in inputdic.items():
|
|
162
|
+
if isVerbose and key in dic:
|
|
163
|
+
print('\t' + str(key) + ': ' + str(value))
|
|
164
|
+
dic[key] = value
|
|
165
|
+
|
|
166
|
+
# Fix the observatory to fit the backend
|
|
167
|
+
try:
|
|
168
|
+
dic = fixObservatory(dic)
|
|
169
|
+
except ValueError:
|
|
170
|
+
raise
|
|
171
|
+
|
|
172
|
+
if isVerbose:
|
|
173
|
+
print('Data submitted to SkyCalc:')
|
|
174
|
+
for key, value in dic.items():
|
|
175
|
+
print('\t' + str(key) + ': ' + str(value))
|
|
176
|
+
|
|
177
|
+
# Get the Sky
|
|
178
|
+
skyModel = SkyModel()
|
|
179
|
+
skyModel.callwith(dic)
|
|
180
|
+
skyModel.write(outputFilename)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2016 The Python Packaging Authority (PyPA)
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
5
|
+
the Software without restriction, including without limitation the rights to
|
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
8
|
+
so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: skycalc_cli
|
|
3
|
+
Version: 1.5
|
|
4
|
+
Summary: ESO SkyCalc Command Line Interface
|
|
5
|
+
Home-page: http://www.eso.org/observing/etc/bin/gen/form?INS.MODE=swspectr+INS.NAME=SKYCALC
|
|
6
|
+
Author: European Southern Observatory
|
|
7
|
+
Author-email: usd-help@eso.org
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: sky model observatory telescope astronomy ephemeris sun moon
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
15
|
+
Requires-Python: >=3.8
|
|
16
|
+
License-File: LICENSE.txt
|
|
17
|
+
Requires-Dist: requests
|
|
18
|
+
|
|
19
|
+
######################################
|
|
20
|
+
SkyCalc Command Line Interface (CLI)
|
|
21
|
+
######################################
|
|
22
|
+
|
|
23
|
+
============
|
|
24
|
+
Installation
|
|
25
|
+
============
|
|
26
|
+
|
|
27
|
+
The SkyCalc CLI requires Python 3.8 or later (Python 2 users: pip installs
|
|
28
|
+
the last compatible release, 1.4). Install the SkyCalc CLI
|
|
29
|
+
on your computer using the command: ::
|
|
30
|
+
|
|
31
|
+
pip install --user skycalc_cli
|
|
32
|
+
|
|
33
|
+
This will also install skycalc_cli's dependency, i.e. the requests package for
|
|
34
|
+
making HTTP calls.
|
|
35
|
+
|
|
36
|
+
=============
|
|
37
|
+
Documentation
|
|
38
|
+
=============
|
|
39
|
+
|
|
40
|
+
Documentation can be found on the `ESO SkyCalc Web Page <https://www.eso.org/observing/etc/doc/skycalc/helpskycalccli.html>`_.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
skycalc_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
skycalc_cli/skycalc.py,sha256=WJcah0qF3vwB-eeQlX3mdXthCKVM_uL8fpok7O-0TKc,14834
|
|
3
|
+
skycalc_cli/skycalc_cli.py,sha256=ITKOzYwEP8IMG24yu2VIA_v3bLgOWEoXHRNPgVUVPYo,5029
|
|
4
|
+
skycalc_cli-1.5.dist-info/LICENSE.txt,sha256=ceC9ZJOV9H6CtQDcYmHOS46NA3dHJ_WD4J9blH513pc,1081
|
|
5
|
+
skycalc_cli-1.5.dist-info/METADATA,sha256=J7N9RyEF1C9xMSSJjqwUO3sb6FvrkrArudTuJeOambI,1320
|
|
6
|
+
skycalc_cli-1.5.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
|
|
7
|
+
skycalc_cli-1.5.dist-info/entry_points.txt,sha256=hD1h_Z4cdfhVu7__PZP_a_3mBcvMB3nt8aqIAqsbkw8,61
|
|
8
|
+
skycalc_cli-1.5.dist-info/top_level.txt,sha256=m2M2VsOhj1sg5gsn6gm0UXHDaW_5-rLrJ_CkHUGbj2Q,12
|
|
9
|
+
skycalc_cli-1.5.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
skycalc_cli
|