plotastrodata 1.9.9__tar.gz → 1.9.10__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.
- {plotastrodata-1.9.9/plotastrodata.egg-info → plotastrodata-1.9.10}/PKG-INFO +1 -1
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata/__init__.py +1 -1
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata/analysis_utils.py +22 -22
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata/coord_utils.py +4 -4
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata/fits_utils.py +19 -19
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata/fitting_utils.py +2 -2
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata/noise_utils.py +4 -4
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata/other_utils.py +4 -4
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata/plot_utils.py +58 -74
- {plotastrodata-1.9.9 → plotastrodata-1.9.10/plotastrodata.egg-info}/PKG-INFO +1 -1
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/LICENSE +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/MANIFEST.in +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/README.md +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata/const_utils.py +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata/ext_utils.py +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata/fft_utils.py +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata/los_utils.py +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata/matrix_utils.py +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata.egg-info/SOURCES.txt +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata.egg-info/dependency_links.txt +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata.egg-info/not-zip-safe +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata.egg-info/requires.txt +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/plotastrodata.egg-info/top_level.txt +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/setup.cfg +0 -0
- {plotastrodata-1.9.9 → plotastrodata-1.9.10}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plotastrodata
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.10
|
|
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
|
|
@@ -117,7 +117,7 @@ class AstroData():
|
|
|
117
117
|
def __post_init__(self):
|
|
118
118
|
n = 0
|
|
119
119
|
if self.fitsimage is not None:
|
|
120
|
-
if
|
|
120
|
+
if not isinstance(self.fitsimage, list):
|
|
121
121
|
n = 1
|
|
122
122
|
elif any(a is not None for a in self.fitsimage):
|
|
123
123
|
n = len(self.fitsimage)
|
|
@@ -126,7 +126,7 @@ class AstroData():
|
|
|
126
126
|
if n > 0:
|
|
127
127
|
self.data = None
|
|
128
128
|
if self.data is not None:
|
|
129
|
-
if
|
|
129
|
+
if not isinstance(self.data, list):
|
|
130
130
|
n = 1
|
|
131
131
|
elif any(a is not None for a in self.data):
|
|
132
132
|
n = len(self.data)
|
|
@@ -590,9 +590,9 @@ def _get_gridsep(axis: np.ndarray | None):
|
|
|
590
590
|
return axis[1] - axis[0] if axis is not None and len(axis) > 1 else None
|
|
591
591
|
|
|
592
592
|
|
|
593
|
-
ASTRODATA_ARGS = [
|
|
594
|
-
|
|
595
|
-
|
|
593
|
+
ASTRODATA_ARGS = ['fitsimage', 'data', 'Tb', 'sigma', 'center', 'restfreq',
|
|
594
|
+
'cfactor', 'bunit', 'fitsimage_org', 'sigma_org',
|
|
595
|
+
'beam_org', 'fitsheader', 'pv', 'pvpa']
|
|
596
596
|
|
|
597
597
|
|
|
598
598
|
@dataclass
|
|
@@ -675,12 +675,12 @@ class AstroFrame():
|
|
|
675
675
|
Returns:
|
|
676
676
|
np.ndarray: absolute coordinates.
|
|
677
677
|
"""
|
|
678
|
-
onexy = np.shape(poslist) == (2,) and
|
|
678
|
+
onexy = np.shape(poslist) == (2,) and not isinstance(poslist[0], str)
|
|
679
679
|
if np.shape(poslist) == () or onexy:
|
|
680
680
|
poslist = [poslist]
|
|
681
681
|
x, y = [None] * len(poslist), [None] * len(poslist)
|
|
682
682
|
for i, p in enumerate(poslist):
|
|
683
|
-
if
|
|
683
|
+
if isinstance(p, str):
|
|
684
684
|
x[i], y[i] = coord2xy(p, self.center) * 3600.
|
|
685
685
|
else:
|
|
686
686
|
x[i], y[i] = rel2abs(*p, self.Xlim, self.Ylim)
|
|
@@ -688,12 +688,12 @@ class AstroFrame():
|
|
|
688
688
|
|
|
689
689
|
def _get_restfreq(self, header: dict):
|
|
690
690
|
"""Extract rest frequency from FITS header."""
|
|
691
|
-
if
|
|
692
|
-
return header[
|
|
693
|
-
if
|
|
694
|
-
return header[
|
|
695
|
-
if
|
|
696
|
-
return header[
|
|
691
|
+
if 'RESTFRQ' in header:
|
|
692
|
+
return header['RESTFRQ']
|
|
693
|
+
if 'RESTFREQ' in header:
|
|
694
|
+
return header['RESTFREQ']
|
|
695
|
+
if 'NAXIS3' in header and header['NAXIS3'] == 1 and not self.pv:
|
|
696
|
+
return header['CRVAL3']
|
|
697
697
|
return None
|
|
698
698
|
|
|
699
699
|
def _read_fitsimage(self, d: AstroData, i: int, grid: list) -> list:
|
|
@@ -715,7 +715,7 @@ class AstroFrame():
|
|
|
715
715
|
if fd.wcsrot:
|
|
716
716
|
d.center[i] = fd.get_center()
|
|
717
717
|
d.beam[i] = fd.get_beam(dist=self.dist)
|
|
718
|
-
d.bunit[i] = fd.get_header(
|
|
718
|
+
d.bunit[i] = fd.get_header('BUNIT')
|
|
719
719
|
return grid
|
|
720
720
|
|
|
721
721
|
def _shift_center(self, d: AstroData, i: int, grid: list) -> list:
|
|
@@ -758,7 +758,7 @@ class AstroFrame():
|
|
|
758
758
|
if self.pv:
|
|
759
759
|
d.v = d.y
|
|
760
760
|
for axis in ['x', 'y', 'v']:
|
|
761
|
-
setattr(d, f
|
|
761
|
+
setattr(d, f'd{axis}', _get_gridsep(getattr(d, axis)))
|
|
762
762
|
|
|
763
763
|
def _convert_to_Tb(self, d: AstroData, i: int):
|
|
764
764
|
"""Convert Jy/beam data to brightness temperature if requested."""
|
|
@@ -766,12 +766,12 @@ class AstroFrame():
|
|
|
766
766
|
return
|
|
767
767
|
|
|
768
768
|
dx = d.dy if self.swapxy else d.dx
|
|
769
|
-
header = {
|
|
770
|
-
|
|
771
|
-
|
|
769
|
+
header = {'CDELT1': dx / 3600,
|
|
770
|
+
'CUNIT1': 'deg',
|
|
771
|
+
'RESTFREQ': d.restfreq[i]}
|
|
772
772
|
if None not in d.beam[i]:
|
|
773
|
-
header[
|
|
774
|
-
header[
|
|
773
|
+
header['BMAJ'] = d.beam[i][0] / 3600 / self.dist
|
|
774
|
+
header['BMIN'] = d.beam[i][1] / 3600 / self.dist
|
|
775
775
|
factor = Jy2K(header=header)
|
|
776
776
|
d.data[i] = d.data[i] * factor
|
|
777
777
|
if d.sigma[i] is not None:
|
|
@@ -785,7 +785,7 @@ class AstroFrame():
|
|
|
785
785
|
bmaj, bmin, bpa = d.beam_org[i] = d.beam[i]
|
|
786
786
|
if d.pvpa[i] is None:
|
|
787
787
|
d.pvpa[i] = bpa
|
|
788
|
-
print(
|
|
788
|
+
print('pvpa is not specified. pvpa=bpa is assumed.')
|
|
789
789
|
angle = np.radians(bpa - d.pvpa[i])
|
|
790
790
|
beam_incut = 1 / np.hypot(np.cos(angle) / bmaj, np.sin(angle) / bmin)
|
|
791
791
|
d.beam[i] = np.array([np.abs(d.dv), beam_incut, 0])
|
|
@@ -827,5 +827,5 @@ class AstroFrame():
|
|
|
827
827
|
d.beam = _as_list(d.beam, d.n, isbeam=True)
|
|
828
828
|
for i in range(d.n):
|
|
829
829
|
self._read_one(d, i)
|
|
830
|
-
for name in ASTRODATA_ARGS + [
|
|
830
|
+
for name in ASTRODATA_ARGS + ['beam']:
|
|
831
831
|
setattr(d, name, _scalar_if_single(getattr(d, name), d.n))
|
|
@@ -18,7 +18,7 @@ def _updateframe(frame: str) -> str:
|
|
|
18
18
|
a = FK5(equinox='J2000')
|
|
19
19
|
elif 'B1950' in frame or 'FK4' in frame:
|
|
20
20
|
a = FK4(equinox='B1950')
|
|
21
|
-
elif
|
|
21
|
+
elif isinstance(frame, str):
|
|
22
22
|
print(f'Unknown frame ({frame}) was found. Use ICRS instead.')
|
|
23
23
|
a = 'icrs'
|
|
24
24
|
else:
|
|
@@ -26,7 +26,7 @@ def _updateframe(frame: str) -> str:
|
|
|
26
26
|
return a
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def _getframe(coord: str) -> tuple:
|
|
29
|
+
def _getframe(coord: str | list) -> tuple:
|
|
30
30
|
"""Internal function to pick up the frame name from the coordinates. When coord is a list, frame and framename are picked up from the first element.
|
|
31
31
|
|
|
32
32
|
Args:
|
|
@@ -43,7 +43,7 @@ def _getframe(coord: str) -> tuple:
|
|
|
43
43
|
framename = c[0] if hasframe else None
|
|
44
44
|
return hmsdms, frame, framename
|
|
45
45
|
|
|
46
|
-
if
|
|
46
|
+
if isinstance(coord, str):
|
|
47
47
|
return getframe_single(coord)
|
|
48
48
|
else:
|
|
49
49
|
outlist = [getframe_single(c) for c in coord]
|
|
@@ -108,7 +108,7 @@ def xy2coord(xy: list, coordorg: str = '00h00m00s 00d00m00s',
|
|
|
108
108
|
coords = coords.transform_to(frame=frame)
|
|
109
109
|
coords = coords.to_string('hmsdms')
|
|
110
110
|
if framename is not None:
|
|
111
|
-
if
|
|
111
|
+
if isinstance(coords, str):
|
|
112
112
|
coords = f'{framename} {coords}'
|
|
113
113
|
else:
|
|
114
114
|
coords = np.array([f'{framename} {s}' for s in coords])
|
|
@@ -241,23 +241,23 @@ class FitsData:
|
|
|
241
241
|
h = self.header
|
|
242
242
|
cxy = (0, 0)
|
|
243
243
|
if center is not None and not self.wcsrot:
|
|
244
|
-
coordorg = xy2coord([h[
|
|
245
|
-
if (radesys := h.get(
|
|
246
|
-
coordorg = f
|
|
244
|
+
coordorg = xy2coord([h['CRVAL1'], h['CRVAL2']])
|
|
245
|
+
if (radesys := h.get('RADESYS')) is not None:
|
|
246
|
+
coordorg = f'{radesys} {coordorg}'
|
|
247
247
|
cxy = coord2xy(center, coordorg)
|
|
248
|
-
slabel = [
|
|
248
|
+
slabel = ['x', 'y']
|
|
249
249
|
|
|
250
250
|
def wrapper(i: int):
|
|
251
251
|
def gen_s(s_in: np.ndarray | None) -> None:
|
|
252
|
-
if h.get(f
|
|
252
|
+
if h.get(f'NAXIS{i+1}') is None or s_in is None:
|
|
253
253
|
s, ds = None, None
|
|
254
254
|
else:
|
|
255
255
|
s = (s_in - cxy[i]) * dist
|
|
256
|
-
if isdeg(h[f
|
|
256
|
+
if isdeg(h[f'CUNIT{i+1}']):
|
|
257
257
|
s *= 3600.
|
|
258
258
|
ds = None if len(s) == 0 else s[1] - s[0]
|
|
259
|
-
setattr(self, f
|
|
260
|
-
setattr(self, f
|
|
259
|
+
setattr(self, f'{slabel[i]}', s)
|
|
260
|
+
setattr(self, f'd{slabel[i]}', ds)
|
|
261
261
|
return gen_s
|
|
262
262
|
|
|
263
263
|
return wrapper(0), wrapper(1)
|
|
@@ -266,29 +266,29 @@ class FitsData:
|
|
|
266
266
|
h = self.header
|
|
267
267
|
|
|
268
268
|
def gen_v(v_in: np.ndarray) -> None:
|
|
269
|
-
vaxis =
|
|
270
|
-
if h.get(f
|
|
269
|
+
vaxis = '2' if pv else '3'
|
|
270
|
+
if h.get(f'NAXIS{vaxis}') is None or v_in is None:
|
|
271
271
|
self.v, self.dv = None, None
|
|
272
272
|
return
|
|
273
273
|
|
|
274
274
|
if restfreq is None:
|
|
275
275
|
freq = np.mean(v_in)
|
|
276
|
-
print(
|
|
276
|
+
print('restfreq is assumed to be the center.')
|
|
277
277
|
else:
|
|
278
278
|
freq = restfreq
|
|
279
|
-
v = v_in + h[f
|
|
279
|
+
v = v_in + h[f'CRVAL{vaxis}']
|
|
280
280
|
key = f'CUNIT{vaxis}'
|
|
281
281
|
cunitv = h[key].strip()
|
|
282
282
|
match cunitv:
|
|
283
|
-
case
|
|
283
|
+
case 'Hz' | 'HZ':
|
|
284
284
|
if freq == 0:
|
|
285
|
-
print(
|
|
285
|
+
print('v is read as is, because restfreq=0.')
|
|
286
286
|
else:
|
|
287
287
|
v = (1 - v / freq) * cu.c_kms - vsys
|
|
288
|
-
case
|
|
288
|
+
case 'm/s' | 'M/S':
|
|
289
289
|
print(f'{key}={cunitv} found.')
|
|
290
290
|
v = v * 1e-3 - vsys
|
|
291
|
-
case 'km/s' |
|
|
291
|
+
case 'km/s' | 'KM/S':
|
|
292
292
|
print(f'{key}={cunitv} found.')
|
|
293
293
|
v = v - vsys
|
|
294
294
|
case _:
|
|
@@ -301,11 +301,11 @@ class FitsData:
|
|
|
301
301
|
|
|
302
302
|
def _get_array(self, i: int) -> np.ndarray:
|
|
303
303
|
h = self.header
|
|
304
|
-
n = h.get(f
|
|
304
|
+
n = h.get(f'NAXIS{i:d}')
|
|
305
305
|
if n is None:
|
|
306
306
|
return None
|
|
307
307
|
|
|
308
|
-
s = (np.arange(n) - h[f
|
|
308
|
+
s = (np.arange(n) - h[f'CRPIX{i:d}'] + 1) * h[f'CDELT{i:d}']
|
|
309
309
|
return s
|
|
310
310
|
|
|
311
311
|
def gen_grid(self, center: str | None = None, dist: float = 1.,
|
|
@@ -343,7 +343,7 @@ class FitsData:
|
|
|
343
343
|
Returns:
|
|
344
344
|
tuple: (x, y, v).
|
|
345
345
|
"""
|
|
346
|
-
if not np.all([hasattr(self, s) for s in [
|
|
346
|
+
if not np.all([hasattr(self, s) for s in ['x', 'y', 'v']]):
|
|
347
347
|
self.gen_grid(**kwargs)
|
|
348
348
|
return self.x, self.y, self.v
|
|
349
349
|
|
|
@@ -290,9 +290,9 @@ class EmceeCorner():
|
|
|
290
290
|
log (list, optional): Whether to search in the logarithmic space. The percentile is counted in the linear space regardless of this option. Defaults to False.
|
|
291
291
|
pcut (float, optional): Posterior is reset to be zero if it is below this cut off.
|
|
292
292
|
"""
|
|
293
|
-
if
|
|
293
|
+
if isinstance(ngrid, int):
|
|
294
294
|
ngrid = [ngrid] * self.dim
|
|
295
|
-
if
|
|
295
|
+
if isinstance(log, bool):
|
|
296
296
|
log = [log] * self.dim
|
|
297
297
|
pargrid = []
|
|
298
298
|
inzip = [self.bounds[:, 0], self.bounds[:, 1], ngrid, log]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import matplotlib.pyplot as plt
|
|
2
|
+
import numbers
|
|
2
3
|
import numpy as np
|
|
3
4
|
import warnings
|
|
4
5
|
from scipy.special import erf
|
|
@@ -12,7 +13,7 @@ def normalize(range: tuple = (-3.5, 3.5), bins: int = 100):
|
|
|
12
13
|
def decorator(f):
|
|
13
14
|
h = np.linspace(*range, bins + 1)
|
|
14
15
|
h = (h[1:] + h[:-1]) / 2
|
|
15
|
-
dh =
|
|
16
|
+
dh = h[1] - h[0]
|
|
16
17
|
|
|
17
18
|
def wrapper(x, *args):
|
|
18
19
|
area = np.sum(f(h, *args)) * dh
|
|
@@ -73,7 +74,7 @@ def select_noise(data: np.ndarray, sigma: str) -> np.ndarray:
|
|
|
73
74
|
Returns:
|
|
74
75
|
np.ndarray: 1D array that includes only the selected pixels.
|
|
75
76
|
"""
|
|
76
|
-
n = data * 1
|
|
77
|
+
n = np.array(data) * 1
|
|
77
78
|
if 'edge' in sigma:
|
|
78
79
|
if np.ndim(n) <= 2:
|
|
79
80
|
print('\'edge\' is ignored because ndim <= 2.')
|
|
@@ -194,8 +195,7 @@ def estimate_rms(data: np.ndarray,
|
|
|
194
195
|
Returns:
|
|
195
196
|
float: The estimated standard deviation of noise.
|
|
196
197
|
"""
|
|
197
|
-
|
|
198
|
-
if sigma is None or type(sigma) in nums:
|
|
198
|
+
if sigma is None or isinstance(sigma, numbers.Number):
|
|
199
199
|
return sigma
|
|
200
200
|
|
|
201
201
|
if np.ndim(np.squeeze(data)) == 0:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import matplotlib.pyplot as plt
|
|
2
|
+
import numbers
|
|
2
3
|
import numpy as np
|
|
3
4
|
import warnings
|
|
4
5
|
from scipy.interpolate import RegularGridInterpolator as RGI
|
|
@@ -10,10 +11,9 @@ def listing(*args) -> list:
|
|
|
10
11
|
Returns:
|
|
11
12
|
list: With a single non-list input, the output is a list like ['a'], rather than [['a']].
|
|
12
13
|
"""
|
|
13
|
-
strnum = [str, float, int, np.float64, np.int64, np.float32, np.int32]
|
|
14
14
|
b = [None] * len(args)
|
|
15
15
|
for i, a in enumerate(args):
|
|
16
|
-
b[i] = [a] if
|
|
16
|
+
b[i] = [a] if isinstance(a, (str, numbers.Number)) else a
|
|
17
17
|
if len(args) == 1:
|
|
18
18
|
b = b[0]
|
|
19
19
|
return b
|
|
@@ -28,7 +28,7 @@ def isdeg(s: str) -> bool:
|
|
|
28
28
|
Returns:
|
|
29
29
|
bool: Whether the given string means degree.
|
|
30
30
|
"""
|
|
31
|
-
if
|
|
31
|
+
if isinstance(s, str):
|
|
32
32
|
return s.strip() in ['deg', 'DEG', 'degree', 'DEGREE']
|
|
33
33
|
else:
|
|
34
34
|
return False
|
|
@@ -86,7 +86,7 @@ def trim(data: np.ndarray | None = None, x: np.ndarray | None = None,
|
|
|
86
86
|
d = np.squeeze(data)
|
|
87
87
|
|
|
88
88
|
if d.ndim == 0:
|
|
89
|
-
print(
|
|
89
|
+
print('data has only one pixel.')
|
|
90
90
|
return data, [xout, yout, vout]
|
|
91
91
|
|
|
92
92
|
if d.ndim == 2:
|
|
@@ -177,9 +177,13 @@ def _get_ch2nij(nrows: int = 1, ncols: int = 1) -> object:
|
|
|
177
177
|
return ch2nij
|
|
178
178
|
|
|
179
179
|
|
|
180
|
-
def _get_vskipfill(nv: float, v_org: np.ndarray, vskip: int
|
|
180
|
+
def _get_vskipfill(nv: float, v_org: np.ndarray, vskip: int,
|
|
181
|
+
channelnumber: int) -> object:
|
|
181
182
|
def vskipfill(c: np.ndarray, v_in: np.ndarray) -> np.ndarray:
|
|
182
|
-
|
|
183
|
+
c = reform_data(c=c, v_in=v_in, nv=nv, v_org=v_org, vskip=vskip)
|
|
184
|
+
if isinstance(channelnumber, int):
|
|
185
|
+
c = [c[channelnumber]]
|
|
186
|
+
return c
|
|
183
187
|
return vskipfill
|
|
184
188
|
|
|
185
189
|
|
|
@@ -203,7 +207,7 @@ class Stretcher():
|
|
|
203
207
|
sigma: float = 0
|
|
204
208
|
|
|
205
209
|
def __post_init__(self):
|
|
206
|
-
self.n = 1 if
|
|
210
|
+
self.n = 1 if isinstance(self.stretch, str) else len(self.stretch)
|
|
207
211
|
stretch = self.stretch
|
|
208
212
|
stsc = self.stretchscale
|
|
209
213
|
vmin = self.vmin
|
|
@@ -372,8 +376,8 @@ class PlotAxes2D():
|
|
|
372
376
|
if self.loglog is not None:
|
|
373
377
|
self.xscale = self.yscale = 'log'
|
|
374
378
|
self.samexy = True
|
|
375
|
-
for axis in [
|
|
376
|
-
attr = f
|
|
379
|
+
for axis in ['x', 'y']:
|
|
380
|
+
attr = f'{axis}lim'
|
|
377
381
|
lim = getattr(self, attr)
|
|
378
382
|
if lim is not None:
|
|
379
383
|
lim[0] = lim[1] / self.loglog
|
|
@@ -383,14 +387,14 @@ class PlotAxes2D():
|
|
|
383
387
|
|
|
384
388
|
def _init_ticks(self, axis):
|
|
385
389
|
ax = self.ax
|
|
386
|
-
ticks_attr = f
|
|
387
|
-
ticklabels_attr = f
|
|
388
|
-
scale = getattr(self, f
|
|
389
|
-
lim = getattr(self, f
|
|
390
|
+
ticks_attr = f'{axis}ticks'
|
|
391
|
+
ticklabels_attr = f'{axis}ticklabels'
|
|
392
|
+
scale = getattr(self, f'{axis}scale')
|
|
393
|
+
lim = getattr(self, f'{axis}lim')
|
|
390
394
|
ticks = getattr(self, ticks_attr)
|
|
391
395
|
if ticks is None:
|
|
392
|
-
ticks = getattr(ax, f
|
|
393
|
-
if scale ==
|
|
396
|
+
ticks = getattr(ax, f'get_{axis}ticks')()
|
|
397
|
+
if scale == 'log':
|
|
394
398
|
ticks, ticklabels = logticks(ticks, lim)
|
|
395
399
|
setattr(self, ticklabels_attr, ticklabels)
|
|
396
400
|
setattr(self, ticks_attr, ticks)
|
|
@@ -403,21 +407,21 @@ class PlotAxes2D():
|
|
|
403
407
|
|
|
404
408
|
def _set_ticks(self, axis):
|
|
405
409
|
ax = self.ax
|
|
406
|
-
attr = f
|
|
410
|
+
attr = f'{axis}ticks'
|
|
407
411
|
ticks = getattr(self, attr)
|
|
408
|
-
getattr(ax, f
|
|
409
|
-
ticksminor = getattr(self, f
|
|
412
|
+
getattr(ax, f'set_{attr}')(ticks)
|
|
413
|
+
ticksminor = getattr(self, f'{attr}minor')
|
|
410
414
|
if ticksminor is not None:
|
|
411
415
|
if isinstance(ticksminor, int):
|
|
412
416
|
ticksminor = self._make_ticks(ticks, ticksminor)
|
|
413
|
-
getattr(ax, f
|
|
417
|
+
getattr(ax, f'set_{attr}')(ticksminor, minor=True)
|
|
414
418
|
|
|
415
419
|
def _apply_if_not_none(self, axis, attr):
|
|
416
420
|
ax = self.ax
|
|
417
|
-
method = getattr(ax, f
|
|
418
|
-
value = getattr(self, f
|
|
421
|
+
method = getattr(ax, f'set_{axis}{attr}')
|
|
422
|
+
value = getattr(self, f'{axis}{attr}')
|
|
419
423
|
if value is not None:
|
|
420
|
-
if attr ==
|
|
424
|
+
if attr == 'lim':
|
|
421
425
|
method(*value)
|
|
422
426
|
else:
|
|
423
427
|
method(value)
|
|
@@ -429,10 +433,10 @@ class PlotAxes2D():
|
|
|
429
433
|
ax.set_xticks(ax.get_yticks())
|
|
430
434
|
ax.set_yticks(ax.get_xticks())
|
|
431
435
|
ax.set_aspect(1)
|
|
432
|
-
for axis in [
|
|
436
|
+
for axis in ['x', 'y']:
|
|
433
437
|
self._init_ticks(axis)
|
|
434
438
|
self._set_ticks(axis)
|
|
435
|
-
for attr in [
|
|
439
|
+
for attr in ['ticklabels', 'label', 'lim']:
|
|
436
440
|
self._apply_if_not_none(axis, attr)
|
|
437
441
|
if self.grid is not None:
|
|
438
442
|
ax.grid(**({} if self.grid is True else self.grid))
|
|
@@ -514,9 +518,10 @@ class PlotAstroData(AstroFrame):
|
|
|
514
518
|
super().__init__(**kwargs)
|
|
515
519
|
internalfig = fig is None
|
|
516
520
|
internalax = ax is None
|
|
521
|
+
animation = isinstance(channelnumber, int)
|
|
517
522
|
v = _get_v(p=self, v=v, restfreq=restfreq, vskip=vskip)
|
|
518
523
|
nv = len(v) # number of channels with a label
|
|
519
|
-
if self.pv or len(v) == 1 or
|
|
524
|
+
if self.pv or len(v) == 1 or animation:
|
|
520
525
|
nrows = ncols = npages = nchan = 1
|
|
521
526
|
else:
|
|
522
527
|
npages = int(np.ceil(nv / nrows / ncols))
|
|
@@ -532,7 +537,7 @@ class PlotAstroData(AstroFrame):
|
|
|
532
537
|
ymin=self.ymin, ymax=self.ymax,
|
|
533
538
|
figsize=figsize,
|
|
534
539
|
ncols=ncols, nrows=nrows, nchan=nchan)
|
|
535
|
-
need_vlabel = nchan > 1 or
|
|
540
|
+
need_vlabel = nchan > 1 or animation
|
|
536
541
|
for ch in range(nchan):
|
|
537
542
|
n, i, j = ch2nij(ch)
|
|
538
543
|
if internalfig and n not in plt.get_fignums():
|
|
@@ -557,8 +562,9 @@ class PlotAstroData(AstroFrame):
|
|
|
557
562
|
self.allchan = np.arange(nv)
|
|
558
563
|
self.bottomleft = nij2ch(np.arange(npages), nrows - 1, 0)
|
|
559
564
|
self.channelnumber = channelnumber
|
|
565
|
+
self.animation = animation
|
|
560
566
|
self.v = v
|
|
561
|
-
self.vskipfill = _get_vskipfill(nv
|
|
567
|
+
self.vskipfill = _get_vskipfill(nv, v, vskip, channelnumber)
|
|
562
568
|
|
|
563
569
|
def _map_init(self, kw: dict) -> tuple:
|
|
564
570
|
"""
|
|
@@ -587,6 +593,12 @@ class PlotAstroData(AstroFrame):
|
|
|
587
593
|
return (d.data, d.x, d.y, d.v, d.sigma, d.bunit,
|
|
588
594
|
self._kw, singlepix)
|
|
589
595
|
|
|
596
|
+
def _validchan(self, include_chan: list) -> list:
|
|
597
|
+
chans = self.allchan if include_chan is None else include_chan
|
|
598
|
+
if self.animation:
|
|
599
|
+
chans = [0] if self.channelnumber in include_chan else [1]
|
|
600
|
+
return chans
|
|
601
|
+
|
|
590
602
|
def add_region(self, patch: str = 'ellipse',
|
|
591
603
|
poslist: list[str | list[float, float]] = [],
|
|
592
604
|
majlist: list[float] = [], minlist: list[float] = [],
|
|
@@ -606,8 +618,6 @@ class PlotAstroData(AstroFrame):
|
|
|
606
618
|
_kw = {'facecolor': 'none', 'edgecolor': 'gray',
|
|
607
619
|
'linewidth': 1.5, 'zorder': 10}
|
|
608
620
|
_kw.update(kwargs)
|
|
609
|
-
if include_chan is None:
|
|
610
|
-
include_chan = self.allchan
|
|
611
621
|
if patch not in ['rectangle', 'ellipse']:
|
|
612
622
|
print('Only patch=\'rectangle\' or \'ellipse\' supported. ')
|
|
613
623
|
return
|
|
@@ -615,9 +625,7 @@ class PlotAstroData(AstroFrame):
|
|
|
615
625
|
z = listing(*self.pos2xy(poslist), minlist, majlist, palist)
|
|
616
626
|
for x, y, width, height, angle in zip(*z):
|
|
617
627
|
for ch, axnow in enumerate(self.ax):
|
|
618
|
-
if
|
|
619
|
-
ch = self.channelnumber
|
|
620
|
-
if ch not in include_chan:
|
|
628
|
+
if ch not in self._validchan(include_chan):
|
|
621
629
|
continue
|
|
622
630
|
if self.fig is None:
|
|
623
631
|
plt.figure(ch // self.rowcol)
|
|
@@ -643,12 +651,11 @@ class PlotAstroData(AstroFrame):
|
|
|
643
651
|
if not show_beam:
|
|
644
652
|
return
|
|
645
653
|
|
|
646
|
-
|
|
647
|
-
include_chan = self.allchan if animation else self.bottomleft
|
|
654
|
+
include_chan = self.allchan if self.animation else self.bottomleft
|
|
648
655
|
patch = 'rectangle' if self.pv else 'ellipse'
|
|
649
656
|
blist = [beam] if np.ndim(beam) == 1 else beam
|
|
650
657
|
n = len(blist)
|
|
651
|
-
bclist = beamcolor if
|
|
658
|
+
bclist = beamcolor if isinstance(beamcolor, list) else [beamcolor] * n
|
|
652
659
|
islist = beampos == [None] * 3 or np.ndim(beampos) == 2
|
|
653
660
|
bplist = beampos if islist else [beampos] * n
|
|
654
661
|
for (bmaj, bmin, bpa), bc, bp in zip(blist, bclist, bplist):
|
|
@@ -679,12 +686,8 @@ class PlotAstroData(AstroFrame):
|
|
|
679
686
|
_kw = {'marker': '+', 'ms': 10, 'mfc': 'gray',
|
|
680
687
|
'mec': 'gray', 'mew': 2, 'alpha': 1, 'zorder': 10}
|
|
681
688
|
_kw.update(kwargs)
|
|
682
|
-
if include_chan is None:
|
|
683
|
-
include_chan = self.allchan
|
|
684
689
|
for ch, axnow in enumerate(self.ax):
|
|
685
|
-
if
|
|
686
|
-
ch = self.channelnumber
|
|
687
|
-
if ch not in include_chan:
|
|
690
|
+
if ch not in self._validchan(include_chan):
|
|
688
691
|
continue
|
|
689
692
|
for x, y in zip(*self.pos2xy(poslist)):
|
|
690
693
|
axnow.plot(x, y, **_kw)
|
|
@@ -707,12 +710,8 @@ class PlotAstroData(AstroFrame):
|
|
|
707
710
|
if long in kwargs:
|
|
708
711
|
kwargs[short] = kwargs.pop(long)
|
|
709
712
|
_kw.update(kwargs)
|
|
710
|
-
if include_chan is None:
|
|
711
|
-
include_chan = self.allchan
|
|
712
713
|
for ch, axnow in enumerate(self.ax):
|
|
713
|
-
if
|
|
714
|
-
ch = self.channelnumber
|
|
715
|
-
if ch not in include_chan:
|
|
714
|
+
if ch not in self._validchan(include_chan):
|
|
716
715
|
continue
|
|
717
716
|
z = listing(*self.pos2xy(poslist), slist)
|
|
718
717
|
for x, y, s in zip(*z):
|
|
@@ -733,12 +732,8 @@ class PlotAstroData(AstroFrame):
|
|
|
733
732
|
_kw = {'color': 'gray', 'linewidth': 1.5,
|
|
734
733
|
'linestyle': '-', 'zorder': 10}
|
|
735
734
|
_kw.update(kwargs)
|
|
736
|
-
if include_chan is None:
|
|
737
|
-
include_chan = self.allchan
|
|
738
735
|
for ch, axnow in enumerate(self.ax):
|
|
739
|
-
if
|
|
740
|
-
ch = self.channelnumber
|
|
741
|
-
if ch not in include_chan:
|
|
736
|
+
if ch not in self._validchan(include_chan):
|
|
742
737
|
continue
|
|
743
738
|
alist = np.radians(anglelist)
|
|
744
739
|
z = listing(*self.pos2xy(poslist), alist, rlist)
|
|
@@ -761,12 +756,8 @@ class PlotAstroData(AstroFrame):
|
|
|
761
756
|
_kw = {'color': 'gray', 'width': 0.012,
|
|
762
757
|
'headwidth': 5, 'headlength': 5, 'zorder': 10}
|
|
763
758
|
_kw.update(kwargs)
|
|
764
|
-
if include_chan is None:
|
|
765
|
-
include_chan = self.allchan
|
|
766
759
|
for ch, axnow in enumerate(self.ax):
|
|
767
|
-
if
|
|
768
|
-
ch = self.channelnumber
|
|
769
|
-
if ch not in include_chan:
|
|
760
|
+
if ch not in self._validchan(include_chan):
|
|
770
761
|
continue
|
|
771
762
|
alist = np.radians(anglelist)
|
|
772
763
|
z = listing(*self.pos2xy(poslist), alist, rlist)
|
|
@@ -878,8 +869,6 @@ class PlotAstroData(AstroFrame):
|
|
|
878
869
|
_kw['vmin'] = cmin
|
|
879
870
|
_kw['vmax'] = cmax
|
|
880
871
|
c = self.vskipfill(c, v)
|
|
881
|
-
if type(self.channelnumber) is int:
|
|
882
|
-
c = [c[self.channelnumber]]
|
|
883
872
|
p = [None] * len(self.ax)
|
|
884
873
|
for ch, (axnow, cnow) in enumerate(zip(self.ax, c)):
|
|
885
874
|
pnow = axnow.pcolormesh(x, y, cnow, **_kw)
|
|
@@ -905,8 +894,6 @@ class PlotAstroData(AstroFrame):
|
|
|
905
894
|
return
|
|
906
895
|
|
|
907
896
|
c = self.vskipfill(c, v)
|
|
908
|
-
if type(self.channelnumber) is int:
|
|
909
|
-
c = [c[self.channelnumber]]
|
|
910
897
|
for axnow, cnow in zip(self.ax, c):
|
|
911
898
|
axnow.contour(x, y, cnow, np.sort(levels) * sigma, **_kw)
|
|
912
899
|
|
|
@@ -963,9 +950,6 @@ class PlotAstroData(AstroFrame):
|
|
|
963
950
|
V = ampfactor * amp * np.cos(np.radians(ang + rotation))
|
|
964
951
|
U = self.vskipfill(U, v)
|
|
965
952
|
V = self.vskipfill(V, v)
|
|
966
|
-
if type(self.channelnumber) is int:
|
|
967
|
-
U = [U[self.channelnumber]]
|
|
968
|
-
V = [V[self.channelnumber]]
|
|
969
953
|
_kw['scale'] = 1. / np.abs(x[1] - x[0])
|
|
970
954
|
for axnow, unow, vnow in zip(self.ax, U, V):
|
|
971
955
|
axnow.quiver(x, y, unow, vnow, **_kw)
|
|
@@ -1027,13 +1011,13 @@ class PlotAstroData(AstroFrame):
|
|
|
1027
1011
|
if title is not None:
|
|
1028
1012
|
if len(self.ax) > 1:
|
|
1029
1013
|
t = {'y': 0.9}
|
|
1030
|
-
t_in = {'t': title} if
|
|
1014
|
+
t_in = {'t': title} if isinstance(title, str) else title
|
|
1031
1015
|
t.update(t_in)
|
|
1032
1016
|
for i in range(self.npages):
|
|
1033
1017
|
fig = plt.figure(i)
|
|
1034
1018
|
fig.suptitle(**t)
|
|
1035
1019
|
else:
|
|
1036
|
-
t = {'label': title} if
|
|
1020
|
+
t = {'label': title} if isinstance(title, str) else title
|
|
1037
1021
|
axnow.set_title(**t)
|
|
1038
1022
|
|
|
1039
1023
|
def set_axis(self, title: dict | str | None = None, **kwargs) -> None:
|
|
@@ -1173,7 +1157,7 @@ class PlotAstroData(AstroFrame):
|
|
|
1173
1157
|
for axnow in self.ax:
|
|
1174
1158
|
axnow.set_xlim(*self.Xlim)
|
|
1175
1159
|
axnow.set_ylim(*self.Ylim)
|
|
1176
|
-
if
|
|
1160
|
+
if isinstance(filename, str):
|
|
1177
1161
|
ext = filename.split('.')[-1]
|
|
1178
1162
|
for i in range(self.npages):
|
|
1179
1163
|
ver = '' if self.npages == 1 else f'_{i:d}'
|
|
@@ -1202,12 +1186,12 @@ class PlotAstroData(AstroFrame):
|
|
|
1202
1186
|
|
|
1203
1187
|
def _get_ylabel_profile(_kw: dict, Tb: bool, flux: bool, bunit: str
|
|
1204
1188
|
) -> str:
|
|
1205
|
-
if
|
|
1206
|
-
return _kw[
|
|
1189
|
+
if 'ylabel' in _kw:
|
|
1190
|
+
return _kw['ylabel']
|
|
1207
1191
|
if Tb:
|
|
1208
|
-
return r
|
|
1192
|
+
return r'$T_b$ (K)'
|
|
1209
1193
|
if flux:
|
|
1210
|
-
return
|
|
1194
|
+
return 'Flux (Jy)'
|
|
1211
1195
|
return bunit
|
|
1212
1196
|
|
|
1213
1197
|
|
|
@@ -1217,7 +1201,7 @@ def _prep_plotprofile(width: int, coords: list | str,
|
|
|
1217
1201
|
_kw: dict) -> tuple:
|
|
1218
1202
|
if isinstance(coords, str):
|
|
1219
1203
|
coords = [coords]
|
|
1220
|
-
Tb = _kw.get(
|
|
1204
|
+
Tb = _kw.get('Tb', False)
|
|
1221
1205
|
f = kwargs2instance(AstroFrame, _kw)
|
|
1222
1206
|
d = kwargs2instance(AstroData, _kw)
|
|
1223
1207
|
f.read(d)
|
|
@@ -1228,7 +1212,7 @@ def _prep_plotprofile(width: int, coords: list | str,
|
|
|
1228
1212
|
ylabel = _get_ylabel_profile(_kw, Tb, flux, d.bunit)
|
|
1229
1213
|
if isinstance(ylabel, str):
|
|
1230
1214
|
ylabel = [ylabel] * len(prof)
|
|
1231
|
-
_kw.setdefault(
|
|
1215
|
+
_kw.setdefault('xlim', [v.min(), v.max()])
|
|
1232
1216
|
pa2 = kwargs2instance(PlotAxes2D, _kw)
|
|
1233
1217
|
return v, prof, gfitres, pa2, ylabel
|
|
1234
1218
|
|
|
@@ -1240,7 +1224,7 @@ def _set_figax_plotprofile(fig, ax, nrows: int, ncols: int,
|
|
|
1240
1224
|
if fig is None:
|
|
1241
1225
|
fig = plt.figure(figsize=(6 * ncols, 3 * nrows))
|
|
1242
1226
|
if nprof > 1 and ax is not None:
|
|
1243
|
-
print(
|
|
1227
|
+
print('External ax is supported only when len(coords)=1.')
|
|
1244
1228
|
ax = None
|
|
1245
1229
|
ax = np.empty(nprof, dtype=object) if ax is None else [ax]
|
|
1246
1230
|
for i in range(nprof):
|
|
@@ -1287,8 +1271,8 @@ def plotprofile(coords: list[str] | str = [],
|
|
|
1287
1271
|
Returns:
|
|
1288
1272
|
tuple: (fig, ax), where ax is a list, if getfigax=True. Otherwise, no return.
|
|
1289
1273
|
"""
|
|
1290
|
-
_kw = {
|
|
1291
|
-
|
|
1274
|
+
_kw = {'drawstyle': 'steps-mid', 'color': 'k',
|
|
1275
|
+
'xlabel': r'Velocity (km s$^{-1}$)', 'samexy': False}
|
|
1292
1276
|
_kw.update(kwargs)
|
|
1293
1277
|
_kwgauss = {'drawstyle': 'default', 'color': 'g'}
|
|
1294
1278
|
_kwgauss.update(gauss_kwargs)
|
|
@@ -1296,20 +1280,20 @@ def plotprofile(coords: list[str] | str = [],
|
|
|
1296
1280
|
= _prep_plotprofile(width, coords, xlist, ylist, ellipse,
|
|
1297
1281
|
ninterp, flux, gaussfit, _kw)
|
|
1298
1282
|
nprof = len(prof)
|
|
1299
|
-
set_rcparams(20,
|
|
1283
|
+
set_rcparams(20, 'w')
|
|
1300
1284
|
fig, ax = _set_figax_plotprofile(fig, ax, nrows, ncols, nprof)
|
|
1301
1285
|
for i in range(nprof):
|
|
1302
1286
|
if gaussfit:
|
|
1303
|
-
ax[i].plot(v, gaussian1d(v, *gfitres[
|
|
1287
|
+
ax[i].plot(v, gaussian1d(v, *gfitres['best'][i]), **_kwgauss)
|
|
1304
1288
|
ax[i].plot(v, prof[i], **_kw)
|
|
1305
|
-
ax[i].hlines([0], v.min(), v.max(), linestyle=
|
|
1289
|
+
ax[i].hlines([0], v.min(), v.max(), linestyle='dashed', color='k')
|
|
1306
1290
|
ax[i].set_ylabel(ylabel[i])
|
|
1307
1291
|
pa2.set_xyaxes(ax[i])
|
|
1308
1292
|
if text is not None:
|
|
1309
1293
|
ax[i].text(**text[i])
|
|
1310
1294
|
if title is not None:
|
|
1311
1295
|
if isinstance(title[i], str):
|
|
1312
|
-
title[i] = {
|
|
1296
|
+
title[i] = {'label': title[i]}
|
|
1313
1297
|
ax[i].set_title(**title[i])
|
|
1314
1298
|
if i <= nprof - ncols - 1:
|
|
1315
1299
|
plt.setp(ax[i].get_xticklabels(), visible=False)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plotastrodata
|
|
3
|
-
Version: 1.9.
|
|
3
|
+
Version: 1.9.10
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|