plotastrodata 1.8.17__tar.gz → 1.8.18__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.8.17/plotastrodata.egg-info → plotastrodata-1.8.18}/PKG-INFO +1 -1
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata/__init__.py +1 -1
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata/analysis_utils.py +10 -9
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata/coord_utils.py +43 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata/other_utils.py +112 -2
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata/plot_utils.py +100 -129
- {plotastrodata-1.8.17 → plotastrodata-1.8.18/plotastrodata.egg-info}/PKG-INFO +1 -1
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/LICENSE +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/MANIFEST.in +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/README.md +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata/const_utils.py +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata/ext_utils.py +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata/fft_utils.py +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata/fits_utils.py +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata/fitting_utils.py +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata/los_utils.py +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata/matrix_utils.py +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata/noise_utils.py +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata.egg-info/SOURCES.txt +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata.egg-info/dependency_links.txt +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata.egg-info/not-zip-safe +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata.egg-info/requires.txt +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/plotastrodata.egg-info/top_level.txt +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/setup.cfg +0 -0
- {plotastrodata-1.8.17 → plotastrodata-1.8.18}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plotastrodata
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.18
|
|
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
|
|
@@ -7,11 +7,12 @@ from scipy.signal import convolve
|
|
|
7
7
|
from plotastrodata import const_utils as cu
|
|
8
8
|
from plotastrodata.coord_utils import coord2xy, rel2abs, xy2coord
|
|
9
9
|
from plotastrodata.fits_utils import data2fits, FitsData, Jy2K
|
|
10
|
-
from plotastrodata.fitting_utils import (EmceeCorner,
|
|
11
|
-
|
|
10
|
+
from plotastrodata.fitting_utils import (EmceeCorner, gaussfit1d,
|
|
11
|
+
gaussfit2d, gaussian2d)
|
|
12
12
|
from plotastrodata.matrix_utils import dot2d, Mfac, Mrot
|
|
13
13
|
from plotastrodata.noise_utils import estimate_rms
|
|
14
|
-
from plotastrodata.other_utils import isdeg,
|
|
14
|
+
from plotastrodata.other_utils import (isdeg, nearest_index,
|
|
15
|
+
RGIxy, RGIxyv, to4dim, trim)
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
def quadrantmean(data: np.ndarray, x: np.ndarray, y: np.ndarray,
|
|
@@ -197,10 +198,10 @@ class AstroData():
|
|
|
197
198
|
includev (bool, optional): Centering in the v direction at each position. Defaults to False.
|
|
198
199
|
"""
|
|
199
200
|
if includexy:
|
|
200
|
-
xnew = self.x - self.x[
|
|
201
|
-
ynew = self.y - self.y[
|
|
201
|
+
xnew = self.x - self.x[nearest_index(self.x)]
|
|
202
|
+
ynew = self.y - self.y[nearest_index(self.y)]
|
|
202
203
|
if includev:
|
|
203
|
-
vnew = self.v - self.v[
|
|
204
|
+
vnew = self.v - self.v[nearest_index(self.v)]
|
|
204
205
|
if includexy and includev:
|
|
205
206
|
self.data = RGIxyv(self.v, self.y, self.x, self.data,
|
|
206
207
|
np.meshgrid(vnew, ynew, xnew, indexing='ij'),
|
|
@@ -518,10 +519,10 @@ class AstroData():
|
|
|
518
519
|
"""
|
|
519
520
|
fhd = self.fitsheader
|
|
520
521
|
h = {}
|
|
521
|
-
ci =
|
|
522
|
+
ci = nearest_index(self.x)
|
|
522
523
|
cx = 0
|
|
523
524
|
if not self.pv:
|
|
524
|
-
cj =
|
|
525
|
+
cj = nearest_index(self.y)
|
|
525
526
|
if self.center is None:
|
|
526
527
|
cx, cy = self.x[ci], self.x[cj]
|
|
527
528
|
else:
|
|
@@ -538,7 +539,7 @@ class AstroData():
|
|
|
538
539
|
h['CDELT1'] = float(self.dx / (3600 if indeg('1') else 1))
|
|
539
540
|
if self.dv is not None:
|
|
540
541
|
vaxis = '2' if self.pv else '3'
|
|
541
|
-
ck =
|
|
542
|
+
ck = nearest_index(self.v)
|
|
542
543
|
cv = self.v[ck]
|
|
543
544
|
dv = self.dv
|
|
544
545
|
if self.restfreq is None or self.restfreq == 0:
|
|
@@ -149,3 +149,46 @@ def abs2rel(xabs: float, yabs: float,
|
|
|
149
149
|
xrel = (xabs - x[0]) / (x[-1] - x[0])
|
|
150
150
|
yrel = (yabs - y[0]) / (y[-1] - y[0])
|
|
151
151
|
return np.array([xrel, yrel])
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def get_sec(coord: str, mode: str) -> str:
|
|
155
|
+
"""Pick up the second number from a hmsdms string.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
coord (str): hmsdms string.
|
|
159
|
+
mode (str): 'ra' or 'dec'
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
str: The second number as a string without the unit.
|
|
163
|
+
"""
|
|
164
|
+
i_axis = 0 if mode == 'ra' else 1
|
|
165
|
+
return coord.split(' ')[i_axis].split('m')[1].strip('s')
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def get_min(coord: str, mode: str) -> str:
|
|
169
|
+
"""Pick up the minute number from a hmsdms string.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
coord (str): hmsdms string.
|
|
173
|
+
mode (str): 'ra' or 'dec'
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
str: The minute number as a string without the unit.
|
|
177
|
+
"""
|
|
178
|
+
i_axis = 0 if mode == 'ra' else 1
|
|
179
|
+
s = 'h' if mode == 'ra' else 'd'
|
|
180
|
+
return coord.split(' ')[i_axis].split(s)[1].split('m')[0]
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def get_hmdm(coord: str, mode: str) -> str:
|
|
184
|
+
"""Pick up the coordinate string before the second part from a hsmdms string.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
coord (str): hmsdms string.
|
|
188
|
+
mode (str): 'ra' or 'dec'
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
str: The hm or dm string with the units.
|
|
192
|
+
"""
|
|
193
|
+
i_axis = 0 if mode == 'ra' else 1
|
|
194
|
+
return coord.split(' ')[i_axis].split('m')[0] + 'm'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import matplotlib.pyplot as plt
|
|
2
2
|
import numpy as np
|
|
3
|
+
import warnings
|
|
3
4
|
from scipy.interpolate import RegularGridInterpolator as RGI
|
|
4
5
|
|
|
5
6
|
|
|
@@ -33,6 +34,20 @@ def isdeg(s: str) -> bool:
|
|
|
33
34
|
return False
|
|
34
35
|
|
|
35
36
|
|
|
37
|
+
def nearest_index(arr: np.ndarray, x: float = 0) -> int:
|
|
38
|
+
"""Get the index of the (sorted) arrary that gives a value nearest to x. This is equivalent to np.argmin(np.abs(arr - x)) but optimized for the sorted array.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
arr (np.ndarray): Sorted array.
|
|
42
|
+
x (float, optional): Value to approach. Defaults to 0.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
int: The index that gives a value nearest to x.
|
|
46
|
+
"""
|
|
47
|
+
idx = np.searchsorted(arr, x).clip(1, len(arr) - 1)
|
|
48
|
+
return idx - 1 if x - arr[idx - 1] <= arr[idx] - x else idx
|
|
49
|
+
|
|
50
|
+
|
|
36
51
|
def trim(data: np.ndarray | None = None, x: np.ndarray | None = None,
|
|
37
52
|
y: np.ndarray | None = None, v: np.ndarray | None = None,
|
|
38
53
|
xlim: list[float] | None = None,
|
|
@@ -57,8 +72,8 @@ def trim(data: np.ndarray | None = None, x: np.ndarray | None = None,
|
|
|
57
72
|
def get_bounds(arr, lim):
|
|
58
73
|
if arr is None or lim is None or None in lim:
|
|
59
74
|
return arr, 0, None
|
|
60
|
-
lo =
|
|
61
|
-
hi =
|
|
75
|
+
lo = nearest_index(arr, max(np.min(arr), lim[0]))
|
|
76
|
+
hi = nearest_index(arr, min(np.max(arr), lim[1]))
|
|
62
77
|
lo, hi = sorted((lo, hi))
|
|
63
78
|
return arr[lo:hi + 1], lo, hi + 1
|
|
64
79
|
|
|
@@ -106,6 +121,101 @@ def to4dim(data: np.ndarray) -> np.ndarray:
|
|
|
106
121
|
return d
|
|
107
122
|
|
|
108
123
|
|
|
124
|
+
def reform_grid(v: np.ndarray | None = None,
|
|
125
|
+
k0: int | None = None, k1: int | None = None,
|
|
126
|
+
vmin: float | None = None, vmax: float | None = None
|
|
127
|
+
) -> np.ndarray:
|
|
128
|
+
"""Extend or cut the given 1D array based on the given range.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
v (np.ndarray | None, optional): Input 1D array. Defaults to None.
|
|
132
|
+
k0 (int | None, optional): How many channels are added before v[0]; the minus sign means extension. k0 has the priority over vmin. Defaults to None.
|
|
133
|
+
k1 (int | None, optional): How many channels are added after v[-1]; the plus sign means extension. k1 has the priority over vmax. Defaults to None.
|
|
134
|
+
vmin (float | None, optional): New minimum velocity. Defaults to None.
|
|
135
|
+
vmax (float | None, optional): New maximum velocity. Defaults to None.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
np.ndarray: Extended or cut 1D array.
|
|
139
|
+
"""
|
|
140
|
+
if v is None or len(v) <= 1:
|
|
141
|
+
return v
|
|
142
|
+
|
|
143
|
+
dv = v[1] - v[0]
|
|
144
|
+
if k0 is None and vmin is not None:
|
|
145
|
+
k0 = int(round((vmin - v[0]) / dv))
|
|
146
|
+
if k0 is not None and k0 != 0:
|
|
147
|
+
if k0 < 0:
|
|
148
|
+
vpre = v[0] + dv * np.arange(k0, 0)
|
|
149
|
+
v = np.concatenate((vpre, v))
|
|
150
|
+
else:
|
|
151
|
+
v = v[k0:]
|
|
152
|
+
if k1 is None and vmax is not None:
|
|
153
|
+
k1 = int(round((vmax - v[-1]) / dv))
|
|
154
|
+
if k1 is not None and k1 != 0:
|
|
155
|
+
if k1 > 0:
|
|
156
|
+
vpost = v[-1] + dv * np.arange(1, k1 + 1)
|
|
157
|
+
v = np.concatenate((v, vpost))
|
|
158
|
+
else:
|
|
159
|
+
v = v[:len(v) + k1]
|
|
160
|
+
return v
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def reform_data(c: np.ndarray, v_in: np.ndarray | None,
|
|
164
|
+
nv: int, v_org: np.ndarray | None = None,
|
|
165
|
+
vskip: int = 1) -> np.ndarray:
|
|
166
|
+
"""Skip and fill channels with nan.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
c (np.ndarray): The input 2D or 3D arrays.
|
|
170
|
+
v_in (np.ndarray): The input velocity 1D array.
|
|
171
|
+
nv (int): The number of channels with a label.
|
|
172
|
+
v (np.ndarray, optional): The velocity 1D array, including the channels with and without a label. Defaults to None.
|
|
173
|
+
vskip (int, optional): How many channels are skipped. Defaults to 1.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
np.ndarray: 3D arrays skipped and filled with nan.
|
|
177
|
+
"""
|
|
178
|
+
if v_org is None:
|
|
179
|
+
return c
|
|
180
|
+
|
|
181
|
+
ndim = np.ndim(c)
|
|
182
|
+
if ndim not in [2, 3]:
|
|
183
|
+
print('c must be 2D or 3D.')
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
if ndim == 2:
|
|
187
|
+
d = np.full((nv, *np.shape(c)), c)
|
|
188
|
+
elif v_in is not None:
|
|
189
|
+
dv_org = v_org[1] - v_org[0]
|
|
190
|
+
dv_in = (v_in[1] - v_in[0]) * vskip
|
|
191
|
+
k0 = nearest_index(v_org, v_in[0])
|
|
192
|
+
k1 = nearest_index(v_org, v_in[-1])
|
|
193
|
+
if np.abs(dv_in - dv_org) / dv_org < 0.01:
|
|
194
|
+
d = c
|
|
195
|
+
else:
|
|
196
|
+
s = 'Velocity resolution mismatch (>1%).' \
|
|
197
|
+
+ ' The cube needs to be regridded' \
|
|
198
|
+
+ ' outside plotastrodata.'
|
|
199
|
+
warnings.warn(s, UserWarning)
|
|
200
|
+
n_valid = k1 - k0
|
|
201
|
+
d = [None] * n_valid
|
|
202
|
+
for k in range(n_valid):
|
|
203
|
+
k_tmp = nearest_index(v_in, v_org[k])
|
|
204
|
+
diffvel = np.abs(v_in[k_tmp] - v_org[k])
|
|
205
|
+
nearby = diffvel < dv_org * 0.5
|
|
206
|
+
d[k] = c[k_tmp] if nearby else c[0] * np.nan
|
|
207
|
+
d = np.array(d)
|
|
208
|
+
if k0 > 0:
|
|
209
|
+
prenan = np.full((k0, *np.shape(d)[1:]), np.nan)
|
|
210
|
+
d = np.concatenate((prenan, d))
|
|
211
|
+
d = d[::vskip]
|
|
212
|
+
shape = np.shape(d)
|
|
213
|
+
shape = (len(v_org) - shape[0], shape[1], shape[2])
|
|
214
|
+
postnan = np.full(shape, np.nan)
|
|
215
|
+
d = np.concatenate((d, postnan))
|
|
216
|
+
return d
|
|
217
|
+
|
|
218
|
+
|
|
109
219
|
def RGIxy(y: np.ndarray, x: np.ndarray, data: np.ndarray,
|
|
110
220
|
yxnew: tuple[np.ndarray, np.ndarray] | None = None,
|
|
111
221
|
**kwargs) -> object | np.ndarray:
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import matplotlib as mpl
|
|
2
2
|
import matplotlib.pyplot as plt
|
|
3
3
|
import numpy as np
|
|
4
|
-
import warnings
|
|
5
4
|
from dataclasses import dataclass
|
|
6
5
|
from matplotlib.patches import Ellipse, Rectangle
|
|
7
6
|
from typing import TypeVar
|
|
8
7
|
|
|
9
8
|
from plotastrodata.analysis_utils import AstroData, AstroFrame
|
|
10
|
-
from plotastrodata.coord_utils import coord2xy, xy2coord
|
|
9
|
+
from plotastrodata.coord_utils import (coord2xy, xy2coord,
|
|
10
|
+
get_hmdm, get_min, get_sec)
|
|
11
11
|
from plotastrodata.noise_utils import estimate_rms
|
|
12
|
-
from plotastrodata.other_utils import close_figure, listing
|
|
12
|
+
from plotastrodata.other_utils import (close_figure, listing,
|
|
13
|
+
reform_grid, reform_data)
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
plt.ioff() # force to turn off interactive mode
|
|
@@ -99,28 +100,42 @@ def logcbticks(vmin: float = 1e-3, vmax: float = 1e3
|
|
|
99
100
|
return ticks[cond], ticklabels[cond]
|
|
100
101
|
|
|
101
102
|
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
def get_figsize(xmin: float, xmax: float, ymin: float, ymax: float,
|
|
104
|
+
figsize: tuple | None = None,
|
|
105
|
+
ncols: int = 1, nrows: int = 1, nchan: int = 1
|
|
106
|
+
) -> tuple[float, float]:
|
|
107
|
+
"""Get a nice figsize (tuple) with the given x and y ranges.
|
|
105
108
|
|
|
109
|
+
Args:
|
|
110
|
+
xmin (float): The figsize is based on the aspect ratio of (ymax - ymin) / (xmax - xmin).
|
|
111
|
+
xmax (float): The figsize is based on the aspect ratio of (ymax - ymin) / (xmax - xmin).
|
|
112
|
+
ymin (float): The figsize is based on the aspect ratio of (ymax - ymin) / (xmax - xmin).
|
|
113
|
+
ymax (float): The figsize is based on the aspect ratio of (ymax - ymin) / (xmax - xmin).
|
|
114
|
+
figsize (tuple | None, optional): If this is not None, this will be the output as is. Defaults to None.
|
|
115
|
+
ncols (int, optional): The number of columns for the channel map. Defaults to 1.
|
|
116
|
+
nrows (int, optional): The number of rows for the channel map. Defaults to 1.
|
|
117
|
+
nchan (int, optional): The number of total channels for the channel map. Defaults to 1.
|
|
106
118
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
Returns:
|
|
120
|
+
tuple[float, float]: figsize for matplotlib.pyplot.Figure.
|
|
121
|
+
"""
|
|
122
|
+
if figsize is not None:
|
|
123
|
+
return figsize
|
|
112
124
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
125
|
+
sqrt_a = (ymax - ymin) / (xmax - xmin)
|
|
126
|
+
sqrt_a = np.sqrt(np.abs(sqrt_a))
|
|
127
|
+
if nchan == 1:
|
|
128
|
+
figsize = (7 / sqrt_a, 5 * sqrt_a)
|
|
129
|
+
else:
|
|
130
|
+
figsize = (ncols * 2 / sqrt_a, max(nrows*2, 3) * sqrt_a)
|
|
131
|
+
return figsize
|
|
116
132
|
|
|
117
133
|
|
|
118
134
|
def _get_gridwidth(mode: str, rmax: float) -> tuple[float, int]:
|
|
119
135
|
# 10^1.5 / 15 ~ 2 grids for R.A.
|
|
120
136
|
# 10^0.5 ~ 3 grids for Dec.
|
|
121
137
|
scale = 1.5 if mode == 'ra' else 0.5
|
|
122
|
-
|
|
123
|
-
x = log2r - scale
|
|
138
|
+
x = np.log10(2. * rmax) - scale
|
|
124
139
|
order = np.floor(x)
|
|
125
140
|
frac = x - order
|
|
126
141
|
if frac <= 0.33:
|
|
@@ -132,6 +147,42 @@ def _get_gridwidth(mode: str, rmax: float) -> tuple[float, int]:
|
|
|
132
147
|
return base * 10**order, int(order)
|
|
133
148
|
|
|
134
149
|
|
|
150
|
+
def _get_v(p, v: np.ndarray | None = None,
|
|
151
|
+
restfreq: float | None = None,
|
|
152
|
+
vskip: int = 1) -> np.ndarray:
|
|
153
|
+
if p.fitsimage is not None:
|
|
154
|
+
p.read(d := AstroData(fitsimage=p.fitsimage,
|
|
155
|
+
restfreq=restfreq, sigma=None))
|
|
156
|
+
v = d.v
|
|
157
|
+
if v is None:
|
|
158
|
+
v = np.array([0])
|
|
159
|
+
if len(v) > 1:
|
|
160
|
+
v = reform_grid(v=v, vmin=p.vmin, vmax=p.vmax)
|
|
161
|
+
v = v[::vskip]
|
|
162
|
+
return v
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _get_nij2ch(nrows: int = 1, ncols: int = 1) -> object:
|
|
166
|
+
def nij2ch(n: int, i: int, j: int) -> int:
|
|
167
|
+
return n*nrows*ncols + i*ncols + j
|
|
168
|
+
return nij2ch
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _get_ch2nij(nrows: int = 1, ncols: int = 1) -> object:
|
|
172
|
+
def ch2nij(ch: int) -> tuple[int, int, int]:
|
|
173
|
+
n = ch // (nrows*ncols)
|
|
174
|
+
i = (ch - n*nrows*ncols) // ncols
|
|
175
|
+
j = ch % ncols
|
|
176
|
+
return n, i, j
|
|
177
|
+
return ch2nij
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _get_vskipfill(nv: float, v_org: np.ndarray, vskip: int) -> object:
|
|
181
|
+
def vskipfill(c: np.ndarray, v_in: np.ndarray) -> np.ndarray:
|
|
182
|
+
return reform_data(c=c, v_in=v_in, nv=nv, v_org=v_org, vskip=vskip)
|
|
183
|
+
return vskipfill
|
|
184
|
+
|
|
185
|
+
|
|
135
186
|
@dataclass
|
|
136
187
|
class Stretcher():
|
|
137
188
|
"""Arguments and methods related to the stretch in PlotAstroData.add_color() and add_rgb().
|
|
@@ -416,7 +467,7 @@ class PlotAstroData(AstroFrame):
|
|
|
416
467
|
kwargs is the arguments of AstroFrame to define plotting ranges.
|
|
417
468
|
|
|
418
469
|
Args:
|
|
419
|
-
v (np.ndarray, optional): Used to set up channels if fitsimage not given. Defaults to
|
|
470
|
+
v (np.ndarray, optional): Used to set up channels if fitsimage not given. Defaults to None.
|
|
420
471
|
vskip (int, optional): How many channels are skipped. Defaults to 1.
|
|
421
472
|
veldigit (int, optional): How many digits after the decimal point. Defaults to 2.
|
|
422
473
|
restfreq (float, optional): Used for velocity and brightness T. Defaults to None.
|
|
@@ -431,7 +482,7 @@ class PlotAstroData(AstroFrame):
|
|
|
431
482
|
ax (optional): External fig.add_subplot(). Defaults to None.
|
|
432
483
|
"""
|
|
433
484
|
def __init__(self,
|
|
434
|
-
v: np.ndarray =
|
|
485
|
+
v: np.ndarray | None = None, vskip: int = 1,
|
|
435
486
|
veldigit: int = 2, restfreq: float | None = None,
|
|
436
487
|
channelnumber: int | None = None,
|
|
437
488
|
nrows: int = 4, ncols: int = 6,
|
|
@@ -443,74 +494,42 @@ class PlotAstroData(AstroFrame):
|
|
|
443
494
|
super().__init__(**kwargs)
|
|
444
495
|
internalfig = fig is None
|
|
445
496
|
internalax = ax is None
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
if self.
|
|
449
|
-
|
|
450
|
-
restfreq=restfreq, sigma=None))
|
|
451
|
-
v = d.v
|
|
452
|
-
if len(v) > 1:
|
|
453
|
-
dv = v[1] - v[0]
|
|
454
|
-
k0 = int(round((self.vmin - v[0]) / dv))
|
|
455
|
-
if k0 < 0:
|
|
456
|
-
vpre = v[0] - (1 + np.arange(-k0)[::-1]) * dv
|
|
457
|
-
v = np.append(vpre, v)
|
|
458
|
-
else:
|
|
459
|
-
v = v[k0:]
|
|
460
|
-
k1 = len(v) + int(round((self.vmax - v[-1]) / dv))
|
|
461
|
-
if k1 > len(v):
|
|
462
|
-
vpost = v[-1] + (1 + np.arange(k1 - len(v))) * dv
|
|
463
|
-
v = np.append(v, vpost)
|
|
464
|
-
else:
|
|
465
|
-
v = v[:k1]
|
|
466
|
-
if self.pv or v is None or len(v) == 1:
|
|
467
|
-
nv = nrows = ncols = npages = nchan = 1
|
|
497
|
+
v = _get_v(p=self, v=v, restfreq=restfreq, vskip=vskip)
|
|
498
|
+
nv = len(v) # number of channels with a label
|
|
499
|
+
if self.pv or len(v) == 1 or channelnumber is not None:
|
|
500
|
+
nrows = ncols = npages = nchan = 1
|
|
468
501
|
else:
|
|
469
|
-
nv = len(v := v[::vskip])
|
|
470
502
|
npages = int(np.ceil(nv / nrows / ncols))
|
|
471
503
|
nchan = npages * nrows * ncols
|
|
472
|
-
v =
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
def nij2ch(n: int, i: int, j: int):
|
|
477
|
-
return n*nrows*ncols + i*ncols + j
|
|
478
|
-
|
|
479
|
-
def ch2nij(ch: int) -> tuple:
|
|
480
|
-
n = ch // (nrows*ncols)
|
|
481
|
-
i = (ch - n*nrows*ncols) // ncols
|
|
482
|
-
j = ch % ncols
|
|
483
|
-
return n, i, j
|
|
484
|
-
|
|
504
|
+
v = reform_grid(v, k1=nchan - nv)
|
|
505
|
+
nij2ch = _get_nij2ch(nrows=nrows, ncols=ncols)
|
|
506
|
+
ch2nij = _get_ch2nij(nrows=nrows, ncols=ncols)
|
|
485
507
|
if fontsize is None:
|
|
486
508
|
fontsize = 18 if nchan == 1 else 12
|
|
487
509
|
set_rcparams(fontsize=fontsize, nancolor=nancolor, dpi=dpi)
|
|
488
|
-
ax = np.empty(nchan, dtype=
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
else:
|
|
495
|
-
figsize = (ncols * 2 / sqrt_a, max(nrows*2, 3) * sqrt_a)
|
|
510
|
+
ax = np.empty(nchan, dtype=object) if internalax else [ax]
|
|
511
|
+
figsize = get_figsize(xmin=self.xmin, xmax=self.xmax,
|
|
512
|
+
ymin=self.ymin, ymax=self.ymax,
|
|
513
|
+
figsize=figsize,
|
|
514
|
+
ncols=ncols, nrows=nrows, nchan=nchan)
|
|
515
|
+
need_vlabel = nchan > 1 or type(channelnumber) is int
|
|
496
516
|
for ch in range(nchan):
|
|
497
517
|
n, i, j = ch2nij(ch)
|
|
498
518
|
if internalfig and n not in plt.get_fignums():
|
|
499
519
|
fig = plt.figure(n, figsize=figsize)
|
|
500
|
-
|
|
501
|
-
|
|
520
|
+
if need_vlabel:
|
|
521
|
+
fig.subplots_adjust(hspace=0, wspace=0,
|
|
522
|
+
right=0.87, top=0.87)
|
|
502
523
|
if internalax:
|
|
524
|
+
sharex = ax[nij2ch(n, i - 1, j)] if i > 0 else None
|
|
525
|
+
sharey = ax[nij2ch(n, i, j - 1)] if j > 0 else None
|
|
503
526
|
ax[ch] = fig.add_subplot(nrows, ncols, i*ncols + j + 1,
|
|
504
527
|
sharex=sharex, sharey=sharey)
|
|
505
|
-
if
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
vd = f'{veldigit:d}'
|
|
511
|
-
ax[ch].text(0.9 * self.rmax, 0.7 * self.rmax,
|
|
512
|
-
rf'${vellabel:.{vd}f}$', color='black',
|
|
513
|
-
backgroundcolor='white', zorder=20)
|
|
528
|
+
if need_vlabel and ch < nv:
|
|
529
|
+
vlabel = v[channelnumber or ch]
|
|
530
|
+
ax[ch].text(0.9 * self.rmax, 0.7 * self.rmax,
|
|
531
|
+
rf'${vlabel:.{veldigit}f}$', color='black',
|
|
532
|
+
backgroundcolor='white', zorder=20)
|
|
514
533
|
self.fig = None if internalfig else fig
|
|
515
534
|
self.ax = ax
|
|
516
535
|
self.rowcol = nrows * ncols
|
|
@@ -519,55 +538,7 @@ class PlotAstroData(AstroFrame):
|
|
|
519
538
|
self.bottomleft = nij2ch(np.arange(npages), nrows - 1, 0)
|
|
520
539
|
self.channelnumber = channelnumber
|
|
521
540
|
self.v = v
|
|
522
|
-
|
|
523
|
-
def vskipfill(c: np.ndarray,
|
|
524
|
-
v_in: np.ndarray | None = None
|
|
525
|
-
) -> np.ndarray:
|
|
526
|
-
"""Skip and fill channels with nan.
|
|
527
|
-
|
|
528
|
-
Args:
|
|
529
|
-
c (np.ndarray): 2D or 3D arrays.
|
|
530
|
-
v_in (np.ndarray): 1D array.
|
|
531
|
-
|
|
532
|
-
Returns:
|
|
533
|
-
np.ndarray: 3D arrays skipped and filled with nan.
|
|
534
|
-
"""
|
|
535
|
-
if np.ndim(c) == 2:
|
|
536
|
-
d = np.full((nv, *np.shape(c)), c)
|
|
537
|
-
elif np.ndim(c) == 3:
|
|
538
|
-
if v_in is not None:
|
|
539
|
-
dv_org = self.v[1] - self.v[0]
|
|
540
|
-
dv_in = (v_in[1] - v_in[0]) * vskip
|
|
541
|
-
k0 = np.argmin(np.abs(self.v - v_in[0]))
|
|
542
|
-
k1 = np.argmin(np.abs(self.v - v_in[-1]))
|
|
543
|
-
if np.abs(dv_in - dv_org) / dv_org < 0.01:
|
|
544
|
-
d = c
|
|
545
|
-
else:
|
|
546
|
-
s = 'Velocity resolution mismatch (>1%).' \
|
|
547
|
-
+ ' The cube needs to be regridded' \
|
|
548
|
-
+ ' outside plotastrodata.'
|
|
549
|
-
warnings.warn(s, UserWarning)
|
|
550
|
-
n_valid = k1 - k0
|
|
551
|
-
d = [None] * n_valid
|
|
552
|
-
for k in range(n_valid):
|
|
553
|
-
k_tmp = np.argmin(np.abs(v_in - self.v[k]))
|
|
554
|
-
diffvel = np.abs(v_in[k_tmp] - self.v[k])
|
|
555
|
-
nearby = diffvel < dv_org * 0.5
|
|
556
|
-
d[k] = c[k_tmp] if nearby else c[0] * np.nan
|
|
557
|
-
d = np.array(d)
|
|
558
|
-
if k0 > 0:
|
|
559
|
-
prenan = np.full((k0, *np.shape(d)[1:]), np.nan)
|
|
560
|
-
d = np.append(prenan, d, axis=0)
|
|
561
|
-
d = d[::vskip]
|
|
562
|
-
else:
|
|
563
|
-
print('c must be 2D or 3D.')
|
|
564
|
-
return
|
|
565
|
-
n = nchan if channelnumber is None else nv
|
|
566
|
-
shape = (n - len(d), len(d[0]), len(d[0, 0]))
|
|
567
|
-
postnan = np.full(shape, d[0] * np.nan)
|
|
568
|
-
d = np.append(d, postnan, axis=0)
|
|
569
|
-
return d
|
|
570
|
-
self.vskipfill = vskipfill
|
|
541
|
+
self.vskipfill = _get_vskipfill(nv=nv, v_org=v, vskip=vskip)
|
|
571
542
|
|
|
572
543
|
def _map_init(self, kw: dict) -> tuple:
|
|
573
544
|
"""
|
|
@@ -1092,10 +1063,10 @@ class PlotAstroData(AstroFrame):
|
|
|
1092
1063
|
center = f'{csplit[1]} {csplit[2]}'
|
|
1093
1064
|
if on_min_scale := (self.rmax >= 60.0):
|
|
1094
1065
|
# On a 5-second grid.
|
|
1095
|
-
ra_s = np.floor(float(
|
|
1066
|
+
ra_s = np.floor(float(get_sec(center, 0)) / 5) * 5
|
|
1096
1067
|
dec_s = 0.0
|
|
1097
|
-
ra =
|
|
1098
|
-
dec =
|
|
1068
|
+
ra = get_hmdm(center, 'ra') + f'{ra_s:.1f}s'
|
|
1069
|
+
dec = get_hmdm(center, 'dec') + f'{dec_s:.1f}s'
|
|
1099
1070
|
center = f'{ra} {dec}'
|
|
1100
1071
|
|
|
1101
1072
|
def get_tickvalues(ticks: np.ndarray, mode: str, no_sec: bool
|
|
@@ -1104,7 +1075,7 @@ class PlotAstroData(AstroFrame):
|
|
|
1104
1075
|
if mode == 'ra':
|
|
1105
1076
|
xy.reverse()
|
|
1106
1077
|
tickvalues = xy2coord(xy, center)
|
|
1107
|
-
getter =
|
|
1078
|
+
getter = get_min if no_sec else get_sec
|
|
1108
1079
|
tickvalues = [getter(t, mode) for t in tickvalues] # str
|
|
1109
1080
|
tickvalues = np.array(tickvalues, dtype=float)
|
|
1110
1081
|
# 7-digit precision for practical use.
|
|
@@ -1122,7 +1093,7 @@ class PlotAstroData(AstroFrame):
|
|
|
1122
1093
|
i_mid = (len(intgrid) - 1) // 2
|
|
1123
1094
|
|
|
1124
1095
|
def makegrid(mode: str):
|
|
1125
|
-
second = float(
|
|
1096
|
+
second = float(get_sec(center, mode))
|
|
1126
1097
|
no_sec = on_min_scale and (mode == 'dec')
|
|
1127
1098
|
# gridwidth is a float like 2 x 10^order (arcsec).
|
|
1128
1099
|
gridwidth, order = _get_gridwidth(mode, self.rmax)
|
|
@@ -1143,8 +1114,8 @@ class PlotAstroData(AstroFrame):
|
|
|
1143
1114
|
|
|
1144
1115
|
xticks, xticksminor, xticklabels = makegrid('ra')
|
|
1145
1116
|
yticks, yticksminor, yticklabels = makegrid('dec')
|
|
1146
|
-
ra_hm =
|
|
1147
|
-
dec_dm =
|
|
1117
|
+
ra_hm = get_hmdm(xy2coord([xticks[i_mid] / 3600., 0], center), 'ra')
|
|
1118
|
+
dec_dm = get_hmdm(xy2coord([0, yticks[i_mid] / 3600.], center), 'dec')
|
|
1148
1119
|
if on_min_scale:
|
|
1149
1120
|
dec_dm = dec_dm.split('d')[0] + 'd'
|
|
1150
1121
|
ra_hm = ra_hm.translate(str.maketrans(units['ra']))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plotastrodata
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.18
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|