plotastrodata 1.9.13__tar.gz → 1.9.15__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 (25) hide show
  1. {plotastrodata-1.9.13/plotastrodata.egg-info → plotastrodata-1.9.15}/PKG-INFO +1 -1
  2. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/__init__.py +1 -1
  3. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/analysis_utils.py +42 -35
  4. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/ext_utils.py +19 -4
  5. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/fft_utils.py +14 -14
  6. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/fits_utils.py +35 -26
  7. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/fitting_utils.py +46 -36
  8. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/los_utils.py +3 -3
  9. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/matrix_utils.py +4 -4
  10. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/noise_utils.py +34 -18
  11. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/other_utils.py +23 -10
  12. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/plot_utils.py +175 -66
  13. {plotastrodata-1.9.13 → plotastrodata-1.9.15/plotastrodata.egg-info}/PKG-INFO +1 -1
  14. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/LICENSE +0 -0
  15. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/MANIFEST.in +0 -0
  16. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/README.md +0 -0
  17. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/const_utils.py +0 -0
  18. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata/coord_utils.py +0 -0
  19. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata.egg-info/SOURCES.txt +0 -0
  20. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata.egg-info/dependency_links.txt +0 -0
  21. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata.egg-info/not-zip-safe +0 -0
  22. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata.egg-info/requires.txt +0 -0
  23. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/plotastrodata.egg-info/top_level.txt +0 -0
  24. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/setup.cfg +0 -0
  25. {plotastrodata-1.9.13 → plotastrodata-1.9.15}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plotastrodata
3
- Version: 1.9.13
3
+ Version: 1.9.15
4
4
  Summary: plotastrodata is a tool for astronomers to create figures from FITS files and perform fundamental data analyses with ease.
5
5
  Home-page: https://github.com/yusukeaso-astron/plotastrodata
6
6
  Download-URL: https://github.com/yusukeaso-astron/plotastrodata
@@ -1,4 +1,4 @@
1
1
  import warnings
2
2
 
3
3
  warnings.simplefilter('ignore', FutureWarning)
4
- __version__ = '1.9.13'
4
+ __version__ = '1.9.15'
@@ -3,7 +3,7 @@ import warnings
3
3
  from dataclasses import dataclass
4
4
  from scipy.interpolate import RegularGridInterpolator as RGI
5
5
  from scipy.signal import convolve
6
- from typing import Callable
6
+ from typing import Any, Callable
7
7
 
8
8
  from plotastrodata import const_utils as cu
9
9
  from plotastrodata.coord_utils import coord2xy, rel2abs, xy2coord
