teareduce 0.4.7__py3-none-any.whl → 0.4.9__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.
teareduce/__init__.py CHANGED
@@ -8,6 +8,7 @@
8
8
  #
9
9
 
10
10
  from .avoid_astropy_warnings import avoid_astropy_warnings
11
+ from .cookbook.get_cookbook_file import get_cookbook_file
11
12
  from .correct_pincushion_distortion import correct_pincushion_distortion
12
13
  from .cosmicrays import cr2images, apply_cr2images_ccddata, crmedian
13
14
  from .ctext import ctext
File without changes
@@ -0,0 +1,49 @@
1
+ #
2
+ # Copyright 2025 Universidad Complutense de Madrid
3
+ #
4
+ # This file is part of teareduce
5
+ #
6
+ # SPDX-License-Identifier: GPL-3.0+
7
+ # License-Filename: LICENSE.txt
8
+ #
9
+
10
+ """Interactive Cosmic Ray cleaning tool."""
11
+
12
+ import argparse
13
+ import tkinter as tk
14
+ import os
15
+
16
+ from .cosmicraycleanerapp import CosmicRayCleanerApp
17
+
18
+ import matplotlib
19
+ matplotlib.use("TkAgg")
20
+
21
+
22
+ def main():
23
+ parser = argparse.ArgumentParser(description="Interactive cosmic ray cleaner for FITS images.")
24
+ parser.add_argument("input_fits", help="Path to the FITS file to be cleaned.")
25
+ parser.add_argument("--extension", type=int, default=0,
26
+ help="FITS extension to use (default: 0).")
27
+ parser.add_argument("--output_fits", type=str, default=None,
28
+ help="Path to save the cleaned FITS file")
29
+ args = parser.parse_args()
30
+
31
+ if not os.path.isfile(args.input_fits):
32
+ print(f"Error: File '{args.input_fits}' does not exist.")
33
+ return
34
+ if args.output_fits is not None and os.path.isfile(args.output_fits):
35
+ print(f"Error: Output file '{args.output_fits}' already exists.")
36
+ return
37
+
38
+ # Initialize Tkinter root
39
+ root = tk.Tk()
40
+
41
+ # Create and run the application
42
+ CosmicRayCleanerApp(root, args.input_fits, args.extension, args.output_fits)
43
+
44
+ # Execute
45
+ root.mainloop()
46
+
47
+
48
+ if __name__ == "__main__":
49
+ main()
@@ -0,0 +1,203 @@
1
+ #
2
+ # Copyright 2025 Universidad Complutense de Madrid
3
+ #
4
+ # This file is part of teareduce
5
+ #
6
+ # SPDX-License-Identifier: GPL-3.0+
7
+ # License-Filename: LICENSE.txt
8
+ #
9
+
10
+ """Define the CosmicRayCleanerApp class."""
11
+
12
+ import tkinter as tk
13
+ from tkinter import filedialog
14
+ from tkinter import simpledialog
15
+
16
+ from astropy.io import fits
17
+ from ccdproc import cosmicray_lacosmic
18
+ import matplotlib.pyplot as plt
19
+ from matplotlib.backend_bases import key_press_handler
20
+ from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
21
+ import numpy as np
22
+ import os
23
+
24
+ from .reviewcosmicray import ReviewCosmicRay
25
+
26
+ from ..imshow import imshow
27
+ from ..zscale import zscale
28
+
29
+ import matplotlib
30
+ matplotlib.use("TkAgg")
31
+
32
+
33
+ class CosmicRayCleanerApp():
34
+ """Main application class for cosmic ray cleaning."""
35
+
36
+ def __init__(self, root, input_fits, extension=0, output_fits=None):
37
+ """
38
+ Initialize the application.
39
+
40
+ Parameters
41
+ ----------
42
+ root : tk.Tk
43
+ The main Tkinter window.
44
+ input_fits : str
45
+ Path to the FITS file to be cleaned.
46
+ extension : int, optional
47
+ FITS extension to use (default is 0).
48
+ output_fits : str, optional
49
+ Path to save the cleaned FITS file (default is None, which prompts
50
+ for a save location).
51
+ """
52
+ self.root = root
53
+ self.root.title("Cosmic Ray Cleaner")
54
+ self.root.geometry("800x700+50+0")
55
+ self.input_fits = input_fits
56
+ self.extension = extension
57
+ self.output_fits = output_fits
58
+ self.load_fits_file()
59
+ self.create_widgets()
60
+
61
+ def load_fits_file(self):
62
+ try:
63
+ with fits.open(self.input_fits, mode='readonly') as hdul:
64
+ self.data = hdul[self.extension].data
65
+ if 'CRMASK' in hdul:
66
+ self.mask_fixed = hdul['CRMASK'].data.astype(bool)
67
+ else:
68
+ self.mask_fixed = np.zeros(self.data.shape, dtype=bool)
69
+ except Exception as e:
70
+ print(f"Error loading FITS file: {e}")
71
+
72
+ def save_fits_file(self):
73
+ if self.output_fits is None:
74
+ base, ext = os.path.splitext(self.input_fits)
75
+ suggested_name = f"{base}_cleaned"
76
+ else:
77
+ suggested_name, _ = os.path.splitext(self.output_fits)
78
+ self.output_fits = filedialog.asksaveasfilename(
79
+ initialdir=os.getcwd(),
80
+ title="Save cleaned FITS file",
81
+ defaultextension=".fits",
82
+ filetypes=[("FITS files", "*.fits"), ("All files", "*.*")],
83
+ initialfile=suggested_name
84
+ )
85
+ try:
86
+ with fits.open(self.input_fits, mode='readonly') as hdul:
87
+ hdul[self.extension].data = self.data
88
+ if 'CRMASK' in hdul:
89
+ hdul['CRMASK'].data = self.mask_fixed.astype(np.uint8)
90
+ else:
91
+ crmask_hdu = fits.ImageHDU(self.mask_fixed.astype(np.uint8), name='CRMASK')
92
+ hdul.append(crmask_hdu)
93
+ hdul.writeto(self.output_fits, overwrite=True)
94
+ print(f"Cleaned data saved to {self.output_fits}")
95
+ except Exception as e:
96
+ print(f"Error saving FITS file: {e}")
97
+
98
+ def create_widgets(self):
99
+ # Row 1
100
+ self.button_frame1 = tk.Frame(self.root)
101
+ self.button_frame1.grid(row=0, column=0, pady=5)
102
+ self.run_lacosmic_button = tk.Button(self.button_frame1, text="Run L.A.Cosmic", command=self.run_lacosmic)
103
+ self.run_lacosmic_button.pack(side=tk.LEFT, padx=5)
104
+ self.save_button = tk.Button(self.button_frame1, text="Save cleaned FITS", command=self.save_fits_file)
105
+ self.save_button.pack(side=tk.LEFT, padx=5)
106
+
107
+ # Row 2
108
+ self.button_frame2 = tk.Frame(self.root)
109
+ self.button_frame2.grid(row=1, column=0, pady=5)
110
+ vmin, vmax = zscale(self.data)
111
+ self.vmin_button = tk.Button(self.button_frame2, text=f"vmin: {vmin:.2f}", command=self.set_vmin)
112
+ self.vmin_button.pack(side=tk.LEFT, padx=5)
113
+ self.vmax_button = tk.Button(self.button_frame2, text=f"vmax: {vmax:.2f}", command=self.set_vmax)
114
+ self.vmax_button.pack(side=tk.LEFT, padx=5)
115
+ self.stop_button = tk.Button(self.button_frame2, text="Stop program", command=self.stop_app)
116
+ self.stop_button.pack(side=tk.LEFT, padx=5)
117
+
118
+ # Main frame for figure and toolbar
119
+ self.main_frame = tk.Frame(self.root)
120
+ self.main_frame.grid(row=2, column=0, sticky="nsew")
121
+ self.root.grid_rowconfigure(2, weight=1)
122
+ self.root.grid_columnconfigure(0, weight=1)
123
+ self.main_frame.grid_rowconfigure(0, weight=1)
124
+ self.main_frame.grid_columnconfigure(0, weight=1)
125
+
126
+ # Create figure and axis
127
+ self.fig, self.ax = plt.subplots(figsize=(8, 6))
128
+ xlabel = 'X pixel (from 1 to NAXIS1)'
129
+ ylabel = 'Y pixel (from 1 to NAXIS2)'
130
+ extent = [0.5, self.data.shape[1] + 0.5, 0.5, self.data.shape[0] + 0.5]
131
+ self.image, _, _ = imshow(self.fig, self.ax, self.data, vmin=vmin, vmax=vmax,
132
+ xlabel=xlabel, ylabel=ylabel, extent=extent)
133
+ # Note: tight_layout should be called before defining the canvas
134
+ self.fig.tight_layout()
135
+
136
+ # Create canvas and toolbar
137
+ self.canvas = FigureCanvasTkAgg(self.fig, master=self.main_frame)
138
+ # The next two instructions prevent a segmentation fault when pressing "q"
139
+ self.canvas.mpl_disconnect(self.canvas.mpl_connect("key_press_event", key_press_handler))
140
+ self.canvas.mpl_connect("key_press_event", self.on_key)
141
+ self.canvas.mpl_connect("button_press_event", self.on_click)
142
+ canvas_widget = self.canvas.get_tk_widget()
143
+ canvas_widget.grid(row=0, column=0, sticky="nsew")
144
+
145
+ # Matplotlib toolbar
146
+ self.toolbar_frame = tk.Frame(self.main_frame)
147
+ self.toolbar_frame.grid(row=1, column=0, sticky="ew")
148
+ self.toolbar = NavigationToolbar2Tk(self.canvas, self.toolbar_frame)
149
+ self.toolbar.update()
150
+
151
+ def set_vmin(self):
152
+ old_vmin = self.get_vmin()
153
+ new_vmin = simpledialog.askfloat("Set vmin", "Enter new vmin:", initialvalue=old_vmin)
154
+ if new_vmin is None:
155
+ return
156
+ self.vmin_button.config(text=f"vmin: {new_vmin:.2f}")
157
+ self.image.set_clim(vmin=new_vmin)
158
+ self.canvas.draw()
159
+
160
+ def set_vmax(self):
161
+ old_vmax = self.get_vmax()
162
+ new_vmax = simpledialog.askfloat("Set vmax", "Enter new vmax:", initialvalue=old_vmax)
163
+ if new_vmax is None:
164
+ return
165
+ self.vmax_button.config(text=f"vmax: {new_vmax:.2f}")
166
+ self.image.set_clim(vmax=new_vmax)
167
+ self.canvas.draw()
168
+
169
+ def get_vmin(self):
170
+ return float(self.vmin_button.cget("text").split(":")[1])
171
+
172
+ def get_vmax(self):
173
+ return float(self.vmax_button.cget("text").split(":")[1])
174
+
175
+ def run_lacosmic(self):
176
+ self.run_lacosmic_button.config(state=tk.DISABLED)
177
+ self.stop_button.config(state=tk.DISABLED)
178
+ # Parameters for L.A.Cosmic can be adjusted as needed
179
+ _, mask_crfound = cosmicray_lacosmic(self.data, sigclip=4.5, sigfrac=0.3, objlim=5.0, verbose=True)
180
+ ReviewCosmicRay(
181
+ root=self.root,
182
+ data=self.data,
183
+ mask_fixed=self.mask_fixed,
184
+ mask_crfound=mask_crfound
185
+ )
186
+ print("L.A.Cosmic cleaning applied.")
187
+ self.run_lacosmic_button.config(state=tk.NORMAL)
188
+ self.stop_button.config(state=tk.NORMAL)
189
+
190
+ def stop_app(self):
191
+ self.root.quit()
192
+ self.root.destroy()
193
+
194
+ def on_key(self, event):
195
+ if event.key == 'q':
196
+ pass # Ignore the "q" key to prevent closing the window
197
+ else:
198
+ print(f"Key pressed: {event.key}")
199
+
200
+ def on_click(self, event):
201
+ if event.inaxes:
202
+ x, y = event.xdata, event.ydata
203
+ print(f"Clicked at image coordinates: ({x:.2f}, {y:.2f})")
@@ -7,25 +7,20 @@
7
7
  # License-Filename: LICENSE.txt
