plotastrodata 1.9.8__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.8/plotastrodata.egg-info → plotastrodata-1.9.10}/PKG-INFO +1 -1
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata/__init__.py +1 -1
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata/analysis_utils.py +22 -22
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata/coord_utils.py +4 -4
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata/fits_utils.py +19 -19
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata/fitting_utils.py +2 -2
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata/noise_utils.py +4 -4
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata/other_utils.py +4 -4
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata/plot_utils.py +168 -165
- {plotastrodata-1.9.8 → plotastrodata-1.9.10/plotastrodata.egg-info}/PKG-INFO +1 -1
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/LICENSE +0 -0
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/MANIFEST.in +0 -0
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/README.md +0 -0
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata/const_utils.py +0 -0
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata/ext_utils.py +0 -0
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata/fft_utils.py +0 -0
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata/los_utils.py +0 -0
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata/matrix_utils.py +0 -0
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata.egg-info/SOURCES.txt +0 -0
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata.egg-info/dependency_links.txt +0 -0
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata.egg-info/not-zip-safe +0 -0
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata.egg-info/requires.txt +0 -0
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/plotastrodata.egg-info/top_level.txt +0 -0
- {plotastrodata-1.9.8 → plotastrodata-1.9.10}/setup.cfg +0 -0
- {plotastrodata-1.9.8 → 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:
|
|
@@ -8,6 +8,7 @@ from typing import TypeVar
|
|
|
8
8
|
from plotastrodata.analysis_utils import AstroData, AstroFrame
|
|
9
9
|
from plotastrodata.coord_utils import (coord2xy, xy2coord,
|
|
10
10
|
get_hmdm, get_min, get_sec)
|
|
11
|
+
from plotastrodata.fitting_utils import gaussian1d
|
|
11
12
|
from plotastrodata.noise_utils import estimate_rms
|
|
12
13
|
from plotastrodata.other_utils import (close_figure, listing,
|
|
13
14
|
reform_grid, reform_data)
|
|
@@ -176,9 +177,13 @@ def _get_ch2nij(nrows: int = 1, ncols: int = 1) -> object:
|
|
|
176
177
|
return ch2nij
|
|
177
178
|
|
|
178
179
|
|
|
179
|
-
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:
|
|
180
182
|
def vskipfill(c: np.ndarray, v_in: np.ndarray) -> np.ndarray:
|
|
181
|
-
|
|
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
|
|
182
187
|
return vskipfill
|
|
183
188
|
|
|
184
189
|
|
|
@@ -202,7 +207,7 @@ class Stretcher():
|
|
|
202
207
|
sigma: float = 0
|
|
203
208
|
|
|
204
209
|
def __post_init__(self):
|
|
205
|
-
self.n = 1 if
|
|
210
|
+
self.n = 1 if isinstance(self.stretch, str) else len(self.stretch)
|
|
206
211
|
stretch = self.stretch
|
|
207
212
|
stsc = self.stretchscale
|
|
208
213
|
vmin = self.vmin
|
|
@@ -371,8 +376,8 @@ class PlotAxes2D():
|
|
|
371
376
|
if self.loglog is not None:
|
|
372
377
|
self.xscale = self.yscale = 'log'
|
|
373
378
|
self.samexy = True
|
|
374
|
-
for axis in [
|
|
375
|
-
attr = f
|
|
379
|
+
for axis in ['x', 'y']:
|
|
380
|
+
attr = f'{axis}lim'
|
|
376
381
|
lim = getattr(self, attr)
|
|
377
382
|
if lim is not None:
|
|
378
383
|
lim[0] = lim[1] / self.loglog
|
|
@@ -382,14 +387,14 @@ class PlotAxes2D():
|
|
|
382
387
|
|
|
383
388
|
def _init_ticks(self, axis):
|
|
384
389
|
ax = self.ax
|
|
385
|
-
ticks_attr = f
|
|
386
|
-
ticklabels_attr = f
|
|
387
|
-
scale = getattr(self, f
|
|
388
|
-
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')
|
|
389
394
|
ticks = getattr(self, ticks_attr)
|
|
390
395
|
if ticks is None:
|
|
391
|
-
ticks = getattr(ax, f
|
|
392
|
-
if scale ==
|
|
396
|
+
ticks = getattr(ax, f'get_{axis}ticks')()
|
|
397
|
+
if scale == 'log':
|
|
393
398
|
ticks, ticklabels = logticks(ticks, lim)
|
|
394
399
|
setattr(self, ticklabels_attr, ticklabels)
|
|
395
400
|
setattr(self, ticks_attr, ticks)
|
|
@@ -402,21 +407,21 @@ class PlotAxes2D():
|
|
|
402
407
|
|
|
403
408
|
def _set_ticks(self, axis):
|
|
404
409
|
ax = self.ax
|
|
405
|
-
attr = f
|
|
410
|
+
attr = f'{axis}ticks'
|
|
406
411
|
ticks = getattr(self, attr)
|
|
407
|
-
getattr(ax, f
|
|
408
|
-
ticksminor = getattr(self, f
|
|
412
|
+
getattr(ax, f'set_{attr}')(ticks)
|
|
413
|
+
ticksminor = getattr(self, f'{attr}minor')
|
|
409
414
|
if ticksminor is not None:
|
|
410
415
|
if isinstance(ticksminor, int):
|
|
411
416
|
ticksminor = self._make_ticks(ticks, ticksminor)
|
|
412
|
-
getattr(ax, f
|
|
417
|
+
getattr(ax, f'set_{attr}')(ticksminor, minor=True)
|
|
413
418
|
|
|
414
419
|
def _apply_if_not_none(self, axis, attr):
|
|
415
420
|
ax = self.ax
|
|
416
|
-
method = getattr(ax, f
|
|
417
|
-
value = getattr(self, f
|
|
421
|
+
method = getattr(ax, f'set_{axis}{attr}')
|
|
422
|
+
value = getattr(self, f'{axis}{attr}')
|
|
418
423
|
if value is not None:
|
|
419
|
-
if attr ==
|
|
424
|
+
if attr == 'lim':
|
|
420
425
|
method(*value)
|
|
421
426
|
else:
|
|
422
427
|
method(value)
|
|
@@ -428,10 +433,10 @@ class PlotAxes2D():
|
|
|
428
433
|
ax.set_xticks(ax.get_yticks())
|
|
429
434
|
ax.set_yticks(ax.get_xticks())
|
|
430
435
|
ax.set_aspect(1)
|
|
431
|
-
for axis in [
|
|
436
|
+
for axis in ['x', 'y']:
|
|
432
437
|
self._init_ticks(axis)
|
|
433
438
|
self._set_ticks(axis)
|
|
434
|
-
for attr in [
|
|
439
|
+
for attr in ['ticklabels', 'label', 'lim']:
|
|
435
440
|
self._apply_if_not_none(axis, attr)
|
|
436
441
|
if self.grid is not None:
|
|
437
442
|
ax.grid(**({} if self.grid is True else self.grid))
|
|
@@ -513,9 +518,10 @@ class PlotAstroData(AstroFrame):
|
|
|
513
518
|
super().__init__(**kwargs)
|
|
514
519
|
internalfig = fig is None
|
|
515
520
|
internalax = ax is None
|
|
521
|
+
animation = isinstance(channelnumber, int)
|
|
516
522
|
v = _get_v(p=self, v=v, restfreq=restfreq, vskip=vskip)
|
|
517
523
|
nv = len(v) # number of channels with a label
|
|
518
|
-
if self.pv or len(v) == 1 or
|
|
524
|
+
if self.pv or len(v) == 1 or animation:
|
|
519
525
|
nrows = ncols = npages = nchan = 1
|
|
520
526
|
else:
|
|
521
527
|
npages = int(np.ceil(nv / nrows / ncols))
|
|
@@ -531,7 +537,7 @@ class PlotAstroData(AstroFrame):
|
|
|
531
537
|
ymin=self.ymin, ymax=self.ymax,
|
|
532
538
|
figsize=figsize,
|
|
533
539
|
ncols=ncols, nrows=nrows, nchan=nchan)
|
|
534
|
-
need_vlabel = nchan > 1 or
|
|
540
|
+
need_vlabel = nchan > 1 or animation
|
|
535
541
|
for ch in range(nchan):
|
|
536
542
|
n, i, j = ch2nij(ch)
|
|
537
543
|
if internalfig and n not in plt.get_fignums():
|
|
@@ -556,8 +562,9 @@ class PlotAstroData(AstroFrame):
|
|
|
556
562
|
self.allchan = np.arange(nv)
|
|
557
563
|
self.bottomleft = nij2ch(np.arange(npages), nrows - 1, 0)
|
|
558
564
|
self.channelnumber = channelnumber
|
|
565
|
+
self.animation = animation
|
|
559
566
|
self.v = v
|
|
560
|
-
self.vskipfill = _get_vskipfill(nv
|
|
567
|
+
self.vskipfill = _get_vskipfill(nv, v, vskip, channelnumber)
|
|
561
568
|
|
|
562
569
|
def _map_init(self, kw: dict) -> tuple:
|
|
563
570
|
"""
|
|
@@ -586,6 +593,12 @@ class PlotAstroData(AstroFrame):
|
|
|
586
593
|
return (d.data, d.x, d.y, d.v, d.sigma, d.bunit,
|
|
587
594
|
self._kw, singlepix)
|
|
588
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
|
+
|
|
589
602
|
def add_region(self, patch: str = 'ellipse',
|
|
590
603
|
poslist: list[str | list[float, float]] = [],
|
|
591
604
|
majlist: list[float] = [], minlist: list[float] = [],
|
|
@@ -605,8 +618,6 @@ class PlotAstroData(AstroFrame):
|
|
|
605
618
|
_kw = {'facecolor': 'none', 'edgecolor': 'gray',
|
|
606
619
|
'linewidth': 1.5, 'zorder': 10}
|
|
607
620
|
_kw.update(kwargs)
|
|
608
|
-
if include_chan is None:
|
|
609
|
-
include_chan = self.allchan
|
|
610
621
|
if patch not in ['rectangle', 'ellipse']:
|
|
611
622
|
print('Only patch=\'rectangle\' or \'ellipse\' supported. ')
|
|
612
623
|
return
|
|
@@ -614,9 +625,7 @@ class PlotAstroData(AstroFrame):
|
|
|
614
625
|
z = listing(*self.pos2xy(poslist), minlist, majlist, palist)
|
|
615
626
|
for x, y, width, height, angle in zip(*z):
|
|
616
627
|
for ch, axnow in enumerate(self.ax):
|
|
617
|
-
if
|
|
618
|
-
ch = self.channelnumber
|
|
619
|
-
if ch not in include_chan:
|
|
628
|
+
if ch not in self._validchan(include_chan):
|
|
620
629
|
continue
|
|
621
630
|
if self.fig is None:
|
|
622
631
|
plt.figure(ch // self.rowcol)
|
|
@@ -642,12 +651,11 @@ class PlotAstroData(AstroFrame):
|
|
|
642
651
|
if not show_beam:
|
|
643
652
|
return
|
|
644
653
|
|
|
645
|
-
|
|
646
|
-
include_chan = self.allchan if animation else self.bottomleft
|
|
654
|
+
include_chan = self.allchan if self.animation else self.bottomleft
|
|
647
655
|
patch = 'rectangle' if self.pv else 'ellipse'
|
|
648
656
|
blist = [beam] if np.ndim(beam) == 1 else beam
|
|
649
657
|
n = len(blist)
|
|
650
|
-
bclist = beamcolor if
|
|
658
|
+
bclist = beamcolor if isinstance(beamcolor, list) else [beamcolor] * n
|
|
651
659
|
islist = beampos == [None] * 3 or np.ndim(beampos) == 2
|
|
652
660
|
bplist = beampos if islist else [beampos] * n
|
|
653
661
|
for (bmaj, bmin, bpa), bc, bp in zip(blist, bclist, bplist):
|
|
@@ -678,12 +686,8 @@ class PlotAstroData(AstroFrame):
|
|
|
678
686
|
_kw = {'marker': '+', 'ms': 10, 'mfc': 'gray',
|
|
679
687
|
'mec': 'gray', 'mew': 2, 'alpha': 1, 'zorder': 10}
|
|
680
688
|
_kw.update(kwargs)
|
|
681
|
-
if include_chan is None:
|
|
682
|
-
include_chan = self.allchan
|
|
683
689
|
for ch, axnow in enumerate(self.ax):
|
|
684
|
-
if
|
|
685
|
-
ch = self.channelnumber
|
|
686
|
-
if ch not in include_chan:
|
|
690
|
+
if ch not in self._validchan(include_chan):
|
|
687
691
|
continue
|
|
688
692
|
for x, y in zip(*self.pos2xy(poslist)):
|
|
689
693
|
axnow.plot(x, y, **_kw)
|
|
@@ -706,12 +710,8 @@ class PlotAstroData(AstroFrame):
|
|
|
706
710
|
if long in kwargs:
|
|
707
711
|
kwargs[short] = kwargs.pop(long)
|
|
708
712
|
_kw.update(kwargs)
|
|
709
|
-
if include_chan is None:
|
|
710
|
-
include_chan = self.allchan
|
|
711
713
|
for ch, axnow in enumerate(self.ax):
|
|
712
|
-
if
|
|
713
|
-
ch = self.channelnumber
|
|
714
|
-
if ch not in include_chan:
|
|
714
|
+
if ch not in self._validchan(include_chan):
|
|
715
715
|
continue
|
|
716
716
|
z = listing(*self.pos2xy(poslist), slist)
|
|
717
717
|
for x, y, s in zip(*z):
|
|
@@ -732,12 +732,8 @@ class PlotAstroData(AstroFrame):
|
|
|
732
732
|
_kw = {'color': 'gray', 'linewidth': 1.5,
|
|
733
733
|
'linestyle': '-', 'zorder': 10}
|
|
734
734
|
_kw.update(kwargs)
|
|
735
|
-
if include_chan is None:
|
|
736
|
-
include_chan = self.allchan
|
|
737
735
|
for ch, axnow in enumerate(self.ax):
|
|
738
|
-
if
|
|
739
|
-
ch = self.channelnumber
|
|
740
|
-
if ch not in include_chan:
|
|
736
|
+
if ch not in self._validchan(include_chan):
|
|
741
737
|
continue
|
|
742
738
|
alist = np.radians(anglelist)
|
|
743
739
|
z = listing(*self.pos2xy(poslist), alist, rlist)
|
|
@@ -760,12 +756,8 @@ class PlotAstroData(AstroFrame):
|
|
|
760
756
|
_kw = {'color': 'gray', 'width': 0.012,
|
|
761
757
|
'headwidth': 5, 'headlength': 5, 'zorder': 10}
|
|
762
758
|
_kw.update(kwargs)
|
|
763
|
-
if include_chan is None:
|
|
764
|
-
include_chan = self.allchan
|
|
765
759
|
for ch, axnow in enumerate(self.ax):
|
|
766
|
-
if
|
|
767
|
-
ch = self.channelnumber
|
|
768
|
-
if ch not in include_chan:
|
|
760
|
+
if ch not in self._validchan(include_chan):
|
|
769
761
|
continue
|
|
770
762
|
alist = np.radians(anglelist)
|
|
771
763
|
z = listing(*self.pos2xy(poslist), alist, rlist)
|
|
@@ -877,8 +869,6 @@ class PlotAstroData(AstroFrame):
|
|
|
877
869
|
_kw['vmin'] = cmin
|
|
878
870
|
_kw['vmax'] = cmax
|
|
879
871
|
c = self.vskipfill(c, v)
|
|
880
|
-
if type(self.channelnumber) is int:
|
|
881
|
-
c = [c[self.channelnumber]]
|
|
882
872
|
p = [None] * len(self.ax)
|
|
883
873
|
for ch, (axnow, cnow) in enumerate(zip(self.ax, c)):
|
|
884
874
|
pnow = axnow.pcolormesh(x, y, cnow, **_kw)
|
|
@@ -904,8 +894,6 @@ class PlotAstroData(AstroFrame):
|
|
|
904
894
|
return
|
|
905
895
|
|
|
906
896
|
c = self.vskipfill(c, v)
|
|
907
|
-
if type(self.channelnumber) is int:
|
|
908
|
-
c = [c[self.channelnumber]]
|
|
909
897
|
for axnow, cnow in zip(self.ax, c):
|
|
910
898
|
axnow.contour(x, y, cnow, np.sort(levels) * sigma, **_kw)
|
|
911
899
|
|
|
@@ -962,9 +950,6 @@ class PlotAstroData(AstroFrame):
|
|
|
962
950
|
V = ampfactor * amp * np.cos(np.radians(ang + rotation))
|
|
963
951
|
U = self.vskipfill(U, v)
|
|
964
952
|
V = self.vskipfill(V, v)
|
|
965
|
-
if type(self.channelnumber) is int:
|
|
966
|
-
U = [U[self.channelnumber]]
|
|
967
|
-
V = [V[self.channelnumber]]
|
|
968
953
|
_kw['scale'] = 1. / np.abs(x[1] - x[0])
|
|
969
954
|
for axnow, unow, vnow in zip(self.ax, U, V):
|
|
970
955
|
axnow.quiver(x, y, unow, vnow, **_kw)
|
|
@@ -1026,13 +1011,13 @@ class PlotAstroData(AstroFrame):
|
|
|
1026
1011
|
if title is not None:
|
|
1027
1012
|
if len(self.ax) > 1:
|
|
1028
1013
|
t = {'y': 0.9}
|
|
1029
|
-
t_in = {'t': title} if
|
|
1014
|
+
t_in = {'t': title} if isinstance(title, str) else title
|
|
1030
1015
|
t.update(t_in)
|
|
1031
1016
|
for i in range(self.npages):
|
|
1032
1017
|
fig = plt.figure(i)
|
|
1033
1018
|
fig.suptitle(**t)
|
|
1034
1019
|
else:
|
|
1035
|
-
t = {'label': title} if
|
|
1020
|
+
t = {'label': title} if isinstance(title, str) else title
|
|
1036
1021
|
axnow.set_title(**t)
|
|
1037
1022
|
|
|
1038
1023
|
def set_axis(self, title: dict | str | None = None, **kwargs) -> None:
|
|
@@ -1172,7 +1157,7 @@ class PlotAstroData(AstroFrame):
|
|
|
1172
1157
|
for axnow in self.ax:
|
|
1173
1158
|
axnow.set_xlim(*self.Xlim)
|
|
1174
1159
|
axnow.set_ylim(*self.Ylim)
|
|
1175
|
-
if
|
|
1160
|
+
if isinstance(filename, str):
|
|
1176
1161
|
ext = filename.split('.')[-1]
|
|
1177
1162
|
for i in range(self.npages):
|
|
1178
1163
|
ver = '' if self.npages == 1 else f'_{i:d}'
|
|
@@ -1199,6 +1184,55 @@ class PlotAstroData(AstroFrame):
|
|
|
1199
1184
|
return fig, self.ax[0]
|
|
1200
1185
|
|
|
1201
1186
|
|
|
1187
|
+
def _get_ylabel_profile(_kw: dict, Tb: bool, flux: bool, bunit: str
|
|
1188
|
+
) -> str:
|
|
1189
|
+
if 'ylabel' in _kw:
|
|
1190
|
+
return _kw['ylabel']
|
|
1191
|
+
if Tb:
|
|
1192
|
+
return r'$T_b$ (K)'
|
|
1193
|
+
if flux:
|
|
1194
|
+
return 'Flux (Jy)'
|
|
1195
|
+
return bunit
|
|
1196
|
+
|
|
1197
|
+
|
|
1198
|
+
def _prep_plotprofile(width: int, coords: list | str,
|
|
1199
|
+
xlist: list, ylist: list, ellipse: list,
|
|
1200
|
+
ninterp: int, flux: bool, gaussfit: bool,
|
|
1201
|
+
_kw: dict) -> tuple:
|
|
1202
|
+
if isinstance(coords, str):
|
|
1203
|
+
coords = [coords]
|
|
1204
|
+
Tb = _kw.get('Tb', False)
|
|
1205
|
+
f = kwargs2instance(AstroFrame, _kw)
|
|
1206
|
+
d = kwargs2instance(AstroData, _kw)
|
|
1207
|
+
f.read(d)
|
|
1208
|
+
d.binning([width, 1, 1])
|
|
1209
|
+
v, prof, gfitres = d.profile(coords=coords, xlist=xlist, ylist=ylist,
|
|
1210
|
+
ellipse=ellipse, ninterp=ninterp,
|
|
1211
|
+
flux=flux, gaussfit=gaussfit)
|
|
1212
|
+
ylabel = _get_ylabel_profile(_kw, Tb, flux, d.bunit)
|
|
1213
|
+
if isinstance(ylabel, str):
|
|
1214
|
+
ylabel = [ylabel] * len(prof)
|
|
1215
|
+
_kw.setdefault('xlim', [v.min(), v.max()])
|
|
1216
|
+
pa2 = kwargs2instance(PlotAxes2D, _kw)
|
|
1217
|
+
return v, prof, gfitres, pa2, ylabel
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
def _set_figax_plotprofile(fig, ax, nrows: int, ncols: int,
|
|
1221
|
+
nprof: int) -> tuple:
|
|
1222
|
+
if ncols == 1:
|
|
1223
|
+
nrows = nprof
|
|
1224
|
+
if fig is None:
|
|
1225
|
+
fig = plt.figure(figsize=(6 * ncols, 3 * nrows))
|
|
1226
|
+
if nprof > 1 and ax is not None:
|
|
1227
|
+
print('External ax is supported only when len(coords)=1.')
|
|
1228
|
+
ax = None
|
|
1229
|
+
ax = np.empty(nprof, dtype=object) if ax is None else [ax]
|
|
1230
|
+
for i in range(nprof):
|
|
1231
|
+
sharex = None if i < nrows - 1 else ax[i - 1]
|
|
1232
|
+
ax[i] = fig.add_subplot(nrows, ncols, i + 1, sharex=sharex)
|
|
1233
|
+
return fig, ax
|
|
1234
|
+
|
|
1235
|
+
|
|
1202
1236
|
def plotprofile(coords: list[str] | str = [],
|
|
1203
1237
|
xlist: list[float] = [], ylist: list[float] = [],
|
|
1204
1238
|
ellipse: list[float, float, float] | None = None,
|
|
@@ -1208,11 +1242,9 @@ def plotprofile(coords: list[str] | str = [],
|
|
|
1208
1242
|
title: list[str] | None = None,
|
|
1209
1243
|
text: list[str] | None = None,
|
|
1210
1244
|
nrows: int = 0, ncols: int = 1,
|
|
1211
|
-
fig: object | None = None,
|
|
1212
|
-
ax: object | None = None,
|
|
1245
|
+
fig: object | None = None, ax: object | None = None,
|
|
1213
1246
|
getfigax: bool = False,
|
|
1214
|
-
savefig: dict | str | None = None,
|
|
1215
|
-
show: bool = False,
|
|
1247
|
+
savefig: dict | str | None = None, show: bool = False,
|
|
1216
1248
|
**kwargs) -> tuple[object, object]:
|
|
1217
1249
|
"""Use Axes.plot of matplotlib to plot line profiles at given coordinates. kwargs must include the arguments of AstroData to specify the data to be plotted. kwargs must include the arguments of AstroFrame to specify the ranges and so on for plotting. kwargs can include the arguments of PlotAxes2D to adjust x and y axes.
|
|
1218
1250
|
|
|
@@ -1239,54 +1271,20 @@ def plotprofile(coords: list[str] | str = [],
|
|
|
1239
1271
|
Returns:
|
|
1240
1272
|
tuple: (fig, ax), where ax is a list, if getfigax=True. Otherwise, no return.
|
|
1241
1273
|
"""
|
|
1242
|
-
_kw = {'drawstyle': 'steps-mid', 'color': 'k'
|
|
1274
|
+
_kw = {'drawstyle': 'steps-mid', 'color': 'k',
|
|
1275
|
+
'xlabel': r'Velocity (km s$^{-1}$)', 'samexy': False}
|
|
1243
1276
|
_kw.update(kwargs)
|
|
1244
1277
|
_kwgauss = {'drawstyle': 'default', 'color': 'g'}
|
|
1245
1278
|
_kwgauss.update(gauss_kwargs)
|
|
1246
|
-
|
|
1247
|
-
coords
|
|
1248
|
-
|
|
1249
|
-
d = kwargs2instance(AstroData, _kw)
|
|
1250
|
-
f.read(d)
|
|
1251
|
-
d.binning([width, 1, 1])
|
|
1252
|
-
v, prof, gfitres = d.profile(coords=coords, xlist=xlist, ylist=ylist,
|
|
1253
|
-
ellipse=ellipse, ninterp=ninterp,
|
|
1254
|
-
flux=flux, gaussfit=gaussfit)
|
|
1279
|
+
v, prof, gfitres, pa2, ylabel \
|
|
1280
|
+
= _prep_plotprofile(width, coords, xlist, ylist, ellipse,
|
|
1281
|
+
ninterp, flux, gaussfit, _kw)
|
|
1255
1282
|
nprof = len(prof)
|
|
1256
|
-
if 'ylabel' in _kw:
|
|
1257
|
-
ylabel = _kw['ylabel']
|
|
1258
|
-
elif d.Tb:
|
|
1259
|
-
ylabel = r'$T_b$ (K)'
|
|
1260
|
-
elif flux:
|
|
1261
|
-
ylabel = 'Flux (Jy)'
|
|
1262
|
-
else:
|
|
1263
|
-
ylabel = d.bunit
|
|
1264
|
-
if type(ylabel) is str:
|
|
1265
|
-
ylabel = [ylabel] * nprof
|
|
1266
|
-
|
|
1267
|
-
def gauss(x, p, c, w):
|
|
1268
|
-
return p * np.exp(-4. * np.log(2.) * ((x - c) / w)**2)
|
|
1269
|
-
|
|
1270
1283
|
set_rcparams(20, 'w')
|
|
1271
|
-
|
|
1272
|
-
nrows = nprof
|
|
1273
|
-
if fig is None:
|
|
1274
|
-
fig = plt.figure(figsize=(6 * ncols, 3 * nrows))
|
|
1275
|
-
if nprof > 1 and ax is not None:
|
|
1276
|
-
print('External ax is supported only when len(coords)=1.')
|
|
1277
|
-
ax = None
|
|
1278
|
-
ax = np.empty(nprof, dtype='object') if ax is None else [ax]
|
|
1279
|
-
if 'xlabel' not in _kw:
|
|
1280
|
-
_kw['xlabel'] = 'Velocity (km s$^{-1}$)'
|
|
1281
|
-
if 'xlim' not in _kw:
|
|
1282
|
-
_kw['xlim'] = [v.min(), v.max()]
|
|
1283
|
-
_kw['samexy'] = False
|
|
1284
|
-
pa2 = kwargs2instance(PlotAxes2D, _kw)
|
|
1284
|
+
fig, ax = _set_figax_plotprofile(fig, ax, nrows, ncols, nprof)
|
|
1285
1285
|
for i in range(nprof):
|
|
1286
|
-
sharex = None if i < nrows - 1 else ax[i - 1]
|
|
1287
|
-
ax[i] = fig.add_subplot(nrows, ncols, i + 1, sharex=sharex)
|
|
1288
1286
|
if gaussfit:
|
|
1289
|
-
ax[i].plot(v,
|
|
1287
|
+
ax[i].plot(v, gaussian1d(v, *gfitres['best'][i]), **_kwgauss)
|
|
1290
1288
|
ax[i].plot(v, prof[i], **_kw)
|
|
1291
1289
|
ax[i].hlines([0], v.min(), v.max(), linestyle='dashed', color='k')
|
|
1292
1290
|
ax[i].set_ylabel(ylabel[i])
|
|
@@ -1294,7 +1292,7 @@ def plotprofile(coords: list[str] | str = [],
|
|
|
1294
1292
|
if text is not None:
|
|
1295
1293
|
ax[i].text(**text[i])
|
|
1296
1294
|
if title is not None:
|
|
1297
|
-
if
|
|
1295
|
+
if isinstance(title[i], str):
|
|
1298
1296
|
title[i] = {'label': title[i]}
|
|
1299
1297
|
ax[i].set_title(**title[i])
|
|
1300
1298
|
if i <= nprof - ncols - 1:
|
|
@@ -1369,8 +1367,69 @@ def plotslice(length: float, dx: float | None = None, pa: float = 0,
|
|
|
1369
1367
|
close_figure(fig, savefig, show)
|
|
1370
1368
|
|
|
1371
1369
|
|
|
1370
|
+
def _plot_on_wall(d: AstroData, x: np.ndarray, y: np.ndarray, v: np.ndarray,
|
|
1371
|
+
measure: object, datalist: list,
|
|
1372
|
+
sign: int, axis: int, **kwargs):
|
|
1373
|
+
dx, dy, dv = x[1] - x[0], y[1] - y[0], v[1] - v[0]
|
|
1374
|
+
s, ds = [x, y, v], [dx, dy, dv]
|
|
1375
|
+
if kwargs == {}:
|
|
1376
|
+
return
|
|
1377
|
+
|
|
1378
|
+
match axis:
|
|
1379
|
+
case 2:
|
|
1380
|
+
shape = np.shape(d.data[:, :, 0])
|
|
1381
|
+
case 1:
|
|
1382
|
+
shape = np.shape(d.data[:, 0, :])
|
|
1383
|
+
case 0:
|
|
1384
|
+
shape = np.shape(d.data[0, :, :])
|
|
1385
|
+
if np.shape(kwargs['data']) != shape:
|
|
1386
|
+
print('The shape of the 2D data is inconsistent'
|
|
1387
|
+
+ ' with the shape of the 3D data.')
|
|
1388
|
+
return
|
|
1389
|
+
|
|
1390
|
+
_kw = {'levels': [3, 6, 12, 24, 48, 96, 192, 384],
|
|
1391
|
+
'sigma': 'hist', 'cmap': 'Jet', 'alpha': 0.3}
|
|
1392
|
+
_kw.update(kwargs)
|
|
1393
|
+
volume = _kw['data']
|
|
1394
|
+
levels = _kw['levels']
|
|
1395
|
+
cmap = _kw['cmap']
|
|
1396
|
+
alpha = _kw['alpha']
|
|
1397
|
+
sigma = estimate_rms(data=volume, sigma=_kw['sigma'])
|
|
1398
|
+
volume[np.isnan(volume)] = 0
|
|
1399
|
+
a = int(sign == -1)
|
|
1400
|
+
b = int(sign == 1)
|
|
1401
|
+
volume = np.moveaxis([volume * a, volume * b], 0, axis)
|
|
1402
|
+
if d.dx < 0:
|
|
1403
|
+
volume = volume[:, :, ::-1]
|
|
1404
|
+
if d.dy < 0:
|
|
1405
|
+
volume = volume[:, ::-1, :]
|
|
1406
|
+
if d.dv < 0:
|
|
1407
|
+
volume = volume[::-1, :, :]
|
|
1408
|
+
for lev in levels:
|
|
1409
|
+
if lev * sigma > np.max(volume):
|
|
1410
|
+
continue
|
|
1411
|
+
vertices, simplices, _, _ = measure.marching_cubes(volume, lev * sigma)
|
|
1412
|
+
Xg, Yg, Zg = [t[0] + i * dt for t, i, dt
|
|
1413
|
+
in zip(s, vertices.T[::-1], ds)]
|
|
1414
|
+
match axis:
|
|
1415
|
+
case 2:
|
|
1416
|
+
Xg = Xg * 0 + (x[-1] if sign == 1 else x[0])
|
|
1417
|
+
case 1:
|
|
1418
|
+
Yg = Yg * 0 + (y[-1] if sign == 1 else y[0])
|
|
1419
|
+
case 0:
|
|
1420
|
+
Zg = Zg * 0 + (v[-1] if sign == 1 else v[0])
|
|
1421
|
+
i, j, k = simplices.T
|
|
1422
|
+
mesh2d = dict(type='mesh3d', x=Xg, y=Yg, z=Zg,
|
|
1423
|
+
i=i, j=j, k=k,
|
|
1424
|
+
intensity=Zg * 0 + lev,
|
|
1425
|
+
colorscale=cmap, reversescale=False,
|
|
1426
|
+
cmin=np.min(levels), cmax=np.max(levels),
|
|
1427
|
+
opacity=alpha, name='', showscale=False)
|
|
1428
|
+
datalist.append(mesh2d)
|
|
1429
|
+
|
|
1430
|
+
|
|
1372
1431
|
def plot3d(levels: list[float] = [3, 6, 12],
|
|
1373
|
-
cmap: str = '
|
|
1432
|
+
cmap: str = 'jet', alpha: float = 0.08,
|
|
1374
1433
|
xlabel: str = 'R.A. (arcsec)',
|
|
1375
1434
|
ylabel: str = 'Dec. (arcsec)',
|
|
1376
1435
|
vlabel: str = 'Velocity (km/s)',
|
|
@@ -1461,67 +1520,11 @@ def plot3d(levels: list[float] = [3, 6, 12],
|
|
|
1461
1520
|
name='', line=dict(color='rgb(0,0,0)', width=1))
|
|
1462
1521
|
data.append(lines)
|
|
1463
1522
|
|
|
1464
|
-
def plot_on_wall(sign: int, axis: int, **kwargs):
|
|
1465
|
-
if kwargs == {}:
|
|
1466
|
-
return
|
|
1467
|
-
|
|
1468
|
-
match axis:
|
|
1469
|
-
case 2:
|
|
1470
|
-
shape = np.shape(d.data[:, :, 0])
|
|
1471
|
-
case 1:
|
|
1472
|
-
shape = np.shape(d.data[:, 0, :])
|
|
1473
|
-
case 0:
|
|
1474
|
-
shape = np.shape(d.data[0, :, :])
|
|
1475
|
-
if np.shape(kwargs['data']) != shape:
|
|
1476
|
-
print('The shape of the 2D data is inconsistent'
|
|
1477
|
-
+ ' with the shape of the 3D data.')
|
|
1478
|
-
return
|
|
1479
|
-
|
|
1480
|
-
_kw = {'levels': [3, 6, 12, 24, 48, 96, 192, 384],
|
|
1481
|
-
'sigma': 'hist', 'cmap': 'Jet', 'alpha': 0.3}
|
|
1482
|
-
_kw.update(kwargs)
|
|
1483
|
-
volume = _kw['data']
|
|
1484
|
-
levels = _kw['levels']
|
|
1485
|
-
cmap = _kw['cmap']
|
|
1486
|
-
alpha = _kw['alpha']
|
|
1487
|
-
sigma = estimate_rms(data=volume, sigma=_kw['sigma'])
|
|
1488
|
-
volume[np.isnan(volume)] = 0
|
|
1489
|
-
a = int(sign == -1)
|
|
1490
|
-
b = int(sign == 1)
|
|
1491
|
-
volume = np.moveaxis([volume * a, volume * b], 0, axis)
|
|
1492
|
-
if d.dx < 0:
|
|
1493
|
-
volume = volume[:, :, ::-1]
|
|
1494
|
-
if d.dy < 0:
|
|
1495
|
-
volume = volume[:, ::-1, :]
|
|
1496
|
-
if d.dv < 0:
|
|
1497
|
-
volume = volume[::-1, :, :]
|
|
1498
|
-
for lev in levels:
|
|
1499
|
-
if lev * sigma > np.max(volume):
|
|
1500
|
-
continue
|
|
1501
|
-
vertices, simplices, _, _ = measure.marching_cubes(volume, lev * sigma)
|
|
1502
|
-
Xg, Yg, Zg = [t[0] + i * dt for t, i, dt
|
|
1503
|
-
in zip(s, vertices.T[::-1], ds)]
|
|
1504
|
-
match axis:
|
|
1505
|
-
case 2:
|
|
1506
|
-
Xg = Xg * 0 + (x[-1] if sign == 1 else x[0])
|
|
1507
|
-
case 1:
|
|
1508
|
-
Yg = Yg * 0 + (y[-1] if sign == 1 else y[0])
|
|
1509
|
-
case 0:
|
|
1510
|
-
Zg = Zg * 0 + (v[-1] if sign == 1 else v[0])
|
|
1511
|
-
i, j, k = simplices.T
|
|
1512
|
-
mesh2d = dict(type='mesh3d', x=Xg, y=Yg, z=Zg,
|
|
1513
|
-
i=i, j=j, k=k,
|
|
1514
|
-
intensity=Zg * 0 + lev,
|
|
1515
|
-
colorscale=cmap, reversescale=False,
|
|
1516
|
-
cmin=np.min(levels), cmax=np.max(levels),
|
|
1517
|
-
opacity=alpha, name='', showscale=False)
|
|
1518
|
-
data.append(mesh2d)
|
|
1519
|
-
|
|
1520
1523
|
klist = [xplus, xminus, yplus, yminus, vplus, vminus]
|
|
1521
1524
|
slist = [1, -1, 1, -1, 1, -1]
|
|
1522
1525
|
alist = [2, 2, 1, 1, 0, 0]
|
|
1523
1526
|
for kw, sign, axis in zip(klist, slist, alist):
|
|
1524
|
-
|
|
1527
|
+
_plot_on_wall(d, x, y, v, measure, data, sign, axis, **kw)
|
|
1525
1528
|
|
|
1526
1529
|
if return_data_layout:
|
|
1527
1530
|
return {'data': data, 'layout': layout}
|
|
@@ -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
|