@@ -18,7 +18,7 @@ from plotastrodata.other_utils import (isdeg, nearest_index,
18
18
 
19
19
  def quadrantmean(data: np.ndarray, x: np.ndarray, y: np.ndarray,
20
20
  quadrants: str = '13'
21
- ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
21
+ ) -> tuple[np.ndarray, np.ndarray, np.ndarray] | None:
22
22
  """Take mean between 1st and 3rd (or 2nd and 4th) quadrants.
23
23
 
24
24
  Args:
@@ -52,7 +52,7 @@ def quadrantmean(data: np.ndarray, x: np.ndarray, y: np.ndarray,
52
52
 
53
53
 
54
54
  def filled2d(data: np.ndarray, x: np.ndarray, y: np.ndarray, n: int = 1,
55
- **kwargs) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
55
+ **kwargs: Any) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
56
56
  """Fill 2D data, 1D x, and 1D y by a factor of n using RGI.
57
57
 
58
58
  Args:
@@ -71,8 +71,8 @@ def filled2d(data: np.ndarray, x: np.ndarray, y: np.ndarray, n: int = 1,
71
71
  return d, xnew, ynew
72
72
 
73
73
 
74
- def _need_multipixels(method):
75
- def wrapper(cls, *args, **kwargs):
74
+ def _need_multipixels(method: Callable) -> Callable:
75
+ def wrapper(cls: Any, *args: Any, **kwargs: Any) -> Any | None:
76
76
  singlepixel = cls.dx is None or cls.dy is None
77
77
  if singlepixel:
78
78
  print('No pixel size.')
@@ -90,7 +90,7 @@ class AstroData():
90
90
  x (np.ndarray, optional): 1D array. Defaults to None.
91
91
  y (np.ndarray, optional): 1D array. Defaults to None.
92
92
  v (np.ndarray, optional): 1D array. Defaults to None.
93
- beam (np.ndarray, optional): [bmaj, bmin, bpa]. Defaults ot [None, None, None].
93
+ beam (np.ndarray, optional): [bmaj, bmin, bpa]. Defaults to [None, None, None].
94
94
  fitsimage (str, optional): Input fits name. Defaults to None.
95
95
  Tb (bool, optional): True means the data array is brightness temperature. Defaults to False.
96
96
  sigma (float or str, optional): Noise level or method for measuring it. Defaults to 'hist'.
@@ -116,7 +116,7 @@ class AstroData():
116
116
  pv: bool = False
117
117
  bunit: str = ''
118
118
 
119
- def __post_init__(self):
119
+ def __post_init__(self) -> None:
120
120
  n = 0
121
121
  if self.fitsimage is not None:
122
122
  if not isinstance(self.fitsimage, list):
@@ -142,7 +142,7 @@ class AstroData():
142
142
  self.beam_org = None
143
143
  self.fitsheader = None
144
144
 
145
- def _binning_one(self, t: str, width: float):
145
+ def _binning_one(self, t: str, width: float) -> None:
146
146
  grid = getattr(self, t)
147
147
  if width == 1 or grid is None:
148
148
  return
@@ -168,7 +168,7 @@ class AstroData():
168
168
  setattr(self, t, gridnew / width)
169
169
  setattr(self, dt, sep * width)
170
170
 
171
- def binning(self, width: list[int] = [1, 1, 1]):
171
+ def binning(self, width: list[int] = [1, 1, 1]) -> None:
172
172
  """Binning up neighboring pixels in the v, y, and x domain.
173
173
 
174
174
  Args:
@@ -202,7 +202,7 @@ class AstroData():
202
202
 
203
203
  def centering(self, includexy: bool = True,
204
204
  includev: bool = False,
205
- **kwargs):
205
+ **kwargs: Any) -> None:
206
206
  """Spatial regridding to set the center at (x,y,v)=(0,0,0).
207
207
 
208
208
  Args:
@@ -238,7 +238,7 @@ class AstroData():
238
238
  print('No change because includexy=False and includev=False.')
239
239
 
240
240
  @_need_multipixels
241
- def circularbeam(self):
241
+ def circularbeam(self) -> None:
242
242
  """Make the beam circular by convolving with 1D Gaussian
243
243
  """
244
244
  if None in self.beam:
@@ -262,7 +262,8 @@ class AstroData():
262
262
  self.beam[1] = self.beam[0]
263
263
  self.beam[2] = 0
264
264
 
265
- def deproject(self, pa: float = 0, incl: float = 0, **kwargs):
265
+ def deproject(self, pa: float = 0, incl: float = 0,
266
+ **kwargs: Any) -> None:
266
267
  """Exapnd by a factor of 1/cos(incl) in the direction of pa+90 deg.
267
268
 
268
269
  Args:
@@ -294,9 +295,12 @@ class AstroData():
294
295
  def fit2d(self, model: Callable, bounds: np.ndarray,
295
296
  progressbar: bool = False,
296
297
  kwargs_fit: dict = {}, kwargs_plotcorner: dict = {},
297
- chan: int | None = None):
298
+ chan: int | None = None) -> dict[str, Any] | None:
298
299
  """Fit a given 2D model function to self.data.
299
300
 
301
+ Default keyword values:
302
+ kwargs_plotcorner: ``show=False`` and ``savefig=None``. User-supplied values in ``kwargs_plotcorner`` override these defaults.
303
+
300
304
  Args:
301
305
  model (function): The model function in the form of f(par, x, y).
302
306
  bounds (np.ndarray): bounds for fitting_utils.EmceeCorner.
@@ -320,7 +324,7 @@ class AstroData():
320
324
  + ' This correction is relatively conservative.'
321
325
  warnings.warn(s, UserWarning)
322
326
 
323
- def logl(p):
327
+ def logl(p: np.ndarray) -> float:
324
328
  rss = np.nansum((model(p, x, y) - d)**2)
325
329
  return -0.5 * rss / self.sigma**2 / pixelperbeam
326
330
 
@@ -374,7 +378,7 @@ class AstroData():
374
378
  'model': model, 'residual': residual,
375
379
  'center': newcenter}
376
380
 
377
- def histogram(self, **kwargs) -> tuple:
381
+ def histogram(self, **kwargs: Any) -> tuple[np.ndarray, np.ndarray]:
378
382
  """Output histogram of self.data using numpy.histogram. This method can take the arguments of numpy.histogram.
379
383
 
380
384
  Returns:
@@ -387,12 +391,12 @@ class AstroData():
387
391
 
388
392
  def mask(self, dataformask: np.ndarray | None = None,
389
393
  includepix: list[float, float] = [],
390
- excludepix: list[float, float] = []):
394
+ excludepix: list[float, float] = []) -> None:
391
395
  """Mask self.data using a 2D or 3D array of dataformask.
392
396
 
393
397
  Args:
394
398
  dataformask (np.ndarray, optional): 2D or 3D array is used for specifying the mask.
395
- includepix (list, optional): Data in this range survivies. Defaults to [].
399
+ includepix (list, optional): Data in this range survives. Defaults to [].
396
400
  excludepix (list, optional): Data in this range is masked. Defaults to [].
397
401
  """
398
402
  if dataformask is None:
@@ -411,7 +415,7 @@ class AstroData():
411
415
  if len(excludepix) == 2:
412
416
  self.data[(excludepix[0] < mask) * (mask < excludepix[1])] = np.nan
413
417
 
414
- def _gfit_profile(self, prof: list, gaussfit: bool):
418
+ def _gfit_profile(self, prof: list, gaussfit: bool) -> dict[str, Any]:
415
419
  if not gaussfit:
416
420
  return {}
417
421
 
@@ -431,7 +435,7 @@ class AstroData():
431
435
  ellipse: list[float, float, float] | None = None,
432
436
  ninterp: int = 1,
433
437
  flux: bool = False, gaussfit: bool = False
434
- ) -> tuple[np.ndarray, np.ndarray, dict]:
438
+ ) -> tuple[np.ndarray, np.ndarray, dict] | None:
435
439
  """Get a list of line profiles at given spatial coordinates.
436
440
 
437
441
  Args:
@@ -477,7 +481,7 @@ class AstroData():
477
481
  gfitres = self._gfit_profile(prof, gaussfit)
478
482
  return self.v, prof, gfitres
479
483
 
480
- def rotate(self, pa: float = 0, **kwargs):
484
+ def rotate(self, pa: float = 0, **kwargs: Any) -> None:
481
485
  """Counter clockwise rotation with respect to the center.
482
486
 
483
487
  Args:
@@ -489,7 +493,7 @@ class AstroData():
489
493
  self.beam[2] = self.beam[2] + pa
490
494
 
491
495
  def slice(self, length: float = 0, pa: float = 0,
492
- dx: float | None = None, **kwargs) -> np.ndarray:
496
+ dx: float | None = None, **kwargs: Any) -> np.ndarray | None:
493
497
  """Get 1D slice with given a length and a position-angle.
494
498
 
495
499
  Args:
@@ -557,7 +561,7 @@ class AstroData():
557
561
  """Write out the AstroData to a FITS file.
558
562
 
559
563
  Args:
560
- fitsimage (str, optional): Output FITS file name. Defaults to 'out.fits'.
564
+ fitsimage (str, optional): Output FITS file name. Existing files with the same name are overwritten. Defaults to 'out.fits'.
561
565
  header (dict, optional): Header dictionary. Defaults to {}.
562
566
  """
563
567
  h = {}
@@ -588,18 +592,18 @@ class AstroData():
588
592
  fitsimage=fitsimage)