8
8
  #
9
9
 
10
- """Interactive Cosmic Ray cleaning tool."""
10
+ """Define the ReviewCosmicRay class."""
11
11
 
12
- import argparse
13
12
  import tkinter as tk
14
- from tkinter import filedialog
15
13
  from tkinter import simpledialog
16
14
 
17
- from astropy.io import fits
18
- from ccdproc import cosmicray_lacosmic
19
15
  import matplotlib.pyplot as plt
20
16
  from matplotlib.backend_bases import key_press_handler
21
17
  from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
22
18
  import numpy as np
23
- import os
24
19
  from scipy import ndimage
25
20
 
26
- from .imshow import imshow
27
- from .sliceregion import SliceRegion2D
28
- from .zscale import zscale
21
+ from ..imshow import imshow
22
+ from ..sliceregion import SliceRegion2D
23
+ from ..zscale import zscale
29
24
 
30
25
  import matplotlib
31
26
  matplotlib.use("TkAgg")
@@ -489,206 +484,3 @@ class ReviewCosmicRay():
489
484
  self.remove_crosses_button.config(state=tk.NORMAL)
490
485
  # Update the display to reflect the change
491
486
  self.update_display()
492
-
493
-
494
- class CosmicRayCleanerApp():
495
- """Main application class for cosmic ray cleaning."""
496
-
497
- def __init__(self, root, input_fits, extension=0, output_fits=None):
498
- """
499
- Initialize the application.
500
-
501
- Parameters
502
- ----------
503
- root : tk.Tk
504
- The main Tkinter window.
505
- input_fits : str
506
- Path to the FITS file to be cleaned.
507
- extension : int, optional
508
- FITS extension to use (default is 0).
509
- output_fits : str, optional
510
- Path to save the cleaned FITS file (default is None, which prompts
511
- for a save location).
512
- """
513
- self.root = root
514
- self.root.title("Cosmic Ray Cleaner")
515
- self.root.geometry("800x700+50+0")
516
- self.input_fits = input_fits
517
- self.extension = extension
518
- self.output_fits = output_fits
519
- self.load_fits_file()
520
- self.create_widgets()
521
-
522
- def load_fits_file(self):
523
- try:
524
- with fits.open(self.input_fits, mode='readonly') as hdul:
525
- self.data = hdul[self.extension].data
526
- if 'CRMASK' in hdul:
527
- self.mask_fixed = hdul['CRMASK'].data.astype(bool)
528
- else:
529
- self.mask_fixed = np.zeros(self.data.shape, dtype=bool)
530
- except Exception as e:
531
- print(f"Error loading FITS file: {e}")
532
-
533
- def save_fits_file(self):
534
- if self.output_fits is None:
535
- base, ext = os.path.splitext(self.input_fits)
536
- suggested_name = f"{base}_cleaned"
537
- else:
538
- suggested_name, _ = os.path.splitext(self.output_fits)
539
- self.output_fits = filedialog.asksaveasfilename(
540
- initialdir=os.getcwd(),
541
- title="Save cleaned FITS file",
542
- defaultextension=".fits",
543
- filetypes=[("FITS files", "*.fits"), ("All files", "*.*")],
544
- initialfile=suggested_name
545
- )
546
- try:
547
- with fits.open(self.input_fits, mode='readonly') as hdul:
548
- hdul[self.extension].data = self.data
549
- if 'CRMASK' in hdul:
550
- hdul['CRMASK'].data = self.mask_fixed.astype(np.uint8)
551
- else:
552
- crmask_hdu = fits.ImageHDU(self.mask_fixed.astype(np.uint8), name='CRMASK')
553
- hdul.append(crmask_hdu)
554
- hdul.writeto(self.output_fits, overwrite=True)
555
- print(f"Cleaned data saved to {self.output_fits}")
556
- except Exception as e:
557
- print(f"Error saving FITS file: {e}")
558
-
559
- def create_widgets(self):
560
- # Row 1
561
- self.button_frame1 = tk.Frame(self.root)
562
- self.button_frame1.grid(row=0, column=0, pady=5)
563
- self.run_lacosmic_button = tk.Button(self.button_frame1, text="Run L.A.Cosmic", command=self.run_lacosmic)
564
- self.run_lacosmic_button.pack(side=tk.LEFT, padx=5)
565
- self.save_button = tk.Button(self.button_frame1, text="Save cleaned FITS", command=self.save_fits_file)
566
- self.save_button.pack(side=tk.LEFT, padx=5)
567
-
568
- # Row 2
569
- self.button_frame2 = tk.Frame(self.root)
570
- self.button_frame2.grid(row=1, column=0, pady=5)
571
- vmin, vmax = zscale(self.data)
572
- self.vmin_button = tk.Button(self.button_frame2, text=f"vmin: {vmin:.2f}", command=self.set_vmin)
573
- self.vmin_button.pack(side=tk.LEFT, padx=5)
574
- self.vmax_button = tk.Button(self.button_frame2, text=f"vmax: {vmax:.2f}", command=self.set_vmax)
575
- self.vmax_button.pack(side=tk.LEFT, padx=5)
576
- self.stop_button = tk.Button(self.button_frame2, text="Stop program", command=self.stop_app)
577
- self.stop_button.pack(side=tk.LEFT, padx=5)
578
-
579
- # Main frame for figure and toolbar
580
- self.main_frame = tk.Frame(self.root)
581
- self.main_frame.grid(row=2, column=0, sticky="nsew")
582
- self.root.grid_rowconfigure(2, weight=1)
583
- self.root.grid_columnconfigure(0, weight=1)
584
- self.main_frame.grid_rowconfigure(0, weight=1)
585
- self.main_frame.grid_columnconfigure(0, weight=1)
586
-
587
- # Create figure and axis
588
- self.fig, self.ax = plt.subplots(figsize=(8, 6))
589
- xlabel = 'X pixel (from 1 to NAXIS1)'
590
- ylabel = 'Y pixel (from 1 to NAXIS2)'
591
- extent = [0.5, self.data.shape[1] + 0.5, 0.5, self.data.shape[0] + 0.5]
592
- self.image, _, _ = imshow(self.fig, self.ax, self.data, vmin=vmin, vmax=vmax,
593
- xlabel=xlabel, ylabel=ylabel, extent=extent)
594
- # Note: tight_layout should be called before defining the canvas
595
- self.fig.tight_layout()
596
-
597
- # Create canvas and toolbar
598
- self.canvas = FigureCanvasTkAgg(self.fig, master=self.main_frame)
599
- # The next two instructions prevent a segmentation fault when pressing "q"
600
- self.canvas.mpl_disconnect(self.canvas.mpl_connect("key_press_event", key_press_handler))
601
- self.canvas.mpl_connect("key_press_event", self.on_key)
602
- self.canvas.mpl_connect("button_press_event", self.on_click)
603
- canvas_widget = self.canvas.get_tk_widget()
604
- canvas_widget.grid(row=0, column=0, sticky="nsew")
605
-
606
- # Matplotlib toolbar
607
- self.toolbar_frame = tk.Frame(self.main_frame)
608
- self.toolbar_frame.grid(row=1, column=0, sticky="ew")
609
- self.toolbar = NavigationToolbar2Tk(self.canvas, self.toolbar_frame)
610
- self.toolbar.update()
611
-
612
- def set_vmin(self):
613
- old_vmin = self.get_vmin()
614
- new_vmin = simpledialog.askfloat("Set vmin", "Enter new vmin:", initialvalue=old_vmin)
615
- if new_vmin is None:
616
- return
617
- self.vmin_button.config(text=f"vmin: {new_vmin:.2f}")
618
- self.image.set_clim(vmin=new_vmin)
619
- self.canvas.draw()
620
-
621
- def set_vmax(self):
622
- old_vmax = self.get_vmax()
623
- new_vmax = simpledialog.askfloat("Set vmax", "Enter new vmax:", initialvalue=old_vmax)
624
- if new_vmax is None:
625
- return
626
- self.vmax_button.config(text=f"vmax: {new_vmax:.2f}")
627
- self.image.set_clim(vmax=new_vmax)
628
- self.canvas.draw()
629
-
630
- def get_vmin(self):
631
- return float(self.vmin_button.cget("text").split(":")[1])
632
-
633
- def get_vmax(self):
634
- return float(self.vmax_button.cget("text").split(":")[1])
635
-
636
- def run_lacosmic(self):
637
- self.run_lacosmic_button.config(state=tk.DISABLED)
638
- self.stop_button.config(state=tk.DISABLED)
639
- # Parameters for L.A.Cosmic can be adjusted as needed
640
- _, mask_crfound = cosmicray_lacosmic(self.data, sigclip=4.5, sigfrac=0.3, objlim=5.0, verbose=True)
641
- ReviewCosmicRay(
642
- root=self.root,
643
- data=self.data,
644
- mask_fixed=self.mask_fixed,
645
- mask_crfound=mask_crfound
646
- )
647
- print("L.A.Cosmic cleaning applied.")
648
- self.run_lacosmic_button.config(state=tk.NORMAL)
649
- self.stop_button.config(state=tk.NORMAL)
650
-
651
- def stop_app(self):
652
- self.root.quit()
653
- self.root.destroy()
654
-
655
- def on_key(self, event):
656
- if event.key == 'q':
657
- pass # Ignore the "q" key to prevent closing the window
658
- else:
659
- print(f"Key pressed: {event.key}")
660
-
661
- def on_click(self, event):
662
- if event.inaxes:
663
- x, y = event.xdata, event.ydata
664
- print(f"Clicked at image coordinates: ({x:.2f}, {y:.2f})")
665
-
666
-
667
- def main():
668
- parser = argparse.ArgumentParser(description="Interactive cosmic ray cleaner for FITS images.")
669
- parser.add_argument("input_fits", help="Path to the FITS file to be cleaned.")
670
- parser.add_argument("--extension", type=int, default=0,
671
- help="FITS extension to use (default: 0).")
672
- parser.add_argument("--output_fits", type=str, default=None,
673
- help="Path to save the cleaned FITS file")
674
- args = parser.parse_args()
675
-
676
- if not os.path.isfile(args.input_fits):
677
- print(f"Error: File '{args.input_fits}' does not exist.")
678
- return
679
- if args.output_fits is not None and os.path.isfile(args.output_fits):
680
- print(f"Error: Output file '{args.output_fits}' already exists.")
681
- return
682
-
683
- # Initialize Tkinter root
684
- root = tk.Tk()
685
-
686
- # Create and run the application
687
- CosmicRayCleanerApp(root, args.input_fits, args.extension, args.output_fits)
688
-
689
- # Execute
690
- root.mainloop()
691
-
692
-
693
- if __name__ == "__main__":
694
- main()
File without changes
@@ -0,0 +1,34 @@
1
+ #
2
+ # Copyright 2025 Universidad Complutense de Madrid
3
+ #
4
+ # This file is part of teareduce
5
+ #
6
+ # SPDX-License-Identifier: GPL-3.0+
7
+ # License-Filename: LICENSE.txt
8
+
9
+ import os
10
+ import requests
11
+
12
+
13
+ def get_cookbook_file(file_path, verbose=True):
14
+ """
15
+ Retrieve the contents of an auxiliary file.
16
+
17
+ The file is downloaded from the teareduce-cookbook GitHub repository
18
+
19
+ Parameters
20
+ ----------
21
+ file_path : str
22
+ The path to the auxiliary file.
23
+ verbose : bool, optional
24
+ If True, print a message when the file is downloaded.
25
+ Default is True.
26
+ """
27
+
28
+ url = f"https://raw.githubusercontent.com/nicocardiel/teareduce-cookbook/main/{file_path}"
29
+ response = requests.get(url)
30
+ local_filename = os.path.basename(file_path)
31
+ with open(local_filename, "wb") as f:
32
+ f.write(response.content)
33
+ if verbose:
34
+ print(f"File {local_filename} downloaded!")
teareduce/imshow.py CHANGED
@@ -14,13 +14,18 @@ from matplotlib.axes import Axes
14
14
  from mpl_toolkits.axes_grid1 import make_axes_locatable
