PyNOT-redux 2.1.2__tar.gz → 2.2__tar.gz

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.
Files changed (117) hide show
  1. {pynot_redux-2.1.2 → pynot_redux-2.2}/PKG-INFO +14 -2
  2. {pynot_redux-2.1.2 → pynot_redux-2.2}/PyNOT_redux.egg-info/PKG-INFO +14 -2
  3. {pynot_redux-2.1.2 → pynot_redux-2.2}/PyNOT_redux.egg-info/SOURCES.txt +21 -1
  4. pynot_redux-2.2/pynot/VERSION +1 -0
  5. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/data/io.py +8 -6
  6. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/data/organizer.py +6 -1
  7. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/extract_gui.py +13 -31
  8. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/fitsio.py +35 -15
  9. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/identify_gui.py +31 -3
  10. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/main.py +52 -1
  11. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/redux.py +2 -2
  12. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/scombine.py +31 -16
  13. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/skysub.py +1 -0
  14. pynot_redux-2.2/pynot/viewer/__init__.py +0 -0
  15. pynot_redux-2.2/pynot/viewer/containers.py +82 -0
  16. pynot_redux-2.2/pynot/viewer/default_linelists.json +103 -0
  17. pynot_redux-2.2/pynot/viewer/dust.py +149 -0
  18. pynot_redux-2.2/pynot/viewer/linelists.py +170 -0
  19. pynot_redux-2.2/pynot/viewer/messages.py +34 -0
  20. pynot_redux-2.2/pynot/viewer/models.py +300 -0
  21. pynot_redux-2.2/pynot/viewer/notes.py +255 -0
  22. pynot_redux-2.2/pynot/viewer/spectrum.py +386 -0
  23. pynot_redux-2.2/pynot/viewer/tablemodels.py +258 -0
  24. pynot_redux-2.2/pynot/viewer/targets.py +115 -0
  25. pynot_redux-2.2/pynot/viewer/templates/Assef_2010.dat +323 -0
  26. pynot_redux-2.2/pynot/viewer/templates/FeII.Vestergaard.dat +4182 -0
  27. pynot_redux-2.2/pynot/viewer/templates/FeIII.Vestergaard.dat +4182 -0
  28. pynot_redux-2.2/pynot/viewer/templates/Hernan_Caballero_quasar_NIR_template.txt +512 -0
  29. pynot_redux-2.2/pynot/viewer/templates/QSO_selsing.dat +5334 -0
  30. pynot_redux-2.2/pynot/viewer/templates/QSO_template.Krawczyk_all.dat +273 -0
  31. pynot_redux-2.2/pynot/viewer/templates/QSO_template.Krawczyk_high.dat +273 -0
  32. pynot_redux-2.2/pynot/viewer/templates/QSO_template.Krawczyk_low.dat +273 -0
  33. pynot_redux-2.2/pynot/viewer/viewer.py +944 -0
  34. pynot_redux-2.1.2/pynot/VERSION +0 -1
  35. {pynot_redux-2.1.2 → pynot_redux-2.2}/.gitattributes +0 -0
  36. {pynot_redux-2.1.2 → pynot_redux-2.2}/.github/dependabot.yml +0 -0
  37. {pynot_redux-2.1.2 → pynot_redux-2.2}/.gitignore +0 -0
  38. {pynot_redux-2.1.2 → pynot_redux-2.2}/LICENSE +0 -0
  39. {pynot_redux-2.1.2 → pynot_redux-2.2}/MANIFEST.in +0 -0
  40. {pynot_redux-2.1.2 → pynot_redux-2.2}/PyNOT_redux.egg-info/dependency_links.txt +0 -0
  41. {pynot_redux-2.1.2 → pynot_redux-2.2}/PyNOT_redux.egg-info/entry_points.txt +0 -0
  42. {pynot_redux-2.1.2 → pynot_redux-2.2}/PyNOT_redux.egg-info/requires.txt +0 -0
  43. {pynot_redux-2.1.2 → pynot_redux-2.2}/PyNOT_redux.egg-info/top_level.txt +0 -0
  44. {pynot_redux-2.1.2 → pynot_redux-2.2}/README.md +0 -0
  45. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/.extract_msg +0 -0
  46. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/.identify_msg +0 -0
  47. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/.instrument.cfg +0 -0
  48. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/.response_msg +0 -0
  49. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/__init__.py +0 -0
  50. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/alfosc.py +0 -0
  51. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/HeAr_linelist.dat +0 -0
  52. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/HeNe_linelist.dat +0 -0
  53. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/ThAr_linelist.dat +0 -0
  54. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/al-gr18_pixeltable.dat +0 -0
  55. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/al-gr19_pixeltable.dat +0 -0
  56. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/al-gr4_pixeltable.dat +0 -0
  57. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/al-gr7_pixeltable.dat +0 -0
  58. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/alfosc_filters.dat +0 -0
  59. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/default_options.yml +0 -0
  60. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/default_options_img.yml +0 -0
  61. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/ef-gr13_pixeltable.dat +0 -0
  62. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/ef-gr14_pixeltable.dat +0 -0
  63. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/ef-gr1_pixeltable.dat +0 -0
  64. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/ef-gr3_pixeltable.dat +0 -0
  65. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/efosc_filters.dat +0 -0
  66. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/lapalma.ext +0 -0
  67. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/lasilla.ext +0 -0
  68. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/paranal.ext +0 -0
  69. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/bd174708.dat +0 -0
  70. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/bd262606.dat +0 -0
  71. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/bd332642.dat +0 -0
  72. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/bd75325.dat +0 -0
  73. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/eg21.dat +0 -0
  74. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/feige110.dat +0 -0
  75. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/feige34.dat +0 -0
  76. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/gd153.dat +0 -0
  77. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/gd50.dat +0 -0
  78. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/gd71.dat +0 -0
  79. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/hd19445.dat +0 -0
  80. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/hd84937.dat +0 -0
  81. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/hd93521.dat +0 -0
  82. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/he3.dat +0 -0
  83. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/hiltner600.dat +0 -0
  84. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/ltt3864.dat +0 -0
  85. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/tcs_namelist.txt +0 -0
  86. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calib/std/wolf1346.dat +0 -0
  87. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/calibs.py +0 -0
  88. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/data/__init__.py +0 -0
  89. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/data/alfosc.rules +0 -0
  90. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/data/efosc.rules +0 -0
  91. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/data/help/welcome_msg_extract.html +0 -0
  92. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/data/help/welcome_msg_identify.html +0 -0
  93. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/data/help/welcome_msg_response.html +0 -0
  94. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/data/obs.py +0 -0
  95. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/efosc.py +0 -0
  96. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/extraction.py +0 -0
  97. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/functions.py +0 -0
  98. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/images.py +0 -0
  99. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/insconfig.py +0 -0
  100. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/logging.py +0 -0
  101. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/operations.py +0 -0
  102. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/phot.py +0 -0
  103. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/phot_redux.py +0 -0
  104. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/reports.py +0 -0
  105. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/response.py +0 -0
  106. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/response_gui.py +0 -0
  107. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/scired.py +0 -0
  108. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/tasks.py +0 -0
  109. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/transients.py +0 -0
  110. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/txtio.py +0 -0
  111. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/wavecal.py +0 -0
  112. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/wcs.py +0 -0
  113. {pynot_redux-2.1.2 → pynot_redux-2.2}/pynot/welcome.py +0 -0
  114. {pynot_redux-2.1.2 → pynot_redux-2.2}/pyproject.toml +0 -0
  115. {pynot_redux-2.1.2 → pynot_redux-2.2}/requirements.txt +0 -0
  116. {pynot_redux-2.1.2 → pynot_redux-2.2}/setup.cfg +0 -0
  117. {pynot_redux-2.1.2 → pynot_redux-2.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: PyNOT-redux
3
- Version: 2.1.2
3
+ Version: 2.2
4
4
  Summary: Data Reduction Pipeline for NOT/ALFOSC
5
5
  Home-page: https://github.com/jkrogager/PyNOT
6
6
  Author: Jens-Kristian Krogager
@@ -28,6 +28,18 @@ Requires-Dist: sep
28
28
  Requires-Dist: astroalign
29
29
  Requires-Dist: astroquery
30
30
  Requires-Dist: beautifulsoup4
31
+ Dynamic: author
32
+ Dynamic: author-email
33
+ Dynamic: classifier
34
+ Dynamic: description
35
+ Dynamic: description-content-type
36
+ Dynamic: home-page
37
+ Dynamic: keywords
38
+ Dynamic: license
39
+ Dynamic: license-file
40
+ Dynamic: requires-dist
41
+ Dynamic: requires-python
42
+ Dynamic: summary
31
43
 
32
44
  # PyNOT-redux
33
45
  A Data Processing Pipeline for ALFOSC at the Nordic Optical Telescope
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: PyNOT-redux
3
- Version: 2.1.2
3
+ Version: 2.2
4
4
  Summary: Data Reduction Pipeline for NOT/ALFOSC
5
5
  Home-page: https://github.com/jkrogager/PyNOT
6
6
  Author: Jens-Kristian Krogager
@@ -28,6 +28,18 @@ Requires-Dist: sep
28
28
  Requires-Dist: astroalign
29
29
  Requires-Dist: astroquery
30
30
  Requires-Dist: beautifulsoup4
31
+ Dynamic: author
32
+ Dynamic: author-email
33
+ Dynamic: classifier
34
+ Dynamic: description
35
+ Dynamic: description-content-type
36
+ Dynamic: home-page
37
+ Dynamic: keywords
38
+ Dynamic: license
39
+ Dynamic: license-file
40
+ Dynamic: requires-dist
41
+ Dynamic: requires-python
42
+ Dynamic: summary
31
43
 
32
44
  # PyNOT-redux
33
45
  A Data Processing Pipeline for ALFOSC at the Nordic Optical Telescope
@@ -91,4 +91,24 @@ pynot/data/obs.py
91
91
  pynot/data/organizer.py
92
92
  pynot/data/help/welcome_msg_extract.html
93
93
  pynot/data/help/welcome_msg_identify.html
94
- pynot/data/help/welcome_msg_response.html
94
+ pynot/data/help/welcome_msg_response.html
95
+ pynot/viewer/__init__.py
96
+ pynot/viewer/containers.py
97
+ pynot/viewer/default_linelists.json
98
+ pynot/viewer/dust.py
99
+ pynot/viewer/linelists.py
100
+ pynot/viewer/messages.py
101
+ pynot/viewer/models.py
102
+ pynot/viewer/notes.py
103
+ pynot/viewer/spectrum.py
104
+ pynot/viewer/tablemodels.py
105
+ pynot/viewer/targets.py
106
+ pynot/viewer/viewer.py
107
+ pynot/viewer/templates/Assef_2010.dat
108
+ pynot/viewer/templates/FeII.Vestergaard.dat
109
+ pynot/viewer/templates/FeIII.Vestergaard.dat
110
+ pynot/viewer/templates/Hernan_Caballero_quasar_NIR_template.txt
111
+ pynot/viewer/templates/QSO_selsing.dat
112
+ pynot/viewer/templates/QSO_template.Krawczyk_all.dat
113
+ pynot/viewer/templates/QSO_template.Krawczyk_high.dat
114
+ pynot/viewer/templates/QSO_template.Krawczyk_low.dat
@@ -0,0 +1 @@
1
+ 2.2
@@ -23,6 +23,7 @@ def get_header_info(fname):
23
23
  slit = hdr['SLIT']
24
24
  filter = '...'
25
25
  shape = "..."
26
+ binning = "NxM_NA"
26
27
  else:
27
28
  hdr = instrument.get_header(fname)
28
29
  object = instrument.get_object(hdr)
@@ -31,7 +32,8 @@ def get_header_info(fname):
31
32
  slit = instrument.get_slit(hdr)
32
33
  filter = instrument.get_filter(hdr)
33
34
  shape = "%ix%i" % (hdr['NAXIS1'], hdr['NAXIS2'])
34
- return object, exptime, grism, slit, filter, shape
35
+ binning = instrument.get_binning_from_hdr(hdr)
36
+ return object, exptime, grism, slit, filter, shape, binning
35
37
 
36
38
 
37
39
  def save_database(database, output_fname):
@@ -46,20 +48,20 @@ def save_database(database, output_fname):
46
48
  for fname in sorted_files:
47
49
  try:
48
50
  if fname[0] == '#':
49
- object, exptime, grism, slit, filter, shape = get_header_info(fname[1:])
51
+ object, exptime, grism, slit, filter, shape, binning = get_header_info(fname[1:])
50
52
  else:
51
- object, exptime, grism, slit, filter, shape = get_header_info(fname)
53
+ object, exptime, grism, slit, filter, shape, binning = get_header_info(fname)
52
54
  except FileNotFoundError:
53
55
  print("[WARNING] - File not found: %s" % fname)
54
56
  continue
55
57
  except Exception:
56
58
  print("[WARNING] - Problem reading header information: %s" % fname)
57
- object, exptime, grism, slit, filter, shape = '-', '-', '-', '-', '-', '-'
58
- file_list.append((fname, filetype, object, exptime, grism, slit, filter, shape))
59
+ object, exptime, grism, slit, filter, shape, binning = '-', '-', '-', '-', '-', '-', '-'
60
+ file_list.append((fname, filetype, object, exptime, grism, slit, filter, shape, binning))
59
61
  file_list = np.array(file_list, dtype=str)
60
62
  if len(file_list) == 0:
61
63
  continue
62
- header_names = ('FILENAME', 'TYPE', 'OBJECT', 'EXPTIME', 'GRISM', 'SLIT', 'FILTER', 'SHAPE')
64
+ header_names = ('FILENAME', 'TYPE', 'OBJECT', 'EXPTIME', 'GRISM', 'SLIT', 'FILTER', 'SHAPE', 'BINNING')
63
65
  max_len = np.max(veclen(file_list), 0)
64
66
  max_len = np.max([max_len, [len(n) for n in header_names]], 0)
65
67
  line_fmt = " ".join(["%-{}s".format(n) for n in max_len])
@@ -200,7 +200,8 @@ def sort_bias(file_list, date=False):
200
200
  for fname in file_list:
201
201
  hdr = instrument.get_header(fname)
202
202
  size = "%ix%i" % (hdr['NAXIS1'], hdr['NAXIS2'])
203
- file_id = size
203
+ binning = instrument.get_binning_from_hdr(hdr)
204
+ file_id = f"{size}_{binning}"
204
205
  if date:
205
206
  date_str = instrument.get_date(hdr)
206
207
  if 'T' in date_str:
@@ -677,6 +678,10 @@ class RawImage(object):
677
678
  criteria.append(this_filter == self.filter)
678
679
  criteria_name.append('filter')
679
680
 
681
+ if debug:
682
+ print(criteria_name)
683
+ print(criteria)
684
+
680
685
  if np.all(criteria):
681
686
  matches.append(fname)
682
687
  all_times.append(this_mjd)
@@ -51,27 +51,6 @@ def run_gui(input_fname, output_fname, app=None, **ext_kwargs):
51
51
  del gui
52
52
 
53
53
 
54
- # def save_ascii_spectrum(fname, wl, flux, err, hdr, bg=None):
55
- # """Write spectrum to an ascii text file with header saved to separate text file."""
56
- # if bg is not None:
57
- # data_table = np.column_stack([wl, flux, err, bg])
58
- # fmt = "%12.4f % .3e %.3e %.3e"
59
- # col_names = "# Wavelength Flux Error Sky"
60
- # else:
61
- # data_table = np.column_stack([wl, flux, err])
62
- # fmt = "%12.4f % .3e %.3e"
63
- # col_names = "# Wavelength Flux Error"
64
- #
65
- # basename, ext = os.path.splitext(fname)
66
- # header_fname = basename + '_hdr.txt'
67
- #
68
- # with open(fname, 'w') as output:
69
- # output.write(col_names + "\n")
70
- # np.savetxt(output, data_table, fmt=fmt)
71
- # hdr.tofile(header_fname, sep='\n', endcard=False, padding=False, overwrite=True)
72
- # return True, "File saved successfully"
73
-
74
-
75
54
  def get_FWHM(y, x=None):
76
55
  """
77
56
  Measure the FWHM of the profile given as `y`.
@@ -285,8 +264,8 @@ class BackgroundModel(object):
285
264
  self.patches[index].remove()
286
265
  self.patches.pop(index)
287
266
  self.ranges[index] = [i_low, i_high]
288
- self.vlines[index][0].set_xdata(i_low)
289
- self.vlines[index][1].set_xdata(i_high)
267
+ self.vlines[index][0].set_xdata([i_low])
268
+ self.vlines[index][1].set_xdata([i_high])
290
269
  patch = self.axis.axvspan(i_low, i_high, color='#29b6f6', alpha=0.3, picker=True)
291
270
  self.patches.insert(index, patch)
292
271
 
@@ -407,13 +386,13 @@ class TraceModel(object):
407
386
 
408
387
  def set_centroid(self, cen):
409
388
  self.cen = cen
410
- self.vlines[1].set_xdata(cen)
389
+ self.vlines[1].set_xdata([cen])
411
390
 
412
391
  def set_range(self, lower, upper):
413
392
  self.lower = lower
414
393
  self.upper = upper
415
- self.vlines[0].set_xdata(lower)
416
- self.vlines[2].set_xdata(upper)
394
+ self.vlines[0].set_xdata([lower])
395
+ self.vlines[2].set_xdata([upper])
417
396
 
418
397
  def get_range(self):
419
398
  return (self.lower, self.upper)
@@ -1380,11 +1359,11 @@ class ExtractGUI(QtWidgets.QMainWindow):
1380
1359
 
1381
1360
  elif artist.get_label() == 'lower':
1382
1361
  trace_model.lower = new_position
1383
- artist.set_xdata(new_position)
1362
+ artist.set_xdata([new_position])
1384
1363
 
1385
1364
  elif artist.get_label() == 'upper':
1386
1365
  trace_model.upper = new_position
1387
- artist.set_xdata(new_position)
1366
+ artist.set_xdata([new_position])
1388
1367
  self.canvas_spsf.draw()
1389
1368
 
1390
1369
  def on_release(self, event):
@@ -1394,12 +1373,12 @@ class ExtractGUI(QtWidgets.QMainWindow):
1394
1373
  if artist.get_label() == 'lower':
1395
1374
  if trace_model.lower > trace_model.cen - 1:
1396
1375
  trace_model.lower = trace_model.cen - 1
1397
- artist.set_xdata(trace_model.cen - 1)
1376
+ artist.set_xdata([trace_model.cen - 1])
1398
1377
  self.canvas_spsf.draw()
1399
1378
  elif artist.get_label() == 'upper':
1400
1379
  if trace_model.upper < trace_model.cen + 1:
1401
1380
  trace_model.upper = trace_model.cen + 1
1402
- artist.set_xdata(trace_model.cen + 1)
1381
+ artist.set_xdata([trace_model.cen + 1])
1403
1382
  self.canvas_spsf.draw()
1404
1383
  centroid_shift = trace_model.cen - old_centroid
1405
1384
  if np.abs(centroid_shift) > 0:
@@ -1669,7 +1648,10 @@ class ExtractGUI(QtWidgets.QMainWindow):
1669
1648
  def update_xmask_in_points(self):
1670
1649
  # Clear old shapes:
1671
1650
  for item in self.xmask:
1672
- item.remove()
1651
+ try:
1652
+ item.remove()
1653
+ except NotImplementedError:
1654
+ pass
1673
1655
  self.xmask = list()
1674
1656
  xmin, xmax, ymin, ymax = self.get_limits()
1675
1657
  if xmin > 0 or xmax < self.image2d.data.shape[1]+1:
@@ -3,6 +3,8 @@ __author__ = "Jens-Kristian Krogager"
3
3
 
4
4
  import warnings
5
5
  from astropy.io import fits
6
+ from astropy import units as u
7
+ from astropy.table import QTable
6
8
  import numpy as np
7
9
  import os
8
10
 
@@ -129,17 +131,17 @@ def get_wavelength_from_header(hdr):
129
131
 
130
132
  # -- These names are used to define proper column names for Wavelength, Flux and Error:
131
133
  wavelength_column_names = ['wl', 'lam', 'lambda', 'loglam', 'wave', 'wavelength', 'awav']
132
- flux_column_names = ['data', 'spec', 'flux', 'flam', 'fnu', 'flux_density']
133
- error_column_names = ['err', 'sig', 'error', 'ivar', 'sigma', 'var']
134
+ flux_column_names = ['data', 'spec', 'flux', 'flam', 'fnu', 'flux_density', 'reduced_flux']
135
+ error_column_names = ['err', 'sig', 'error', 'ivar', 'sigma', 'var', 'err_flux', 'flux_ivar', 'flux_var', 'flux_err']
134
136
  mask_column_names = ['mask', 'qual', 'dq', 'qc']
135
137
 
136
138
  # -- These names are used to define proper ImageHDU names for Flux and Error:
137
139
  flux_HDU_names = ['FLUX', 'SCI', 'FLAM', 'FNU']
138
- error_HDU_names = ['ERR', 'ERRS', 'SIG', 'SIGMA', 'ERROR', 'ERRORS', 'IVAR', 'VAR']
140
+ error_HDU_names = ['ERR', 'ERRS', 'SIG', 'SIGMA', 'ERROR', 'ERRORS', 'IVAR', 'VAR', 'ERR_FLUX']
139
141
  mask_HDU_names = ['MASK', 'QUAL', 'QC', 'DQ']
140
142
 
141
143
 
142
- def get_spectrum_fits_table(tbdata):
144
+ def get_spectrum_fits_table(table_hdu):
143
145
  """
144
146
  Scan the TableData for columns containing wavelength, flux, error and mask.
145
147
  All arrays of {wavelength, flux and error} must be present.
@@ -163,7 +165,12 @@ def get_spectrum_fits_table(tbdata):
163
165
  Numpy boolean array of pixel mask. `True` if the pixel is 'good',
164
166
  `False` if the pixel is bad and should not be used.
165
167
  """
166
- table_names = [name.lower() for name in tbdata.names]
168
+ tbdata = QTable.read(table_hdu)
169
+ # table_names = [name.lower() for name in tbdata.names]
170
+ for colname in tbdata.colnames:
171
+ tbdata.rename_column(colname, colname.lower())
172
+ table_names = tbdata.colnames
173
+
167
174
  wl_in_table = False
168
175
  for colname in wavelength_column_names:
169
176
  if colname in table_names:
@@ -185,9 +192,10 @@ def get_spectrum_fits_table(tbdata):
185
192
  for colname in error_column_names:
186
193
  if colname in table_names:
187
194
  error_in_table = True
188
- if colname == 'ivar':
189
- error = 1./np.sqrt(tbdata[colname])
190
- elif colname == 'var':
195
+ if 'ivar' in colname.lower():
196
+ with np.errstate(divide='ignore', invalid='ignore'):
197
+ error = 1 / np.sqrt(tbdata[colname])
198
+ elif 'var' in colname.lower():
191
199
  error = np.sqrt(tbdata[colname])
192
200
  else:
193
201
  error = tbdata[colname]
@@ -208,6 +216,7 @@ def get_spectrum_fits_table(tbdata):
208
216
 
209
217
  return wavelength.flatten(), data.flatten(), error.flatten(), mask.flatten()
210
218
 
219
+
211
220
  # Hack the doc-string of the function to input the variable names:
212
221
  output_column_names = {'WL_COL_NAMES': wavelength_column_names,
213
222
  'FLUX_COL_NAMES': flux_column_names,
@@ -270,6 +279,7 @@ def get_spectrum_hdulist(HDUlist):
270
279
 
271
280
  return data, error, mask, data_hdr
272
281
 
282
+
273
283
  # Hack the doc-string of the function to input the variable names:
274
284
  output_hdu_names = {'FLUX_HDU_NAMES': flux_HDU_names,
275
285
  'ERR_HDU_NAMES': error_HDU_names}
@@ -363,17 +373,19 @@ def load_fits_spectrum(fname, ext=None, iraf_obj=None):
363
373
  else:
364
374
  is_fits_table = isinstance(HDUlist[1], fits.BinTableHDU) or isinstance(HDUlist[1], fits.TableHDU)
365
375
  if is_fits_table:
366
- if ext:
367
- tbdata = HDUlist[ext].data
368
- data_hdr = HDUlist[ext].header
369
- else:
370
- tbdata = HDUlist[1].data
371
- data_hdr = HDUlist[1].header
376
+ if not ext:
377
+ ext = 1
378
+
379
+ table_hdu = HDUlist[ext]
380
+ tbdata = table_hdu.data
381
+ data_hdr = table_hdu.header
372
382
 
373
383
  has_multi_extensions = len(HDUlist) > 2
374
384
  if has_multi_extensions and (ext is None):
375
385
  msg = "[WARNING] - More than one data extension detected in the file"
376
- wavelength, data, error, mask = get_spectrum_fits_table(tbdata)
386
+ wavelength, data, error, mask = get_spectrum_fits_table(table_hdu)
387
+ if 'CUNIT1' in data_hdr:
388
+ wavelength *= u.Unit(data_hdr['CUNIT1'])
377
389
  return wavelength, data, error, mask, data_hdr, msg
378
390
 
379
391
  elif len(HDUlist) == 2:
@@ -536,3 +548,11 @@ def create_error_image(base_fname, overwrite=False):
536
548
  hdu.append(ext)
537
549
  output_msg = " - Successfully created an error image"
538
550
  return output_msg
551
+
552
+
553
+ def detect_4most_MEC(fname):
554
+ prim = fits.getheader(fname)
555
+ is_qmost = prim.get('INSTRUME', 'NONE').strip() == 'QMOST'
556
+ hdr = fits.getheader(fname, 1)
557
+ has_mec_extname = hdr.get('EXTNAME') in ['OBMETATAB', 'SPECTAB', 'FIBMETATAB']
558
+ return is_qmost & has_mec_extname
@@ -21,6 +21,7 @@ from PyQt5 import QtWidgets
21
21
  from scipy.optimize import curve_fit
22
22
  from numpy.polynomial import Chebyshev
23
23
  from astropy.io import fits
24
+ from astropy.table import Table
24
25
 
25
26
  from pynot import instrument
26
27
  from pynot.data import organizer
@@ -226,7 +227,7 @@ def load_linelist(fname):
226
227
 
227
228
  class GraphicInterface(QtWidgets.QMainWindow):
228
229
  def __init__(self, arc_fname='', grism_name='', pixtable='', linelist_fname='', output='',
229
- dispaxis=2, order_wl=3, air=False, loc=-1, parent=None, locked=False, vac=True):
230
+ dispaxis=2, order_wl=3, air=False, loc=-1, parent=None, locked=False):
230
231
  QtWidgets.QMainWindow.__init__(self, parent)
231
232
  self.setWindowTitle('PyNOT: Identify Arc Lines')
232
233
  self._main = QtWidgets.QWidget()
@@ -572,9 +573,9 @@ class GraphicInterface(QtWidgets.QMainWindow):
572
573
  self.reftable.setItem(rowPosition, 1, item2)
573
574
 
574
575
  def load_spectrum(self, arc_fname=None):
575
- if arc_fname is False:
576
+ if not arc_fname:
576
577
  current_dir = './'
577
- filters = "FITS files (*.fits | *.fit)"
578
+ filters = "FITS or txt files (*.fits | *.fit | *.txt | *.csv)"
578
579
  arc_fname = QtWidgets.QFileDialog.getOpenFileName(self, 'Open Pixeltable', current_dir, filters)
579
580
  arc_fname = str(arc_fname[0])
580
581
  if self.first_time_open:
@@ -583,6 +584,31 @@ class GraphicInterface(QtWidgets.QMainWindow):
583
584
  self.first_time_open = False
584
585
 
585
586
  if arc_fname:
587
+
588
+ try:
589
+ tab1d = Table.read(arc_fname, hdu=1, format='fits')
590
+ self.pix = tab1d['pixel']
591
+ self.arc1d = tab1d['flux']
592
+ self.update_arc1d()
593
+ return
594
+
595
+ except (OSError, ValueError):
596
+ try:
597
+ tab1d = Table.read(arc_fname, format='ascii')
598
+ tab1d.rename_column(tab1d.colnames[0], 'pixel')
599
+ tab1d.rename_column(tab1d.colnames[1], 'flux')
600
+ self.pix = tab1d['pixel']
601
+ self.arc1d = tab1d['flux']
602
+ self.update_arc1d()
603
+ return
604
+ except Exception:
605
+ if 'fits' in arc_fname.lower():
606
+ pass
607
+ else:
608
+ error_msg = "Could not read the spectrum. Must be a FITS or ASCII table, or a FITS 2D image"
609
+ QtWidgets.QMessageBox.critical(None, 'Invalid Format!', error_msg)
610
+ return
611
+
586
612
  self.arc_fname = arc_fname
587
613
  with fits.open(arc_fname) as hdu:
588
614
  primhdr = hdu[0].header
@@ -647,7 +673,9 @@ class GraphicInterface(QtWidgets.QMainWindow):
647
673
 
648
674
  self.arc1d = np.nanmean(self.arc_image_data[:, ilow:ihigh], axis=1)
649
675
  self.pix = instrument.create_pixel_array(self.primhdr, self.dispaxis)
676
+ self.update_arc1d()
650
677
 
678
+ def update_arc1d(self):
651
679
  self.ax.lines[0].set_data(self.pix, self.arc1d)
652
680
  self.ax.relim()
653
681
  self.ax.autoscale()
@@ -372,6 +372,10 @@ def main(inspect=False):
372
372
  help="Set this option for 2D spectra of extended sources to turn off automatic localization")
373
373
  parser_scomb.add_argument("--mef", action="store_false",
374
374
  help="Set this option to save output as a multiextension FITS file instead of a FITS table.")
375
+ parser_scomb.add_argument("--imin", type=int, default=0,
376
+ help="Slice each input spectrum along dispersion axis [imin:imax], only for 1D spectra")
377
+ parser_scomb.add_argument("--imax", type=int, default=None,
378
+ help="Slice each input spectrum along dispersion axis [imin:imax], only for 1D spectra")
375
379
 
376
380
 
377
381
  # -- extract :: Extraction of 1D spectrum from 2D
@@ -389,6 +393,31 @@ def main(inspect=False):
389
393
  set_default_pars(parser_ext, section='extract', default_type=int,
390
394
  ignore_pars=['interactive'])
391
395
 
396
+ # -- view :: View 1D spectra in an interactive window
397
+ parser_view = tasks.add_parser('view', formatter_class=set_help_width(31),
398
+ help="Display and interact with 1D spectra")
399
+ parser_view.add_argument("files", type=str, nargs='*',
400
+ help="Filenames of spectral data to load. Each file is loaded as one target")
401
+ parser_view.add_argument("-t", "--table", type=str,
402
+ help="Filename of association table. Each line in the file gives a comma-separated list of filenames. "
403
+ "All files in one line are loaded as a single target.")
404
+ parser_view.add_argument("-c", "--container", action="store_true",
405
+ help="Load the file(s) as a FITS container "
406
+ "(such as 4MOST MEC, SDSS bricks, or a large collection of files)")
407
+ parser_view.add_argument("-l", "--list", type=str, default='',
408
+ help="File list. Name of a text file where each line is the path of one spectrum."
409
+ "(Useful for loading many files in `container` mode with `-c`)")
410
+ # redshift_table=None, z_col=None, name_col=None, cls_col=None
411
+ parser_view.add_argument("-z", type=str, default='',
412
+ help="Filename of redshift catalog with a name, redshift and spectral type column. "
413
+ "Set column names using `--ncol`, `--zcol`, `--tcol`")
414
+ parser_view.add_argument("--ncol", type=str, default='',
415
+ help="Name of the target name column in `-z REDSHIFT_TABLE`")
416
+ parser_view.add_argument("--zcol", type=str, default='',
417
+ help="Name of the redshift column in `-z REDSHIFT_TABLE`")
418
+ parser_view.add_argument("--tcol", type=str, default='',
419
+ help="Name of the spectral type column in `-z REDSHIFT_TABLE`")
420
+
392
421
 
393
422
  # Spectral Redux:
394
423
  parser_redux = tasks.add_parser('spex', formatter_class=set_help_width(30),
@@ -770,7 +799,7 @@ def main(inspect=False):
770
799
 
771
800
  if data_is_1d:
772
801
  out_args = combine_1d(filelist, output=args.output, method=args.method,
773
- scale=args.scale, table_output=args.mef)
802
+ scale=args.scale, table_output=args.mef, imin=args.imin, imax=args.imax)
774
803
  else:
775
804
  out_args = combine_2d(filelist, output=args.output, method=args.method, trim=args.trim,
776
805
  scale=args.scale, extended=args.extended, dispaxis=args.axis)
@@ -796,6 +825,28 @@ def main(inspect=False):
796
825
  gui.show()
797
826
  app.exit(app.exec_())
798
827
 
828
+ elif task == 'view':
829
+ from PyQt5 import QtWidgets
830
+ from pynot.viewer.viewer import MainWindow
831
+
832
+ app = QtWidgets.QApplication(sys.argv)
833
+ screenSize = app.primaryScreen().size()
834
+ ratio = 0.85
835
+ if args.list:
836
+ with open(args.list) as l:
837
+ input_files = [line.strip() for line in l.readlines()]
838
+ else:
839
+ input_files = args.files
840
+ main = MainWindow(input_files,
841
+ assn_table=args.table,
842
+ container_mode=args.container,
843
+ width=ratio*screenSize.width(),
844
+ height=ratio*screenSize.height(),
845
+ redshift_table=args.z,
846
+ z_col=args.zcol, name_col=args.ncol, cls_col=args.tcol,
847
+ )
848
+ main.show()
849
+ app.exit(app.exec_())
799
850
 
800
851
  # -- Imaging tasks:
801
852
  elif task == 'phot':
@@ -193,13 +193,13 @@ def run_pipeline(options_fname, object_id=None, verbose=True, interactive=False,
193
193
  identify_all = options['identify']['all']
194
194
  local_pixtables = glob.glob(os.path.join(output_base, "arcs", "pixtab_*.dat"))
195
195
  for fname in local_pixtables:
196
- pixtab_id = os.path.splitext(fname)[0].split('_')[1]
196
+ pixtab_id = os.path.splitext(os.path.basename(fname))[0].split('_')[1]
197
197
  status[pixtab_id] = fname
198
198
  cached_pixtables = glob.glob(os.path.join(calib_dir, "*_pixeltable.dat"))
199
199
  for fname in cached_pixtables:
200
200
  # TODO: Potentially use `basename(fname)` to split
201
201
  # This avoids bugs if the filename contains more than one `_`
202
- pixtab_id = fname.split('_')[0]
202
+ pixtab_id = os.path.basename(fname).split('_')[0]
203
203
  status[pixtab_id] = fname
204
204
 
205
205
 
@@ -5,7 +5,7 @@ from functools import reduce
5
5
  import warnings
6
6
 
7
7
  from pynot.functions import get_version_number
8
- from pynot.fitsio import load_fits_spectrum, save_fitstable_spectrum, save_fits_spectrum
8
+ from pynot.fitsio import load_fits_spectrum, save_fitstable_spectrum, save_fits_spectrum, error_HDU_names
9
9
  from pynot.txtio import load_ascii_spectrum
10
10
 
11
11
 
@@ -55,18 +55,26 @@ def combine_2d(files, output=None, method='mean', scale=False, extended=False, d
55
55
  hdr = fits.getheader(fname)
56
56
  data2D = fits.getdata(fname)
57
57
 
58
- try:
59
- err2D = fits.getdata(fname, 'ERR')
60
- err2D[err2D <= 0.] = np.nanmedian(err2D)*100
61
- msg.append(" - Loaded error image")
62
- except KeyError:
58
+ for err_name in error_HDU_names:
59
+ try:
60
+ err2D = fits.getdata(fname, err_name)
61
+ err2D[err2D <= 0.] = np.nanmedian(err2D)*100
62
+ msg.append(f" - Loaded error image extension: {err_name}")
63
+ break
64
+ except KeyError:
65
+ pass
66
+ else:
63
67
  msg.append("[WARNING] - No ERR extension could be found in the FITS file!")
64
68
  err2D = np.ones_like(data2D)
65
69
 
66
- try:
67
- mask2D = fits.getdata(fname, 'MASK')
68
- msg.append(" - Loaded mask image")
69
- except KeyError:
70
+ for mask_name in ['MASK', 'QUAL']:
71
+ try:
72
+ mask2D = fits.getdata(fname, mask_name)
73
+ msg.append(f" - Loaded mask image extension: {mask_name}")
74
+ break
75
+ except KeyError:
76
+ pass
77
+ else:
70
78
  msg.append("[WARNING] - No MASK extension could be found in the FITS file!")
71
79
  mask2D = np.zeros_like(data2D)
72
80
 
@@ -345,7 +353,7 @@ def combine_2d(files, output=None, method='mean', scale=False, extended=False, d
345
353
 
346
354
 
347
355
 
348
- def combine_1d(files, output=None, method='mean', scale=False, table_output=True):
356
+ def combine_1d(files, output=None, method='mean', scale=False, table_output=True, imin=0, imax=None):
349
357
  """Combine a list of 1d-spectra using either median or mean combination.
350
358
  For median combination, only the overlapping parts of the spectra will be
351
359
  combined. The mean combination uses a weighted average over the entire
@@ -368,6 +376,12 @@ def combine_1d(files, output=None, method='mean', scale=False, table_output=True
368
376
 
369
377
  table_output : bool [default=True]
370
378
  Use FITS table for the output format? Otherwise use a MultiExtension Fits File
379
+
380
+ imin : int
381
+ Apply slicing to each spectrum from imin to imax.
382
+
383
+ imax : int | None
384
+ Apply slicing to each spectrum from imin to imax.
371
385
  """
372
386
 
373
387
  wl_all = list()
@@ -377,6 +391,7 @@ def combine_1d(files, output=None, method='mean', scale=False, table_output=True
377
391
  size_all = list()
378
392
  scales = list()
379
393
 
394
+ cut = slice(imin, imax)
380
395
  msg = list()
381
396
  for fname in files:
382
397
  if fname.endswith('.fits') or fname.endswith('.fit'):
@@ -393,11 +408,11 @@ def combine_1d(files, output=None, method='mean', scale=False, table_output=True
393
408
  msg.append(load_msg)
394
409
  msg.append(" - Loaded ASCII spectrum: %s" % fname)
395
410
 
396
- wl_all.append(wl)
397
- flux_all.append(flux)
398
- err_all.append(err)
399
- mask_all.append(mask)
400
- size_all.append(len(wl))
411
+ wl_all.append(wl[cut])
412
+ flux_all.append(flux[cut])
413
+ err_all.append(err[cut])
414
+ mask_all.append(mask[cut])
415
+ size_all.append(len(wl[cut]))
401
416
  if scale:
402
417
  nonzero = flux.nonzero()[0]
403
418
  idx_0 = min(nonzero) + len(nonzero)/2
@@ -30,6 +30,7 @@ def fit_background_row(x, row, mask=None, order_bg=3, med_kernel=15, kappa=5):
30
30
  mask = np.ones(len(row), dtype=bool)
31
31
 
32
32
  # Median filter the data to remove outliers:
33
+ row = row.astype(np.float64)
33
34
  med_row = median_filter(row, med_kernel)
34
35
  noise = mad(row) * 1.4826
35
36
  this_mask = mask * (np.abs(row - med_row) < kappa*noise)
File without changes