589
593
 
590
594
 
591
- def _as_list(value, n: int, isbeam: bool = False):
595
+ def _as_list(value: Any, n: int, isbeam: bool = False) -> Any:
592
596
  if isbeam:
593
597
  return [value] * n if np.ndim(value) == 1 else value
594
598
  else:
595
599
  return value if isinstance(value, list) else [value] * n
596
600
 
597
601
 
598
- def _scalar_if_single(value, n: int):
602
+ def _scalar_if_single(value: Any, n: int) -> Any:
599
603
  return value[0] if n == 1 else value
600
604
 
601
605
 
602
- def _get_gridsep(axis: np.ndarray | None):
606
+ def _get_gridsep(axis: np.ndarray | None) -> float | None:
603
607
  return axis[1] - axis[0] if axis is not None and len(axis) > 1 else None
604
608
 
605
609
 
@@ -618,7 +622,7 @@ class AstroFrame():
618
622
  vsys (float, optional): Each channel shows v-vsys. Defaults to 0..
619
623
  center (str, optional): Central coordinate like '12h34m56.7s 12d34m56.7s'. Defaults to None.
620
624
  fitsimage (str, optional): Fits to get center. Defaults to None.
621
- rmax (float, optional): The x range is [-rmax, rmax]. The y range is [-rmax, ramx]. Defaults to 1e10.
625
+ rmax (float, optional): The x range is [-rmax, rmax]. The y range is [-rmax, rmax]. Defaults to 1e10.
622
626
  xmax (float, optional): The x range is [xmin, xmax]. Defaults to None.
623
627
  xmin (float, optional): The x range is [xmin, xmax]. Defaults to None.
624
628
  ymax (float, optional): The y range is [ymin, ymax]. Defaults to None.