15
15
 
16
16
 
17
- def imshowme(data, **kwargs):
17
+ def imshowme(data, return_objs=False, **kwargs):
18
18
  """Simple execution of teareduce.imshow.
19
19
 
20
20
  Parameters
21
21
  ----------
22
22
  data : numpy.ndarray
23
23
  2D array to be displayed
24
+ return_objs : bool
25
+ If True, return the Figure, Axes, AxesImage,
26
+ color bar Axes and Colorbar instances.
27
+ **kwargs : dict
28
+ Additional parameters passed to imshow().
24
29
 
25
30
  Returns
26
31
  -------
@@ -37,14 +42,16 @@ def imshowme(data, **kwargs):
37
42
  Instance of Colorbar, or None if colorbar is False.
38
43
  """
39
44
  fig, ax = plt.subplots()
40
- img, cax, cbar = imshow(fig=fig, ax=ax, data=data, **kwargs)
41
- return fig, ax, img, cax, cbar
45
+ img, cax, cbar = imshow(fig=fig, ax=ax, data=data, return_objs=True, **kwargs)
46
+ if return_objs:
47
+ return fig, ax, img, cax, cbar
42
48
 
43
49
 
44
50
  def imshow(fig=None, ax=None, data=None, ds9mode=False,
45
51
  crpix1=1, crval1=None, cdelt1=None, cunit1=None, cunitx=Unit('Angstrom'),
46
52
  xlabel=None, ylabel=None, title=None,
47
53
  colorbar=True, cblabel='Number of counts',
54
+ return_objs=True,
48
55
  **kwargs):
