plotastrodata 1.8.15__tar.gz → 1.8.17__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.15/plotastrodata.egg-info → plotastrodata-1.8.17}/PKG-INFO +1 -1
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata/__init__.py +1 -1
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata/analysis_utils.py +18 -33
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata/fitting_utils.py +116 -27
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata/other_utils.py +0 -25
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata/plot_utils.py +89 -71
- {plotastrodata-1.8.15 → plotastrodata-1.8.17/plotastrodata.egg-info}/PKG-INFO +1 -1
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/LICENSE +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/MANIFEST.in +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/README.md +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata/const_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata/coord_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata/ext_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata/fft_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata/fits_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata/los_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata/matrix_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata/noise_utils.py +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata.egg-info/SOURCES.txt +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata.egg-info/dependency_links.txt +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata.egg-info/not-zip-safe +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata.egg-info/requires.txt +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/plotastrodata.egg-info/top_level.txt +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/setup.cfg +0 -0
- {plotastrodata-1.8.15 → plotastrodata-1.8.17}/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.17
|
|
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
|
|
@@ -2,17 +2,16 @@ import numpy as np
|
|
|
2
2
|
import warnings
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from scipy.interpolate import RegularGridInterpolator as RGI
|
|
5
|
-
from scipy.optimize import curve_fit
|
|
6
5
|
from scipy.signal import convolve
|
|
7
6
|
|
|
8
7
|
from plotastrodata import const_utils as cu
|
|
9
8
|
from plotastrodata.coord_utils import coord2xy, rel2abs, xy2coord
|
|
10
9
|
from plotastrodata.fits_utils import data2fits, FitsData, Jy2K
|
|
11
|
-
from plotastrodata.fitting_utils import EmceeCorner,
|
|
10
|
+
from plotastrodata.fitting_utils import (EmceeCorner, gaussian2d,
|
|
11
|
+
gaussfit1d, gaussfit2d)
|
|
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
|
|
15
|
-
RGIxy, RGIxyv, to4dim, trim)
|
|
14
|
+
from plotastrodata.other_utils import isdeg, RGIxy, RGIxyv, to4dim, trim
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
def quadrantmean(data: np.ndarray, x: np.ndarray, y: np.ndarray,
|
|
@@ -106,7 +105,7 @@ class AstroData():
|
|
|
106
105
|
beam: np.ndarray | tuple[None] = (None, None, None)
|
|
107
106
|
fitsimage: str | None = None
|
|
108
107
|
Tb: bool = False
|
|
109
|
-
sigma: str = 'hist'
|
|
108
|
+
sigma: str | float | None = 'hist'
|
|
110
109
|
center: str = 'common'
|
|
111
110
|
restfreq: float | None = None
|
|
112
111
|
cfactor: float = 1
|
|
@@ -333,47 +332,32 @@ class AstroData():
|
|
|
333
332
|
|
|
334
333
|
@_need_multipixels
|
|
335
334
|
def gaussfit2d(self, chan: int | None = None) -> dict:
|
|
336
|
-
"""Fit a 2D Gaussian function to self.data.
|
|
335
|
+
"""Fit a 2D Gaussian function to self.data using fitting_utils.gaussfit2d().
|
|
337
336
|
|
|
338
337
|
Args:
|
|
339
338
|
chan (int): The channel number where the 2D Gaussian is fitted. Defaults to None.
|
|
340
339
|
|
|
341
340
|
Returns:
|
|
342
|
-
dict: The best parameter set (popt), the
|
|
341
|
+
dict: The best parameter set (popt), the error set (perr), the best 2D Gaussian array (model), the residual from the model (residual), and the coordinates of the best-fit center (center).
|
|
343
342
|
"""
|
|
344
|
-
|
|
345
|
-
x = self.x
|
|
346
|
-
y = self.y
|
|
347
|
-
ds = np.min([np.abs(self.dx), np.abs(self.dy)])
|
|
348
|
-
p0 = (np.max(d), np.median(x), np.median(y), 5 * ds, 5 * ds, 0)
|
|
349
|
-
amax = np.max(np.abs(d))
|
|
350
|
-
xmin = np.min(x)
|
|
351
|
-
xmax = np.max(x)
|
|
352
|
-
ymin = np.min(y)
|
|
353
|
-
ymax = np.max(y)
|
|
354
|
-
smax = np.max([xmax - xmin, ymax - ymin])
|
|
355
|
-
bounds = [[-amax, xmin, ymin, ds, ds, -90],
|
|
356
|
-
[amax, xmax, ymax, smax, smax, 90]]
|
|
357
|
-
x, y = np.meshgrid(x, y)
|
|
343
|
+
z = self.data if chan is None else self.data[chan]
|
|
358
344
|
Omega = np.pi * self.beam[0] * self.beam[1] / 4 / np.log(2)
|
|
359
345
|
pixelperbeam = Omega / np.abs(self.dx * self.dy)
|
|
360
346
|
s = 'sigma is multiplied by sqrt(pixel-per-beam)' \
|
|
361
347
|
+ ' to consider the noise correlation in a beam.' \
|
|
362
348
|
+ ' This correction is relatively conservative.'
|
|
363
349
|
warnings.warn(s, UserWarning)
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
xy = popt[1:3] / 3600
|
|
373
|
-
newcenter = xy2coord(xy, coordorg=center)
|
|
350
|
+
res = gaussfit2d(xdata=self.x, ydata=self.y, zdata=z,
|
|
351
|
+
sigma=self.sigma * np.sqrt(pixelperbeam),
|
|
352
|
+
show=False, nwalkersperdim=4)
|
|
353
|
+
popt, perr = res['popt'], res['perr']
|
|
354
|
+
model = gaussian2d(np.meshgrid(self.x, self.y), *popt)
|
|
355
|
+
residual = z - model
|
|
356
|
+
if self.center is not None:
|
|
357
|
+
newcenter = xy2coord(popt[1:3] / 3600, coordorg=self.center)
|
|
374
358
|
else:
|
|
375
359
|
newcenter = None
|
|
376
|
-
return {'popt': popt, '
|
|
360
|
+
return {'popt': popt, 'perr': perr,
|
|
377
361
|
'model': model, 'residual': residual,
|
|
378
362
|
'center': newcenter}
|
|
379
363
|
|
|
@@ -468,7 +452,8 @@ class AstroData():
|
|
|
468
452
|
res = [None] * nprof
|
|
469
453
|
for i in range(nprof):
|
|
470
454
|
res[i] = gaussfit1d(xdata=v, ydata=prof[i],
|
|
471
|
-
sigma=None, show=True
|
|
455
|
+
sigma=None, show=True,
|
|
456
|
+
nwalkersperdim=8)
|
|
472
457
|
gfitres['best'] = [a['popt'][:3] for a in res]
|
|
473
458
|
gfitres['error'] = [a['perr'][:3] for a in res]
|
|
474
459
|
return v, prof, gfitres
|
|
@@ -8,6 +8,7 @@ from dynesty import DynamicNestedSampler as DNS
|
|
|
8
8
|
from multiprocessing import Pool
|
|
9
9
|
from tqdm import tqdm
|
|
10
10
|
|
|
11
|
+
from plotastrodata.matrix_utils import Mrot, dot2d
|
|
11
12
|
from plotastrodata.other_utils import close_figure
|
|
12
13
|
|
|
13
14
|
|
|
@@ -395,6 +396,47 @@ class EmceeCorner():
|
|
|
395
396
|
return {'evidence': evidence, 'error': error}
|
|
396
397
|
|
|
397
398
|
|
|
399
|
+
def gaussian1d(x: np.ndarray | float,
|
|
400
|
+
amplitude: float, xo: float, fwhm: float,
|
|
401
|
+
) -> np.ndarray:
|
|
402
|
+
"""One dimensional Gaussian function.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
x (np.ndarray): Variable of the Gaussian function.
|
|
406
|
+
amplitude (float): Peak value.
|
|
407
|
+
xo (float): Offset in the x direction.
|
|
408
|
+
fwhm (float): Full width at half maximum.
|
|
409
|
+
|
|
410
|
+
Returns:
|
|
411
|
+
g (np.ndarray): 1D numpy array.
|
|
412
|
+
"""
|
|
413
|
+
g = amplitude * np.exp2(-4 * (((x - xo) / fwhm)**2))
|
|
414
|
+
return g
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def gaussian2d(xy: np.ndarray,
|
|
418
|
+
amplitude: float, xo: float, yo: float,
|
|
419
|
+
fwhm_major: float, fwhm_minor: float, pa: float
|
|
420
|
+
) -> np.ndarray:
|
|
421
|
+
"""Two dimensional Gaussian function.
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
xy (np.ndarray): A pair of (x, y).
|
|
425
|
+
amplitude (float): Peak value.
|
|
426
|
+
xo (float): Offset in the x direction.
|
|
427
|
+
yo (float): Offset in the y direction.
|
|
428
|
+
fwhm_major (float): Full width at half maximum in the major axis (but can be shorter than the minor axis).
|
|
429
|
+
fwhm_minor (float): Full width at half maximum in the minor axis (but can be longer then the major axis).
|
|
430
|
+
pa (float): Position angle of the major axis from the +y axis to the +x axis in the unit of degree.
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
g (np.ndarray): Output array in the same shape as xy.
|
|
434
|
+
"""
|
|
435
|
+
s, t = dot2d(Mrot(-pa), [xy[1] - yo, xy[0] - xo])
|
|
436
|
+
g = amplitude * np.exp2(-4 * ((s / fwhm_major)**2 + (t / fwhm_minor)**2))
|
|
437
|
+
return g
|
|
438
|
+
|
|
439
|
+
|
|
398
440
|
def gaussfit1d(xdata: np.ndarray, ydata: np.ndarray,
|
|
399
441
|
sigma: float | np.ndarray | None,
|
|
400
442
|
show: bool = False, **kwargs) -> dict:
|
|
@@ -403,43 +445,90 @@ def gaussfit1d(xdata: np.ndarray, ydata: np.ndarray,
|
|
|
403
445
|
Args:
|
|
404
446
|
xdata (np.ndarray): ydata is compared with Gauss(xdata).
|
|
405
447
|
ydata (np.ndarray): ydata is compared with Gauss(xdata).
|
|
406
|
-
sigma (float | np.ndarray | None): Noise level of ydata. If None is given, sigma is
|
|
448
|
+
sigma (float | np.ndarray | None): Noise level of ydata. If None is given, sigma is estimated by a temporary fitting. Defaults to None.
|
|
407
449
|
show (bool, optional): True means to show the best-fit parameters and uncertainties. Defaults to False.
|
|
408
450
|
|
|
409
451
|
Returns:
|
|
410
|
-
dict:
|
|
452
|
+
dict: The keys are popt, perr, and sigma.
|
|
411
453
|
"""
|
|
412
|
-
if sigma is not None and np.shape(sigma) == ():
|
|
413
|
-
sigma = [sigma] * len(xdata)
|
|
414
454
|
xmin, xmax = np.min(xdata), np.max(xdata)
|
|
415
455
|
ymin, ymax = np.min(ydata), np.max(ydata)
|
|
456
|
+
xw = xmax - xmin
|
|
457
|
+
yw = ymax - ymin
|
|
416
458
|
dx = np.abs(xdata[1] - xdata[0])
|
|
417
|
-
bounds = [[ymin, ymax], [xmin, xmax], [dx,
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
if sigma is None:
|
|
426
|
-
def logl(p):
|
|
427
|
-
sigmdl = np.exp(p[3])
|
|
428
|
-
chi2 = np.sum(((ydata - g(xdata, p[:3])) / sigmdl)**2)
|
|
429
|
-
return -0.5 * chi2 - p[3]
|
|
430
|
-
else:
|
|
431
|
-
def logl(p):
|
|
432
|
-
chi2 = np.sum(((ydata - g(xdata, p)) / sigma)**2)
|
|
433
|
-
return -0.5 * chi2
|
|
434
|
-
|
|
435
|
-
fitter = EmceeCorner(bounds=bounds, logl=logl)
|
|
436
|
-
fitter.fit(**kwargs)
|
|
459
|
+
bounds = [[ymin - yw * 10, ymax + yw * 10], [xmin, xmax], [dx, xw]]
|
|
460
|
+
sigtmp = sigma or max(np.abs(ymin), np.abs(ymax)) * 0.01
|
|
461
|
+
for i in range(2 if sigma is None else 1):
|
|
462
|
+
fitter = EmceeCorner(bounds=bounds, model=gaussian1d,
|
|
463
|
+
sigma=sigtmp, xdata=xdata, ydata=ydata)
|
|
464
|
+
fitter.fit(**kwargs)
|
|
465
|
+
if i == 0:
|
|
466
|
+
sigtmp = np.std(ydata - gaussian1d(xdata, *fitter.popt))
|
|
437
467
|
popt = fitter.popt
|
|
438
468
|
plow = fitter.plow
|
|
439
469
|
phigh = fitter.phigh
|
|
440
470
|
perr = (phigh - plow) / 2
|
|
441
|
-
pars = 'peak, center, FWHM' + (', lnsigma' if sigma is None else '')
|
|
442
471
|
if show:
|
|
443
|
-
print(
|
|
472
|
+
print('Gauss (peak, center, FWHM):', popt)
|
|
473
|
+
print('Gauss uncertainties:', perr)
|
|
474
|
+
if sigma is None:
|
|
475
|
+
print('Estimated sigma: ', sigtmp)
|
|
476
|
+
return {'popt': popt, 'perr': perr, 'sigma': sigtmp}
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def gaussfit2d(xdata: np.ndarray, ydata: np.ndarray, zdata: np.ndarray,
|
|
480
|
+
sigma: float | np.ndarray | None,
|
|
481
|
+
show: bool = False, **kwargs) -> dict:
|
|
482
|
+
"""Gaussian fitting to a pair of 1D arrays.
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
xdata (np.ndarray): zdata is compared with Gauss(xdata, ydata).
|
|
486
|
+
ydata (np.ndarray): zdata is compared with Gauss(xdata, ydata).
|
|
487
|
+
zdata (np.ndarray): zdata is compared with Gauss(xdata, ydata).
|
|
488
|
+
sigma (float | np.ndarray | None): Noise level of ydata. If None is given, sigma is estimated by a temporary fitting. Defaults to None.
|
|
489
|
+
show (bool, optional): True means to show the best-fit parameters and uncertainties. Defaults to False.
|
|
490
|
+
|
|
491
|
+
Returns:
|
|
492
|
+
dict: The keys are popt, perr, and sigma.
|
|
493
|
+
"""
|
|
494
|
+
xmin, xmax = np.min(xdata), np.max(xdata)
|
|
495
|
+
ymin, ymax = np.min(ydata), np.max(ydata)
|
|
496
|
+
zmin, zmax = np.min(zdata), np.max(zdata)
|
|
497
|
+
xw = xmax - xmin
|
|
498
|
+
yw = ymax - ymin
|
|
499
|
+
zw = zmax - zmin
|
|
500
|
+
dx = min(np.abs(xdata[1] - xdata[0]), np.abs(ydata[1] - ydata[0]))
|
|
501
|
+
xw = max(xw, yw)
|
|
502
|
+
xy = np.meshgrid(xdata, ydata)
|
|
503
|
+
sigtmp = sigma or max(np.abs(zmin), np.abs(zmax)) * 0.01
|
|
504
|
+
|
|
505
|
+
def model(xy, a, cx, cy, wmaj, wmin, pa):
|
|
506
|
+
if wmaj < wmin:
|
|
507
|
+
return np.inf
|
|
508
|
+
else:
|
|
509
|
+
return gaussian2d(xy, a, cx, cy, wmaj, wmin, pa)
|
|
510
|
+
|
|
511
|
+
bounds = [[zmin - zw * 10, zmax + zw * 10],
|
|
512
|
+
[xmin, xmax], [ymin, ymax],
|
|
513
|
+
[dx, xw], [dx, xw], [-90, 90]]
|
|
514
|
+
for i in range(2):
|
|
515
|
+
fitter = EmceeCorner(bounds=bounds, model=model,
|
|
516
|
+
sigma=sigtmp, xdata=xy, ydata=zdata)
|
|
517
|
+
fitter.fit(**kwargs)
|
|
518
|
+
a, cx, cy, wmaj, wmin, pa = fitter.popt
|
|
519
|
+
wmax = max(wmaj, wmin)
|
|
520
|
+
bounds = [sorted([a / 2, a * 2]),
|
|
521
|
+
[cx - wmax, cx + wmax], [cy - wmax, cy + wmax],
|
|
522
|
+
[wmaj / 2, wmaj * 2], [wmin / 2, wmin * 2],
|
|
523
|
+
[pa - 45, pa + 45]]
|
|
524
|
+
if i == 0 and sigma is None:
|
|
525
|
+
sigtmp = np.std(ydata - gaussian2d(xy, *fitter.popt))
|
|
526
|
+
popt = fitter.popt
|
|
527
|
+
popt[-1] = (popt[-1] + 90) % 180 - 90
|
|
528
|
+
perr = (fitter.phigh - fitter.plow) / 2
|
|
529
|
+
if show:
|
|
530
|
+
print('Gauss (peak, center, FWHM):', popt)
|
|
444
531
|
print('Gauss uncertainties:', perr)
|
|
445
|
-
|
|
532
|
+
if sigma is None:
|
|
533
|
+
print('Estimated sigma: ', sigtmp)
|
|
534
|
+
return {'popt': popt, 'perr': perr, 'sigma': sigtmp}
|
|
@@ -2,8 +2,6 @@ import matplotlib.pyplot as plt
|
|
|
2
2
|
import numpy as np
|
|
3
3
|
from scipy.interpolate import RegularGridInterpolator as RGI
|
|
4
4
|
|
|
5
|
-
from plotastrodata.matrix_utils import Mrot, dot2d
|
|
6
|
-
|
|
7
5
|
|
|
8
6
|
def listing(*args) -> list:
|
|
9
7
|
"""Output a list of the input when the input is string or number.
|
|
@@ -176,29 +174,6 @@ def RGIxyv(v: np.ndarray, y: np.ndarray, x: np.ndarray, data: np.ndarray,
|
|
|
176
174
|
return np.squeeze([f3d(tuple(vyxnew)) for f3d in f])
|
|
177
175
|
|
|
178
176
|
|
|
179
|
-
def gaussian2d(xy: np.ndarray,
|
|
180
|
-
amplitude: float, xo: float, yo: float,
|
|
181
|
-
fwhm_major: float, fwhm_minor: float, pa: float
|
|
182
|
-
) -> np.ndarray:
|
|
183
|
-
"""Two dimensional Gaussian function.
|
|
184
|
-
|
|
185
|
-
Args:
|
|
186
|
-
xy (np.ndarray): A pair of (x, y).
|
|
187
|
-
amplitude (float): Peak value.
|
|
188
|
-
xo (float): Offset in the x direction.
|
|
189
|
-
yo (float): Offset in the y direction.
|
|
190
|
-
fwhm_major (float): Full width at half maximum in the major axis (but can be shorter than the minor axis).
|
|
191
|
-
fwhm_minor (float): Full width at half maximum in the minor axis (but can be longer then the major axis).
|
|
192
|
-
pa (float): Position angle of the major axis from the +y axis to the +x axis in the unit of degree.
|
|
193
|
-
|
|
194
|
-
Returns:
|
|
195
|
-
g (np.ndarray): 2D numpy array.
|
|
196
|
-
"""
|
|
197
|
-
s, t = dot2d(Mrot(-pa), [xy[1] - yo, xy[0] - xo])
|
|
198
|
-
g = amplitude * np.exp2(-4 * ((s / fwhm_major)**2 + (t / fwhm_minor)**2))
|
|
199
|
-
return g
|
|
200
|
-
|
|
201
|
-
|
|
202
177
|
def close_figure(fig: object, savefig: dict | str | None = None,
|
|
203
178
|
show: bool = False, tight: bool = True) -> None:
|
|
204
179
|
"""Save, show, and close the figure.
|
|
@@ -99,6 +99,39 @@ def logcbticks(vmin: float = 1e-3, vmax: float = 1e3
|
|
|
99
99
|
return ticks[cond], ticklabels[cond]
|
|
100
100
|
|
|
101
101
|
|
|
102
|
+
def _get_sec(coord: str, mode: str) -> str:
|
|
103
|
+
i_axis = 0 if mode == 'ra' else 1
|
|
104
|
+
return coord.split(' ')[i_axis].split('m')[1].strip('s')
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _get_min(coord: str, mode: str) -> str:
|
|
108
|
+
i_axis = 0 if mode == 'ra' else 1
|
|
109
|
+
s = 'h' if mode == 'ra' else 'd'
|
|
110
|
+
return coord.split(' ')[i_axis].split(s)[1].split('m')[0]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _get_hmdm(coord: str, mode: str) -> str:
|
|
114
|
+
i_axis = 0 if mode == 'ra' else 1
|
|
115
|
+
return coord.split(' ')[i_axis].split('m')[0] + 'm'
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _get_gridwidth(mode: str, rmax: float) -> tuple[float, int]:
|
|
119
|
+
# 10^1.5 / 15 ~ 2 grids for R.A.
|
|
120
|
+
# 10^0.5 ~ 3 grids for Dec.
|
|
121
|
+
scale = 1.5 if mode == 'ra' else 0.5
|
|
122
|
+
log2r = np.log10(2. * rmax)
|
|
123
|
+
x = log2r - scale
|
|
124
|
+
order = np.floor(x)
|
|
125
|
+
frac = x - order
|
|
126
|
+
if frac <= 0.33:
|
|
127
|
+
base = 1
|
|
128
|
+
elif frac <= 0.68:
|
|
129
|
+
base = 2
|
|
130
|
+
else:
|
|
131
|
+
base = 5
|
|
132
|
+
return base * 10**order, int(order)
|
|
133
|
+
|
|
134
|
+
|
|
102
135
|
@dataclass
|
|
103
136
|
class Stretcher():
|
|
104
137
|
"""Arguments and methods related to the stretch in PlotAstroData.add_color() and add_rgb().
|
|
@@ -1057,86 +1090,71 @@ class PlotAstroData(AstroFrame):
|
|
|
1057
1090
|
center = '00h00m00s 00d00m00s'
|
|
1058
1091
|
if len(csplit := center.split()) == 3:
|
|
1059
1092
|
center = f'{csplit[1]} {csplit[2]}'
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
def get_min(x, i):
|
|
1065
|
-
s = 'h' if i == 0 else 'd'
|
|
1066
|
-
return x.split(' ')[i].split(s)[1].split('m')[0]
|
|
1067
|
-
|
|
1068
|
-
def get_hmdm(x, i):
|
|
1069
|
-
return x.split(' ')[i].split('m')[0] + 'm'
|
|
1070
|
-
|
|
1071
|
-
on_min_scale = self.rmax >= 60.0
|
|
1072
|
-
if on_min_scale:
|
|
1073
|
-
ra_s = np.floor(float(get_sec(center, 0)) / 5) * 5
|
|
1093
|
+
if on_min_scale := (self.rmax >= 60.0):
|
|
1094
|
+
# On a 5-second grid.
|
|
1095
|
+
ra_s = np.floor(float(_get_sec(center, 0)) / 5) * 5
|
|
1074
1096
|
dec_s = 0.0
|
|
1075
|
-
ra =
|
|
1076
|
-
dec =
|
|
1097
|
+
ra = _get_hmdm(center, 'ra') + f'{ra_s:.1f}s'
|
|
1098
|
+
dec = _get_hmdm(center, 'dec') + f'{dec_s:.1f}s'
|
|
1077
1099
|
center = f'{ra} {dec}'
|
|
1078
1100
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
def makegrid(second, mode):
|
|
1084
|
-
second = float(second)
|
|
1085
|
-
is_dec = mode == 'dec'
|
|
1086
|
-
scale = 0.5 if is_dec else 1.5
|
|
1087
|
-
factor = 1 if is_dec else 15 * np.cos(dec)
|
|
1088
|
-
no_sec = on_min_scale and is_dec
|
|
1089
|
-
if no_sec:
|
|
1090
|
-
unit = r'$^{\prime}$' if is_dec else r'$^\mathrm{m}$'
|
|
1091
|
-
else:
|
|
1092
|
-
unit = r'$^{\prime\prime}$' if is_dec else r'$^\mathrm{s}$'
|
|
1093
|
-
unit = r'.$\hspace{-0.4}$' + unit
|
|
1094
|
-
dorder = log2r - scale - (order := np.floor(log2r - scale))
|
|
1095
|
-
if 0.00 < dorder <= 0.33:
|
|
1096
|
-
g = 1
|
|
1097
|
-
elif 0.33 < dorder <= 0.68:
|
|
1098
|
-
g = 2
|
|
1099
|
-
elif 0.68 < dorder <= 1.00:
|
|
1100
|
-
g = 5
|
|
1101
|
-
g *= 10**order
|
|
1102
|
-
decimals = max(-int(order), -1)
|
|
1103
|
-
rounded = round(second, decimals)
|
|
1104
|
-
lastdigit = round(rounded // 10**(-decimals-1) % 100 / 10) % 10
|
|
1105
|
-
rounded -= lastdigit * 10**(-decimals) % g
|
|
1106
|
-
ticks = (n*g - second + rounded) * factor
|
|
1107
|
-
ticksminor = np.linspace(ticks[0], ticks[-1], 6*nticksminor + 1)
|
|
1108
|
-
decimals = max(decimals, 0)
|
|
1109
|
-
decimals = f'{decimals:d}'
|
|
1101
|
+
def get_tickvalues(ticks: np.ndarray, mode: str, no_sec: bool
|
|
1102
|
+
) -> np.ndarray:
|
|
1103
|
+
xy = [np.zeros_like(ticks), ticks / 3600.]
|
|
1110
1104
|
if mode == 'ra':
|
|
1111
|
-
xy
|
|
1112
|
-
else:
|
|
1113
|
-
xy, i = [ticks * 0, ticks / 3600.], 1
|
|
1105
|
+
xy.reverse()
|
|
1114
1106
|
tickvalues = xy2coord(xy, center)
|
|
1115
|
-
|
|
1116
|
-
tickvalues =
|
|
1117
|
-
tickvalues = np.
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1107
|
+
getter = _get_min if no_sec else _get_sec
|
|
1108
|
+
tickvalues = [getter(t, mode) for t in tickvalues] # str
|
|
1109
|
+
tickvalues = np.array(tickvalues, dtype=float)
|
|
1110
|
+
# 7-digit precision for practical use.
|
|
1111
|
+
tickvalues = np.round(tickvalues, 7)
|
|
1112
|
+
return tickvalues
|
|
1113
|
+
|
|
1114
|
+
units = {'ra': {'h': r'$^\mathrm{h}$',
|
|
1115
|
+
'm': r'$^\mathrm{m}$',
|
|
1116
|
+
's': r'.$\hspace{-0.4}^\mathrm{s}$'},
|
|
1117
|
+
'dec': {'d': r'$^{\circ}$',
|
|
1118
|
+
'm': r'$^{\prime}$',
|
|
1119
|
+
's': r'.$\hspace{-0.4}^{\prime\prime}$'}}
|
|
1120
|
+
cos_dec = np.cos(np.radians(coord2xy(center)[1]))
|
|
1121
|
+
intgrid = np.array([-3, -2, -1, 0, 1, 2, 3])
|
|
1122
|
+
i_mid = (len(intgrid) - 1) // 2
|
|
1123
|
+
|
|
1124
|
+
def makegrid(mode: str):
|
|
1125
|
+
second = float(_get_sec(center, mode))
|
|
1126
|
+
no_sec = on_min_scale and (mode == 'dec')
|
|
1127
|
+
# gridwidth is a float like 2 x 10^order (arcsec).
|
|
1128
|
+
gridwidth, order = _get_gridwidth(mode, self.rmax)
|
|
1129
|
+
# ndigits = -1 is the largest case for 10", 20", ...
|
|
1130
|
+
decimals = str(max(-order, 0))
|
|
1131
|
+
rounded = round(second, ndigits=max(-order, -1))
|
|
1132
|
+
# Get a grid point closest to the input second.
|
|
1133
|
+
rounded = round(rounded / gridwidth) * gridwidth
|
|
1134
|
+
factor = 15 * cos_dec if mode == 'ra' else 1
|
|
1135
|
+
ticks = (intgrid * gridwidth - second + rounded) * factor
|
|
1136
|
+
ticksminor = np.linspace(ticks[0], ticks[-1], 6*nticksminor + 1)
|
|
1137
|
+
tickvalues = get_tickvalues(ticks, mode, no_sec)
|
|
1138
|
+
whole, frac = np.divmod(tickvalues, 1)
|
|
1139
|
+
u = units[mode]['m' if no_sec else 's']
|
|
1140
|
+
ticklabels = [f'{int(i):02d}{u}' + f'{j:.{decimals}f}'[2:]
|
|
1141
|
+
for i, j in zip(whole % 60, frac)]
|
|
1121
1142
|
return ticks, ticksminor, ticklabels
|
|
1122
1143
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
xticks
|
|
1126
|
-
|
|
1127
|
-
ra_hm = get_hmdm(xy2coord([xticks[3] / 3600., 0], center), 0)
|
|
1128
|
-
dec_dm = get_hmdm(xy2coord([0, yticks[3] / 3600.], center), 1)
|
|
1144
|
+
xticks, xticksminor, xticklabels = makegrid('ra')
|
|
1145
|
+
yticks, yticksminor, yticklabels = makegrid('dec')
|
|
1146
|
+
ra_hm = _get_hmdm(xy2coord([xticks[i_mid] / 3600., 0], center), 'ra')
|
|
1147
|
+
dec_dm = _get_hmdm(xy2coord([0, yticks[i_mid] / 3600.], center), 'dec')
|
|
1129
1148
|
if on_min_scale:
|
|
1130
1149
|
dec_dm = dec_dm.split('d')[0] + 'd'
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
yticklabels, xticksminor, yticksminor, grid)
|
|
1150
|
+
ra_hm = ra_hm.translate(str.maketrans(units['ra']))
|
|
1151
|
+
dec_dm = dec_dm.translate(str.maketrans(units['dec']))
|
|
1152
|
+
xticklabels[i_mid] = ra_hm + xticklabels[i_mid]
|
|
1153
|
+
yticklabels[i_mid] = dec_dm + '\n' + yticklabels[i_mid]
|
|
1154
|
+
pa2 = PlotAxes2D(True, None, 'linear', 'linear',
|
|
1155
|
+
self.Xlim, self.Ylim, xlabel, ylabel,
|
|
1156
|
+
xticks, yticks, xticklabels, yticklabels,
|
|
1157
|
+
xticksminor, yticksminor, grid)
|
|
1140
1158
|
self._set_axis_shared(pa2=pa2, title=title)
|
|
1141
1159
|
|
|
1142
1160
|
def savefig(self, filename: str | None = None,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plotastrodata
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.17
|
|
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
|