@@ -651,7 +655,7 @@ class AstroFrame():
651
655
  pv: bool = False
652
656
  quadrants: str | None = None
653
657
 
654
- def __post_init__(self):
658
+ def __post_init__(self) -> None:
655
659
  self.xdir = -1 if self.xflip else 1
656
660
  self.ydir = -1 if self.yflip else 1
657
661
  self.xmin = -self.rmax if self.xmin is None else self.xmin
@@ -699,7 +703,7 @@ class AstroFrame():
699
703
  x[i], y[i] = rel2abs(*p, self.Xlim, self.Ylim)
700
704
  return np.array([x, y])
701
705
 
702
- def _get_restfreq(self, header: dict):
706
+ def _get_restfreq(self, header: dict) -> float | None:
703
707
  """Extract rest frequency from FITS header."""
704
708
  if 'RESTFRQ' in header:
705
709
  return header['RESTFRQ']
@@ -743,21 +747,22 @@ class AstroFrame():
743
747
  d.center[i] = cnew
744
748
  return grid
745
749
 
746
- def _ascending_v(self, d: AstroData, i: int, v: np.ndarray | None):
750
+ def _ascending_v(self, d: AstroData, i: int,
751
+ v: np.ndarray | None) -> None:
747
752
  if v is not None and len(v) > 1 and v[1] < v[0]:
748
753
  d.data[i], v = d.data[i][::-1], v[::-1]
749
754
  print('Velocity has been inverted.')
750
755
  d.v = v
751
756
 
752
757
  def _xyskip(self, d: AstroData, i: int,
753
- x: np.ndarray | None, y: np.ndarray | None):
758
+ x: np.ndarray | None, y: np.ndarray | None) -> None:
754
759
  d.x = x[::self.xskip]
755
760
  d.y = y[::self.yskip]
756
761
  data = np.moveaxis(d.data[i], [-2, -1], [0, 1])
757
762
  data = data[::self.yskip, ::self.xskip]
758
763
  d.data[i] = np.moveaxis(data, [0, 1], [-2, -1])
759
764
 