49
56
  """Call imshow() with color bar and default labels.
50
57
 
@@ -89,6 +96,11 @@ def imshow(fig=None, ax=None, data=None, ds9mode=False,
89
96
  If True, display color bar.
90
97
  cblabel : str
91
98
  Color bar label.
99
+ return_objs : bool
100
+ If True, return AxesImage, color bar Axes and Colorbar
101
+ instances.
102
+ **kwargs : dict
103
+ Additional parameters passed to imshow().
92
104
 
93
105
  Return
94
106
  ------
@@ -188,4 +200,5 @@ def imshow(fig=None, ax=None, data=None, ds9mode=False,
188
200
  cax = None
189
201
  cbar = None
190
202
 
191
- return img, cax, cbar
203
+ if return_objs:
204
+ return img, cax, cbar
teareduce/sliceregion.py CHANGED
@@ -85,12 +85,12 @@ class SliceRegion1D:
85
85
 
86
86
  if self.mode == 'fits':
87
87
  if region.stop < region.start:
88
- raise ValueError(f'Invalid {region!r}')
88
+ raise ValueError(f'Invalid {region!r}: stop must be >= start')
89
89
  self.fits = region
90
90
  self.python = slice(region.start-1, region.stop)
91
91
  elif self.mode == 'python':
92
92
  if region.stop <= region.start:
93
- raise ValueError(f'Invalid {region!r}')
93
+ raise ValueError(f'Invalid {region!r}: stop must be > start')
94
94
  self.fits = slice(region.start+1, region.stop)
95
95
  self.python = region
96
96
  else:
@@ -227,16 +227,16 @@ class SliceRegion2D:
227
227
 
228
228
  if self.mode == 'fits':
229
229
  if s1.stop < s1.start:
230
- raise ValueError(f'Invalid {s1!r}')
230
+ raise ValueError(f'Invalid {s1!r}: stop must be >= start')
231
231
  if s2.stop < s2.start:
232
- raise ValueError(f'Invalid {s2!r}')
232
+ raise ValueError(f'Invalid {s2!r}: stop must be >= start')
233
233
  self.fits = region
234
234
  self.python = slice(s2.start-1, s2.stop), slice(s1.start-1, s1.stop)
235
235
  elif self.mode == 'python':
236
236
  if s1.stop <= s1.start:
237
- raise ValueError(f'Invalid {s1!r}')
237
+ raise ValueError(f'Invalid {s1!r}: stop must be > start')
238
238
  if s2.stop <= s2.start:
239
- raise ValueError(f'Invalid {s2!r}')
239
+ raise ValueError(f'Invalid {s2!r}: stop must be > start')
240
240
  self.fits = slice(s2.start+1, s2.stop), slice(s1.start+1, s1.stop)
241
241
  self.python = region
242
242
  else:
@@ -387,20 +387,20 @@ class SliceRegion3D:
387
387
 
388
388
  if self.mode == 'fits':
389
389
  if s1.stop < s1.start:
390
- raise ValueError(f'Invalid {s1!r}')
390
+ raise ValueError(f'Invalid {s1!r}: stop must be >= start')
391
391
  if s2.stop < s2.start:
392
- raise ValueError(f'Invalid {s2!r}')
392
+ raise ValueError(f'Invalid {s2!r}: stop must be >= start')
393
393
  if s3.stop < s3.start:
394
- raise ValueError(f'Invalid {s3!r}')
394
+ raise ValueError(f'Invalid {s3!r}: stop must be >= start')
395
395
  self.fits = region
396
396
  self.python = slice(s3.start-1, s3.stop), slice(s2.start-1, s2.stop), slice(s1.start-1, s1.stop)
397
397
  elif self.mode == 'python':
398
398
  if s1.stop <= s1.start:
399
- raise ValueError(f'Invalid {s1!r}')
399
+ raise ValueError(f'Invalid {s1!r}: stop must be > start')
400
400
  if s2.stop <= s2.start:
401
- raise ValueError(f'Invalid {s2!r}')
401
+ raise ValueError(f'Invalid {s2!r}: stop must be > start')
402
402
  if s3.stop <= s3.start:
403
- raise ValueError(f'Invalid {s3!r}')
403
+ raise ValueError(f'Invalid {s3!r}: stop must be > start')
404
404
  self.fits = slice(s3.start+1, s3.stop), slice(s2.start+1, s2.stop), slice(s1.start+1, s1.stop)
405
405
  self.python = region
406
406
  else:
teareduce/version.py CHANGED
@@ -9,7 +9,7 @@
9
9
  #
10
10
  """Module to define the version of the teareduce package."""
11
11
 
12
- VERSION = '0.4.7'
12
+ VERSION = '0.4.9'
13
13
 
14
14
 
15
15
  def main():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: teareduce
3
- Version: 0.4.7
3
+ Version: 0.4.9
4
4
  Summary: Utilities for astronomical data reduction
5
5
  Author-email: Nicolás Cardiel <cardiel@ucm.es>
6
6
  License: GPL-3.0-or-later
@@ -1,31 +1,36 @@
1
- teareduce/__init__.py,sha256=u-6eZ9oz2reoSWxm1kf-9uj2VB4yERhh7fuE5gd4-RA,1304
1
+ teareduce/__init__.py,sha256=TkPRzPxHYcnfZtBX2hkA8bP5V7rjUlP7iGx-0L4vOmg,1362
2
2
  teareduce/avoid_astropy_warnings.py,sha256=2YgQ47pxsKYWDxUtzyEOfh3Is3aAHHmjJkuOa1JCDN4,648
3
- teareduce/cleanest.py,sha256=F5FXrTsMSb0EG7Om6izrhyNn_d0JKKSXphoftwd-_1k,30865
4
3
  teareduce/correct_pincushion_distortion.py,sha256=Xpt03jtmJMyqik4ta95zMRE3Z6dVfzzHI2z5IDbtnMk,1685
5
4
  teareduce/cosmicrays.py,sha256=gLHgq9LdfNHmZ5n_FYxvxcGI2245TXGV-rFHSIogxPE,26877
6
5
  teareduce/ctext.py,sha256=cCVVsFRQ73V0JwDCytd73U3HWRjfv5xkt3ca5-YWuwo,2081
7
6
  teareduce/draw_rectangle.py,sha256=mKZ0j8ZRtbxkZH9pQjja_xgP-LSpfe9c7YTgHJX_nSc,1911
8
7
  teareduce/elapsed_time.py,sha256=QWakPeiOUA__WpjjFREnodKqW1FZzVGWstab0N3Ro6k,1418
9
8
  teareduce/histogram1d.py,sha256=3hlvcI8XPRmZ27H_sFmyL26gIcbJozk07ELBKWtngQk,2835
10
- teareduce/imshow.py,sha256=d5rb5bwCWvt139X0nOW0xtCozSwkHf1p-lndPifuA8E,6600
9
+ teareduce/imshow.py,sha256=qnic8X0GTjHW5yIITEZcqZmefzDpQlGJH7v2bPHI6t8,7082
11
10
  teareduce/numsplines.py,sha256=1PpG-frdc9Qz3VRbC7XyZFWKmhus05ID4REtFnWDmUo,8049
12
11
  teareduce/peaks_spectrum.py,sha256=XLMbJZnyYDVSaa5nCMXk9kSn0fyG_lhVmXWHzTwehiA,9878
13
12
  teareduce/polfit.py,sha256=CGsrRsz_Du2aKxOcgXi36lpAZO04JyqCCUaxhC0C-Mk,14281
14
13
  teareduce/robust_std.py,sha256=dk1G3VgiplZzmSfL7qWniEZ-5c3novHnBpRPCM77u84,1084
15
14
  teareduce/sdistortion.py,sha256=5ZsZn4vD5Sw2aoqO8-NIOH7H89Zmh7ZDkow6YbAotHU,5916
16
15
  teareduce/simulateccdexposure.py,sha256=cdbpca6GVuM3d7R1LGzlIZZvjTq_jzrlkk_Cli7aouQ,24636
17
- teareduce/sliceregion.py,sha256=AbkBqY7YaMYTvg-5BCdEoCB8NhIYsSMGcadXioCQfzo,18356
16
+ teareduce/sliceregion.py,sha256=Jdf8XvmGaY_vaY1cneTaRtSOYPxpUsJm9cXJDDMa0YM,18626
18
17
  teareduce/statsummary.py,sha256=VTNAnBV8z6suqiyB2Lhw3YjUUOjlmwUPX3enkOKRF54,5422
19
- teareduce/version.py,sha256=YfSDOnYOawt4dVMMDQzqFv1P8lf52tpIIDIBbNnrZW0,419
18
+ teareduce/version.py,sha256=wcSjl385CD5AkqC44ZOXcxI-nn2bOzLYHTz2gsT753c,419
20
19
  teareduce/wavecal.py,sha256=2MewWz5Y3ms41c305UtWOM_LaLNdk1mugDXCtzf-fSw,68586
21
20
  teareduce/write_array_to_fits.py,sha256=kWDrEH9coJ1yIu56oQJpWtDqJL4c8HGmssE9jle4e94,617
22
21
  teareduce/zscale.py,sha256=SDgmcDD2N5GvDn46JADCgTQJPBF_N_jSKMHoeTz9Nsw,1152
22
+ teareduce/cleanest/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ teareduce/cleanest/__main__.py,sha256=DGr32TeUa2gsiokQzNXkYrOCJcLWoE0BRQACKq20xfg,1371
24
+ teareduce/cleanest/cosmicraycleanerapp.py,sha256=qE8Wq6j240Pzfd5DB8mR579qnYt359yN0Lj5FZeFp4s,7976
25
+ teareduce/cleanest/reviewcosmicray.py,sha256=ha3MeSfT-RvSQnRSq8_M59JfnOGNd3IgtyHklrujE08,22450
26
+ teareduce/cookbook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ teareduce/cookbook/get_cookbook_file.py,sha256=n40HoWv3eiFtaquYWTAM6CbVfha9oUO9LHmnU2a5jkc,902
23
28
  teareduce/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
29
  teareduce/tests/test_sliceregion.py,sha256=S7Zoh2eEBFIEbfsXgWBEMCf7pottjw2oLhqlZJQkAwg,3785
25
30
  teareduce/tests/test_version.py,sha256=mKLnbXyvVNc1pATq5PxR8qeoFMPAFL_GilFV6IHLOi0,172
26
- teareduce-0.4.7.dist-info/licenses/LICENSE.txt,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
27
- teareduce-0.4.7.dist-info/METADATA,sha256=ylCxvLo5-wbLkWnlz1ve_c4THv-q6VHfB8RG2FUK1Ac,3017
28
- teareduce-0.4.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
- teareduce-0.4.7.dist-info/entry_points.txt,sha256=0ovOBIllSl9nN65mVOVVh--Gdd4AE2JebKuFk2YQ0wk,57
30
- teareduce-0.4.7.dist-info/top_level.txt,sha256=7OkwtX9zNRkGJ7ACgjk4ESgC74qUYcS5O2qcO0v-Si4,10
31
- teareduce-0.4.7.dist-info/RECORD,,
31
+ teareduce-0.4.9.dist-info/licenses/LICENSE.txt,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
32
+ teareduce-0.4.9.dist-info/METADATA,sha256=4N3TttqfbtFwlNcHNzRPZotpOp1ZKLkD9Nrtrx7tYpM,3017
33
+ teareduce-0.4.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
+ teareduce-0.4.9.dist-info/entry_points.txt,sha256=6yBvig5jTL2ugqz5SF767AiszzrHKGRASsX1II84kqA,66
35
+ teareduce-0.4.9.dist-info/top_level.txt,sha256=7OkwtX9zNRkGJ7ACgjk4ESgC74qUYcS5O2qcO0v-Si4,10
36
+ teareduce-0.4.9.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ tea-cleanest = teareduce.cleanest.__main__:main
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- tea-cleanest = teareduce.cleanest:main