760
- def _trim_skip(self, d: AstroData, i: int, grid: list):
765
+ def _trim_skip(self, d: AstroData, i: int, grid: list) -> None:
761
766
  d.data[i], grid = trim(data=d.data[i],
762
767
  x=grid[0], y=grid[1], v=grid[2],
763
768
  xlim=self.xlim, ylim=self.ylim,
@@ -773,7 +778,7 @@ class AstroFrame():
773
778
  for axis in ['x', 'y', 'v']:
774
779
  setattr(d, f'd{axis}', _get_gridsep(getattr(d, axis)))
775
780
 
776
- def _convert_to_Tb(self, d: AstroData, i: int):
781
+ def _convert_to_Tb(self, d: AstroData, i: int) -> None:
777
782
  """Convert Jy/beam data to brightness temperature if requested."""
778
783
  if not d.Tb[i]:
779
784
  return
@@ -790,7 +795,7 @@ class AstroFrame():
790
795
  if d.sigma[i] is not None:
791
796
  d.sigma[i] = d.sigma[i] * factor
792
797
 
793
- def _set_pv_beam(self, d: AstroData, i: int):
798
+ def _set_pv_beam(self, d: AstroData, i: int) -> None:
794
799
  """Set effective PV beam."""
795
800
  if not self.pv or d.pv[i] or None in d.beam[i]:
796
801
  return
@@ -803,7 +808,7 @@ class AstroFrame():
803
808
  beam_incut = 1 / np.hypot(np.cos(angle) / bmaj, np.sin(angle) / bmin)
804
809
  d.beam[i] = np.array([np.abs(d.dv), beam_incut, 0])
805
810
 
806
- def _read_one(self, d: AstroData, i: int):
811
+ def _read_one(self, d: AstroData, i: int) -> None:
807
812
  if d.center[i] == 'common':
808
813
  d.center[i] = self.center
809
814
  d.sigma_org[i] = d.sigma[i]
@@ -826,9 +831,11 @@ class AstroFrame():
826
831
  d.fitsimage_org[i] = d.fitsimage[i]
827
832
  d.fitsimage[i] = None
828
833
 
829
- def read(self, d: AstroData, xskip: int = 1, yskip: int = 1):
834
+ def read(self, d: AstroData, xskip: int = 1, yskip: int = 1) -> None:
830
835
  """Get data, grid, sigma, beam, and bunit from AstroData, which is a part of the input of add_color, add_contour, add_segment, and add_rgb.
831
836
 
837
+ This method modifies ``d`` in place. During the read, the input fields are normalized to per-dataset lists, FITS-derived values are filled, trimming and coordinate-frame changes are applied, and bookkeeping fields such as ``fitsimage``, ``fitsimage_org``, ``Tb``, ``cfactor``, and ``sigma`` are updated.
838
+
832
839
  Args:
833
840
  d (AstroData): Dataclass for the add_* input.
834
841
  xskip, yskip (int): Spatial pixel skip. Defaults to 1.
@@ -1,26 +1,41 @@
1
1
  import numpy as np
2
2
  import shlex
3
3
  import subprocess
4
+ from typing import Any
4
5
 
5
6
  from plotastrodata import const_utils as cu
6
7
 
7
8
 
8
- def terminal(cmd: str, **kwargs) -> None:
9
+ def terminal(cmd: str, check: bool = True,
10
+ **kwargs: Any) -> subprocess.CompletedProcess:
9
11
  """Run a terminal command through subprocess.run.
10
12
 
13
+ The command is split with ``shlex.split`` and executed without a shell. It can still run destructive programs, so pass only trusted command strings.
14
+
11
15
  Args:
12
16
  cmd (str): Terminal command.
17
+ check (bool, optional): Whether to raise an exception if the command fails. Defaults to True.
18
+
19
+ Returns:
20
+ subprocess.CompletedProcess: Result from subprocess.run.
13
21
  """
14
- subprocess.run(shlex.split(cmd), **kwargs)
22
+ return subprocess.run(shlex.split(cmd), check=check, **kwargs)
15
23
 
16
24
 
17
- def runpython(filename: str, **kwargs) -> None:
25
+ def runpython(filename: str, check: bool = True,
26
+ **kwargs: Any) -> subprocess.CompletedProcess:
18
27
  """Run a python file.
19
28
 
29
+ The file is executed as a Python script and can perform arbitrary file or system operations. Pass only trusted files.
30
+
20
31
  Args:
21
32
  filename (str): Python file name.
33
+ check (bool, optional): Whether to raise an exception if the script fails. Defaults to True.
34
+
35
+ Returns:
36
+ subprocess.CompletedProcess: Result from subprocess.run.
22
37
  """
23
- terminal(f'python {filename}', **kwargs)
38
+ return subprocess.run(['python', filename], check=check, **kwargs)
24
39
 
25
40
 
26
41
  def BnuT(T: float = 30, nu: float = 230e9) -> float:
@@ -163,7 +163,7 @@ def ifftcentering2(F: np.ndarray,
163
163
  Args:
164
164
  F (np.ndarray): 2D array. An FFT result in the frequency domain.
165
165
  u (np.ndarray, optional): 1D or 2D array. The first frequency coordinate. Defaults to None.
166
- v (np.ndarray, optional): 1D or 2D array. The second frequency cooridnate. Defaults to None.
166
+ v (np.ndarray, optional): 1D or 2D array. The second frequency coordinate. Defaults to None.
167
167
  xcenter (float, optional): x of phase reference (used in fftcentering2). Defaults to 0.
168
168
  ycenter (float, optional): y of phase reference (used in fftcentering2). Defaults to 0.
169
169
  x0 (float, optional): spatial coordinate of x[0]. Defaults to None.
@@ -221,7 +221,7 @@ class FftCentering():
221
221
  y: np.ndarray | None = None,
222
222
  xcenter: float = 0,
223
223
  ycenter: float = 0,
224
- rfft: bool = False):
224
+ rfft: bool = False) -> None:
225
225
  nx = len(x)
226
226
  self.x = x
227
227
  self.dx = dx = x[1] - x[0]
@@ -252,14 +252,14 @@ class FftCentering():
252
252
  np.ndarray: FFT result. When f is None, the return is the FFT function.
253
253
  """
254
254
  if self.ndim == 1:
255
- def func(f: np.ndarray):
255
+ def func(f: np.ndarray) -> np.ndarray:
256
256
  F, _ = fftcentering(f=f, x=self.x,
257
257
  xcenter=self.xcenter,
258
258
  rfft=self.rfft)
259
259
  return F
260
260
 
261
261
  else:
262
- def func(f: np.ndarray):
262
+ def func(f: np.ndarray) -> np.ndarray:
263
263
  F, _, _ = fftcentering2(f=f, x=self.x, y=self.y,
264
264
  xcenter=self.xcenter,
265
265
  ycenter=self.ycenter,
@@ -279,7 +279,7 @@ class FftCentering():
279
279
  np.ndarray: iFFT result. When F is None, the return is the iFFT function.
280
280
  """
281
281
  if self.ndim == 1:
282
- def func(F: np.ndarray):
282
+ def func(F: np.ndarray) -> np.ndarray:
283
283
  f, _ = ifftcentering(F=F, u=self.u,
284
284
  xcenter=self.xcenter,
285
285
  x0=self.x[0],
@@ -289,7 +289,7 @@ class FftCentering():
289
289
  return f
290
290
 
291
291
  else:
292
- def func(F: np.ndarray):
292
+ def func(F: np.ndarray) -> np.ndarray:
293
293
  f, _, _ = ifftcentering2(F=F, u=self.u, v=self.v,
294
294
  xcenter=self.xcenter,
295
295
  ycenter=self.ycenter,
@@ -349,7 +349,7 @@ def fftfits(fitsimage: str, center: str | None = None, lam: float = 1,
349
349
  lam (float, optional): Return u * lam and v * lam. Defaults to 1.
350
350
  xlim (list, optional): Range of x for zero padding in arcsec.
351
351
  ylim (list, optional): Range of y for zero padding in arcsec.
352
- savefig (dict or str, optional): For plt.figure().savefig(). Defaults to None.
352
+ savefig (dict or str, optional): Passed to ``close_figure``. Existing files may be overwritten, and the figure is closed after saving/showing. Defaults to None.
353
353
  show (bool, optional): True means doing plt.show(). Defaults to False.
354
354
 
355
355
  Returns:
@@ -384,16 +384,16 @@ def fftfits(fitsimage: str, center: str | None = None, lam: float = 1,
384
384
  def findindex(u: np.ndarray | None = None, v: np.ndarray | None = None,
385
385
  uobs: np.ndarray | None = None, vobs: np.ndarray | None = None
386
386
  ) -> np.ndarray:
387
- """Find indicies of the observed visibility points.
387
+ """Find indices of the observed visibility points.
388
388
 
389
389
  Args:
390
390
  u (np.ndarray, optional): 1D array. The first frequency coordinate. Defaults to None.
391
- v (np.ndarray, optional): 1D array. The second frequency cooridnate. Defaults to None.
391
+ v (np.ndarray, optional): 1D array. The second frequency coordinate. Defaults to None.
392
392
  uobs (np.ndarray, optional): 1D array. Observed u. Defaults to None.
393
393
  vobs (np.ndarray, optional): 1D array. Observed v. Defaults to None.
394
394
 
395
395
  Returns:
396
- np.ndarray: Indicies or an array of indicies.
396
+ np.ndarray: Indices or an array of indices.
397
397
  """
398
398
  if u is not None:
399
399
  Nu, du = len(u), u[1] - u[0]
@@ -417,13 +417,13 @@ def fftfitssample(fitsimage: str, center: str | None = None,
417
417
  getindex: bool = False,
418
418
  u_sample: np.ndarray | None = None,
419
419
  v_sample: np.ndarray | None = None) -> np.ndarray:
420
- """Find indicies or the visibilities on them from an image fits file.
420
+ """Find indices or the visibilities on them from an image FITS file.
421
421
 
422
422
  Args:
423
423
  fitsimage (str): Input fits name in the unit of Jy/pixel.
424
424
  center (str, optional): Text coordinate. Defaults to None.
425
- index_u (np.ndarray, optional): Indicies. Output from the getindex mode. Defaults to None.
426
- index_v (np.ndarray, optional): Indicies. Output from the getindex mode. Defaults to None.
425
+ index_u (np.ndarray, optional): Indices. Output from the getindex mode. Defaults to None.
426
+ index_v (np.ndarray, optional): Indices. Output from the getindex mode. Defaults to None.
427
427
  xlim (list, optional): Range of x for zero padding in arcsec.
428
428
  ylim (list, optional): Range of y for zero padding in arcsec.
429
429
  getindex (bool, optional): True outputs [index_u, index_v]. Defaults to False.
@@ -431,7 +431,7 @@ def fftfitssample(fitsimage: str, center: str | None = None,
431
431
  v_sample (np.ndarray, optional): 1D array. Observed u. Defaults to None.
432
432
 
433
433
  Returns:
434
- np.ndarray: Array of indicies or sampled FFT.
434
+ np.ndarray: Array of indices or sampled FFT.
435
435
  """
436
436
  F, u, v = fftfits(fitsimage=fitsimage, center=center, xlim=xlim, ylim=ylim)
437
437
  if index_u is None or index_v is None:
@@ -1,6 +1,7 @@
1
1
  import numpy as np
2
2
  from astropy import units, wcs
3
3
  from astropy.io import fits
4
+ from typing import Any, Callable
4
5
 
5
6
  from plotastrodata import const_utils as cu
6
7
  from plotastrodata.coord_utils import coord2xy, xy2coord
@@ -9,15 +10,16 @@ from plotastrodata.noise_utils import estimate_rms
9
10
  from plotastrodata.other_utils import isdeg, RGIxy, trim
10
11
 
11
12
 
12
- def Jy2K(header=None, bmaj: float | None = None, bmin: float | None = None,
13
- restfreq: float | None = None) -> float:
13
+ def Jy2K(header: Any = None, bmaj: float | None = None,
14
+ bmin: float | None = None,
15
+ restfreq: float | None = None) -> float | None:
14
16
  """Calculate a conversion factor in the unit of K/Jy.
15
17
 
16
18
  Args:
17
19
  header (optional): astropy.io.fits.open('a.fits')[0].header. Defaults to None.
18
- bmaj (float, optional): beam major axis in degree. Defaults to None.
19
- bmin (float, optional): beam minor axis in degree. Defaults to None.
20
- freq (float, optional): rest frequency in Hz. Defaults to None.
20
+ bmaj (float, optional): Beam major axis in arcsec. Defaults to None.
21
+ bmin (float, optional): Beam minor axis in arcsec. Defaults to None.
22
+ restfreq (float, optional): Rest frequency in Hz. Defaults to None.
21
23
 
22
24
  Returns:
23
25
  float: the conversion factor in the unit of K/Jy.
@@ -53,11 +55,11 @@ class FitsData:
53
55
  Args:
54
56
  fitsimage (str): Input FITS file name.
55
57
  """
56
- def __init__(self, fitsimage: str):
58
+ def __init__(self, fitsimage: str) -> None:
57
59
  self.fitsimage = fitsimage
58
60
 
59
61
  def gen_hdu(self) -> None:
60
- """Generate self.hdu, which is astropy,io.fits.open()[0].
62
+ """Generate self.hdu, which is astropy.io.fits.open()[0].
61
63
  """
62
64
  hdu = fits.open(self.fitsimage)
63
65
  self.hdu = hdu[0]
@@ -75,7 +77,7 @@ class FitsData:
75
77
  self.gen_hdu()
76
78
  self.header = self.hdu.header
77
79
 
78
- def get_header(self, key: str | None = None) -> dict | float:
80
+ def get_header(self, key: str | None = None) -> Any:
79
81
  """Output the entire header or a value when a key is given.
80
82
 
81
83
  Args:
@@ -94,7 +96,7 @@ class FitsData:
94
96
  return None
95
97
 
96
98
  def gen_beam(self, dist: float = 1.) -> None:
97
- """Generate sef.bmaj, self.bmin, self.bpa from header['BMAJ'], etc.
99
+ """Generate self.bmaj, self.bmin, self.bpa from header['BMAJ'], etc.
98
100
 
99
101
  Args:
100
102
  dist (float, optional): bmaj and bmin are multiplied by dist. Defaults to 1..
@@ -146,7 +148,7 @@ class FitsData:
146
148
  return a
147
149
 
148
150
  def gen_data(self, Tb: bool = False, log: bool = False,
149
- drop: bool = True, restfreq: float = None) -> None:
151
+ drop: bool = True, restfreq: float | None = None) -> None:
150
152
  """Generate data, which may be brightness temperature.
151
153
 
152
154
  Args:
@@ -167,7 +169,7 @@ class FitsData:
167
169
  d = np.log10(d.clip(np.min(d[d > 0]), None))
168
170
  self.data = d
169
171
 
170
- def get_data(self, **kwargs) -> np.ndarray:
172
+ def get_data(self, **kwargs: Any) -> np.ndarray:
171
173
  """Output data. This method can take the arguments of gen_data().
172
174
 
173
175
  Returns:
@@ -177,7 +179,7 @@ class FitsData:
177
179
  self.gen_data(**kwargs)
178
180
  return self.data
179
181
 
180
- def _read_cd(self):
182
+ def _read_cd(self) -> None:
181
183
  h = self.header
182
184
  cdij = ['CD1_1', 'CD1_2', 'CD2_1', 'CD2_2']
183
185
  if not np.all([k in list(h.keys()) for k in cdij]):
@@ -211,7 +213,7 @@ class FitsData:
211
213
  del h[k]
212
214
  print(f'WCS rotation was found (CROTA2 = {crota2:f} deg).')
213
215
 
214
- def _rotate_cd(self):
216
+ def _rotate_cd(self) -> None:
215
217
  h = self.header
216
218
  data = self.get_data()
217
219
  ic = len(self.x) // 2
@@ -237,7 +239,10 @@ class FitsData:
237
239
  self.data = datanew
238
240
  print('Data values were interpolated for WCS rotation.')
239
241
 
240
- def _get_genx_geny(self, center: str, dist: float):
242
+ def _get_genx_geny(self, center: str,
243
+ dist: float
244
+ ) -> tuple[Callable[[np.ndarray | None], None],
245
+ Callable[[np.ndarray | None], None]]:
241
246
  h = self.header
242
247
  cxy = (0, 0)
243
248
  if center is not None and not self.wcsrot:
@@ -247,7 +252,7 @@ class FitsData:
247
252
  cxy = coord2xy(center, coordorg)
248
253
  slabel = ['x', 'y']
249
254
 
250
- def wrapper(i: int):
255
+ def wrapper(i: int) -> Callable[[np.ndarray | None], None]:
251
256
  def gen_s(s_in: np.ndarray | None) -> None:
252
257
  if h.get(f'NAXIS{i+1}') is None or s_in is None:
253
258
  s, ds = None, None
@@ -262,10 +267,11 @@ class FitsData:
262
267
 
263
268
  return wrapper(0), wrapper(1)
264
269
 
265
- def _get_genv(self, restfreq: float | None, vsys: float, pv: bool):
270
+ def _get_genv(self, restfreq: float | None, vsys: float,
271
+ pv: bool) -> Callable[[np.ndarray | None], None]:
266
272
  h = self.header
267
273
 
268
- def gen_v(v_in: np.ndarray) -> None:
274
+ def gen_v(v_in: np.ndarray | None) -> None:
269
275
  vaxis = '2' if pv else '3'
270
276
  if h.get(f'NAXIS{vaxis}') is None or v_in is None:
271
277
  self.v, self.dv = None, None
@@ -299,7 +305,7 @@ class FitsData:
299
305
 
300
306
  return gen_v
301
307
 
302
- def _get_array(self, i: int) -> np.ndarray:
308
+ def _get_array(self, i: int) -> np.ndarray | None:
303
309
  h = self.header
304
310
  n = h.get(f'NAXIS{i:d}')
305
311
  if n is None:
@@ -337,7 +343,9 @@ class FitsData:
337
343
  if self.wcsrot:
338
344
  self._rotate_cd()
339
345
 
340
- def get_grid(self, **kwargs) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
346
+ def get_grid(self, **kwargs: Any
347
+ ) -> tuple[np.ndarray | None, np.ndarray | None,
348
+ np.ndarray | None]:
341
349
  """Output the grids, [x, y, v]. This method can take the arguments of gen_grid().
342
350
 
343
351
  Returns:
@@ -374,15 +382,16 @@ class FitsData:
374
382
  def fits2data(fitsimage: str, Tb: bool = False, log: bool = False,
375
383
  dist: float = 1., sigma: str | None = None,
376
384
  restfreq: float | None = None, center: str | None = None,
377
- vsys: float = 0., pv: bool = False, **kwargs
378
- ) -> tuple[np.ndarray, tuple[np.ndarray, np.ndarray, np.ndarray],
379
- np.ndarray, str, float]:
385
+ vsys: float = 0., pv: bool = False, **kwargs: Any
386
+ ) -> tuple[np.ndarray, tuple[np.ndarray | None, np.ndarray | None,
387
+ np.ndarray | None],
388
+ np.ndarray, str | None, float | None]:
380
389
  """Extract data from a fits file. kwargs are arguments of FitsData.trim().
381
390
 
382
391
  Args:
383
392
  fitsimage (str): Input fits name.
384
- Tb (bool, optional): True means ouput data are brightness temperature. Defaults to False.
385
- log (bool, optional): True means output data are logarhismic. Defaults to False.
393
+ Tb (bool, optional): True means output data are brightness temperature. Defaults to False.
394
+ log (bool, optional): True means output data are logarithmic. Defaults to False.
386
395
  dist (float, optional): Change x and y in arcsec to au. Defaults to 1..
387
396
  sigma (str, optional): Noise level or method for measuring it. Defaults to None.
388
397
  restfreq (float, optional): Used for velocity and brightness temperature. Defaults to None.
@@ -411,8 +420,8 @@ def data2fits(d: np.ndarray, h: dict = {},
411
420
  Args:
412
421
  d (np.ndarray): N-D array.
413
422
  h (dict, optional): Additional FITS header. Defaults to {}.
414
- templatefits (str, optional): FITS file whose header is used as a temperate. Defaults to None.
415
- fitsimage (str, optional): Output filename, with or without '.fits'. Defaults to 'test'.
423
+ templatefits (str, optional): FITS file whose header is used as a template. Defaults to None.
424
+ fitsimage (str, optional): Output filename, with or without '.fits'. Existing files with the same name are overwritten. Defaults to 'test'.
416
425
  """
417
426
  _h = {} if templatefits is None else FitsData(templatefits).get_header()
418
427
  _h.update(h)