plotastrodata 1.9.21__tar.gz → 1.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {plotastrodata-1.9.21/plotastrodata.egg-info → plotastrodata-1.10.0}/PKG-INFO +5 -3
  2. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/README.md +2 -1
  3. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata/__init__.py +1 -1
  4. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata/analysis_utils.py +4 -3
  5. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata/plot_utils.py +111 -38
  6. {plotastrodata-1.9.21 → plotastrodata-1.10.0/plotastrodata.egg-info}/PKG-INFO +5 -3
  7. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata.egg-info/requires.txt +2 -1
  8. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/setup.cfg +2 -1
  9. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/LICENSE +0 -0
  10. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/MANIFEST.in +0 -0
  11. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata/const_utils.py +0 -0
  12. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata/coord_utils.py +0 -0
  13. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata/ext_utils.py +0 -0
  14. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata/fft_utils.py +0 -0
  15. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata/fits_utils.py +0 -0
  16. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata/fitting_utils.py +0 -0
  17. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata/los_utils.py +0 -0
  18. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata/matrix_utils.py +0 -0
  19. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata/noise_utils.py +0 -0
  20. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata/other_utils.py +0 -0
  21. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata.egg-info/SOURCES.txt +0 -0
  22. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata.egg-info/dependency_links.txt +0 -0
  23. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata.egg-info/not-zip-safe +0 -0
  24. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/plotastrodata.egg-info/top_level.txt +0 -0
  25. {plotastrodata-1.9.21 → plotastrodata-1.10.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plotastrodata
3
- Version: 1.9.21
3
+ Version: 1.10.0
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
@@ -14,12 +14,13 @@ License-File: LICENSE
14
14
  Requires-Dist: astropy>=7.2
15
15
  Requires-Dist: corner
16
16
  Requires-Dist: dynesty
17
- Requires-Dist: emcee>=3.0
17
+ Requires-Dist: emcee>=3.1.5
18
18
  Requires-Dist: matplotlib
19
19
  Requires-Dist: numpy>=2.0
20
20
  Requires-Dist: pillow
21
21
  Requires-Dist: plotly
22
22
  Requires-Dist: ptemcee
23
+ Requires-Dist: pydantic>=2
23
24
  Requires-Dist: scikit-image
24
25
  Requires-Dist: scipy
25
26
  Requires-Dist: tqdm
@@ -84,13 +85,14 @@ plotastrodata can do the following things.
84
85
  * astropy >= 7.2
85
86
  * corner (only for fitting)
86
87
  * dynesty (only for fitting)
87
- * emcee >= 3.0 (only for fitting)
88
+ * emcee >= 3.1.5 (only for fitting)
88
89
  * ffmpeg (only for movie)
89
90
  * matplotlib
90
91
  * numpy >= 2.0
91
92
  * pillow (only for RGB figures)
92
93
  * plotly (only for html cube)
93
94
  * ptemcee (only for fitting)
95
+ * pydantic >= 2
94
96
  * scikit-image (only for html cube)
95
97
  * scipy
96
98
  * tqdm (only for fitting)
@@ -56,13 +56,14 @@ plotastrodata can do the following things.
56
56
  * astropy >= 7.2
57
57
  * corner (only for fitting)
58
58
  * dynesty (only for fitting)
59
- * emcee >= 3.0 (only for fitting)
59
+ * emcee >= 3.1.5 (only for fitting)
60
60
  * ffmpeg (only for movie)
61
61
  * matplotlib
62
62
  * numpy >= 2.0
63
63
  * pillow (only for RGB figures)
64
64
  * plotly (only for html cube)
65
65
  * ptemcee (only for fitting)
66
+ * pydantic >= 2
66
67
  * scikit-image (only for html cube)
67
68
  * scipy
68
69
  * tqdm (only for fitting)
@@ -1,4 +1,4 @@
1
1
  import warnings
2
2
 
3
3
  warnings.simplefilter('ignore', FutureWarning)
4
- __version__ = '1.9.21'
4
+ __version__ = '1.10.0'
@@ -3,7 +3,8 @@ import warnings
3
3
  from dataclasses import dataclass
4
4
  from scipy.interpolate import RegularGridInterpolator as RGI
5
5
  from scipy.signal import convolve
6
- from typing import Any, Callable
6
+ from pydantic.dataclasses import dataclass as pydantic_dataclass
7
+ from typing import Any, Callable, Literal
7
8
 
8
9
  from plotastrodata import const_utils as cu
9
10
  from plotastrodata.coord_utils import coord2xy, rel2abs, xy2coord
@@ -621,7 +622,7 @@ ASTRODATA_ARGS = ['fitsimage', 'data', 'Tb', 'sigma', 'center', 'restfreq',
621
622
  'beam_org', 'fitsheader', 'pv', 'pvpa']
622
623
 
623
624
 
624
- @dataclass
625
+ @pydantic_dataclass
625
626
  class AstroFrame():
626
627
  """Parameter set to limit and reshape the data in the AstroData format.
627
628
 
@@ -662,7 +663,7 @@ class AstroFrame():
662
663
  yflip: bool = False
663
664
  swapxy: bool = False
664
665
  pv: bool = False
665
- quadrants: str | None = None
666
+ quadrants: Literal['13', '24'] | None = None
666
667
 
667
668
  def __post_init__(self) -> None:
668
669
  self.xdir = -1 if self.xflip else 1
@@ -1,9 +1,10 @@
1
1
  import matplotlib as mpl
2
2
  import matplotlib.pyplot as plt
3
3
  import numpy as np
4
- from dataclasses import dataclass
5
4
  from matplotlib.patches import Ellipse, Rectangle
6
- from typing import Any, TypeVar, Callable
5
+ from pydantic import Field, field_validator
6
+ from pydantic.dataclasses import dataclass as pydantic_dataclass
7
+ from typing import Any, Callable, Literal, TypeVar
7
8
 
8
9
  from plotastrodata.analysis_utils import AstroData, AstroFrame
9
10
  from plotastrodata.coord_utils import (coord2xy, xy2coord,
@@ -17,6 +18,13 @@ from plotastrodata.other_utils import (close_figure, listing,
17
18
  plt.ioff() # force to turn off interactive mode
18
19
 
19
20
  T = TypeVar('T')
21
+ Stretch = Literal['linear', 'log', 'asinh', 'power']
22
+ AxisScale = Literal['linear', 'log', 'symlog', 'asinh', 'logit']
23
+ FloatOrList = float | list[float]
24
+ OptionalFloatOrList = float | list[float | None] | None
25
+ BeamTriple = list[float | None]
26
+ BeamValue = BeamTriple | list[BeamTriple]
27
+ BeamPosition = list[float] | list[list[float] | None] | None
20
28
 
21
29
 
22
30
  def set_rcparams(fontsize: int = 18, nancolor: str = 'w',
@@ -187,24 +195,24 @@ def _get_vskipfill(nv: int, v_org: np.ndarray, vskip: int,
187
195
  return vskipfill
188
196
 
189
197
 
190
- @dataclass
198
+ @pydantic_dataclass
191
199
  class Stretcher():
192
200
  """Arguments and methods related to the stretch in PlotAstroData.add_color() and add_rgb().
193
201
 
194
202
  Args:
195
203
  stretch (str, optional): 'log', 'asinh', 'power', or 'linear'. Any other means 'linear'. 'log' means the mapped data are logarithmic. 'asinh' means the mapped data are arc sin hyperbolic. 'power' means the mapped data are power-law (see also stretchpower). Defaults to 'linear'.
196
- stretchscale (float, optional): The output is asinh(data / stretchscale). Defaults to None.
197
- stretchpower (float, optional): The output is data**stretchpower / stretchpower. 1 means the linear scale, while 0 means the logarithmic scale. Defaults to 0.5.
198
- vmin (float, optional): The minimum value for Axes.pcolormesh() of matplotlib. Defaults to None.
199
- vmax (float, optional): The maximum value for Axes.pcolormesh() of matplotlib. Defaults to None.
200
- sigma (float, optional): Noise level. Defaults to 0.
204
+ stretchscale (float or list, optional): The output is asinh(data / stretchscale). Defaults to None.
205
+ stretchpower (float or list, optional): The output is data**stretchpower / stretchpower. 1 means the linear scale, while 0 means the logarithmic scale. Defaults to 0.5.
206
+ vmin (float or list, optional): The minimum value for Axes.pcolormesh() of matplotlib. Defaults to None.
207
+ vmax (float or list, optional): The maximum value for Axes.pcolormesh() of matplotlib. Defaults to None.
208
+ sigma (float, list, or None, optional): Noise level. None means no noise level is used. Defaults to 0.
201
209
  """
202
- stretch: str = 'linear'
203
- stretchscale: float | None = None
204
- stretchpower: float = 0.5
205
- vmin: float | None = None
206
- vmax: float | None = None
207
- sigma: float = 0
210
+ stretch: Stretch | list[Stretch] = 'linear'
211
+ stretchscale: OptionalFloatOrList = None
212
+ stretchpower: FloatOrList = 0.5
213
+ vmin: OptionalFloatOrList = None
214
+ vmax: OptionalFloatOrList = None
215
+ sigma: OptionalFloatOrList = 0
208
216
 
209
217
  def __post_init__(self) -> None:
210
218
  self.n = 1 if isinstance(self.stretch, str) else len(self.stretch)
@@ -301,27 +309,88 @@ class Stretcher():
301
309
  return dataout, vminout, vmaxout
302
310
 
303
311
 
312
+ @pydantic_dataclass
304
313
  class Beam():
305
314
  """Arguments for PlotAstroData.add_beam().
306
315
 
307
316
  Args:
308
317
  show_beam (bool, optional): Defaults to True.
309
- beam (list, optional): [bmaj, bmin, bpa]. This may be a list of list. Defaults to [None, None, None].
310
- beamcolor (str, optional): matplotlib color. This may be a list of str. Defaults to 'gray'.
311
- beampos (list, optional): Relative position. This may be a list of list or a list of None. Defaults to None.
312
- beam_kwargs (dict, optional): Additional arguments for matplotlib.patches. Defaults to {}.
318
+ beam (list or np.ndarray, optional): One ``[bmaj, bmin, bpa]`` triple or a list of triples. ``bmaj`` and ``bmin`` must be positive; ``bpa`` is in degrees. ``[None, None, None]`` means no beam is available. Partial triples are not supported. Defaults to ``[None, None, None]``.
319
+ beamcolor (str or list of str, optional): Matplotlib color. One color is used for every beam; when a list is given, provide one color per beam. Defaults to ``'gray'``.
320
+ beampos (list or list of list, optional): One relative ``[x, y]`` position is used for every beam; when a list of positions is given, provide one position per beam. Each coordinate must be from 0 (left or bottom) to 1 (right or top). None selects the automatic position. Defaults to None.
321
+ beam_kwargs (dict, optional): Additional Matplotlib patch arguments. Defaults to {}.
313
322
  """
314
- def __init__(self,
315
- show_beam: bool = True,
316
- beam: list[float | None] = [None] * 3,
317
- beamcolor: str = 'gray',
318
- beampos: list[float] | None = None,
319
- beam_kwargs: dict = {}) -> None:
320
- self.show_beam = show_beam
321
- self.beam = beam
322
- self.beamcolor = beamcolor
323
- self.beampos = beampos
324
- self.beam_kwargs = beam_kwargs
323
+ show_beam: bool = True
324
+ beam: BeamValue = Field(default_factory=lambda: [None] * 3)
325
+ beamcolor: str | list[str] = 'gray'
326
+ beampos: BeamPosition = None
327
+ beam_kwargs: dict[str, Any] = Field(default_factory=dict)
328
+
329
+ @field_validator('beam', mode='before')
330
+ @classmethod
331
+ def _convert_beam_arrays(cls, value: Any) -> Any:
332
+ """Convert documented NumPy beam inputs to the canonical list form.
333
+
334
+ A single NumPy triple becomes a list, and NumPy triples inside a
335
+ list of beams are converted individually. This keeps subsequent
336
+ validation and plotting code independent of the input container.
337
+ """
338
+ if isinstance(value, np.ndarray):
339
+ value = value.tolist()
340
+ if isinstance(value, list):
341
+ return [item.tolist() if isinstance(item, np.ndarray) else item
342
+ for item in value]
343
+ return value
344
+
345
+ @field_validator('beam')
346
+ @classmethod
347
+ def _validate_beam(cls, value: BeamValue) -> BeamValue:
348
+ beams = value if value and isinstance(value[0], list) else [value]
349
+ for beam in beams:
350
+ if len(beam) != 3:
351
+ raise ValueError('Each beam must contain [bmaj, bmin, bpa].')
352
+ if all(component is None for component in beam):
353
+ continue
354
+ if any(component is None for component in beam):
355
+ raise ValueError('A beam must be complete or all None.')
356
+ if not all(np.isfinite(component) for component in beam):
357
+ raise ValueError('Beam values must be finite.')
358
+ if beam[0] <= 0 or beam[1] <= 0:
359
+ raise ValueError('bmaj and bmin must be positive.')
360
+ return value
361
+
362
+ @field_validator('beampos')
363
+ @classmethod
364
+ def _validate_beampos(cls, value: BeamPosition) -> BeamPosition:
365
+ if value is None:
366
+ return value
367
+ positions = [value] if cls._is_position(value) else value
368
+ for position in positions:
369
+ if position is None:
370
+ continue
371
+ if len(position) != 2:
372
+ raise ValueError('Each beam position must contain [x, y].')
373
+ if not all(0 <= coordinate <= 1 for coordinate in position):
374
+ raise ValueError('Beam positions must be between 0 and 1.')
375
+ return value
376
+
377
+ @staticmethod
378
+ def _is_position(value: Any) -> bool:
379
+ return (isinstance(value, list) and len(value) == 2
380
+ and all(isinstance(coordinate, float)
381
+ for coordinate in value))
382
+
383
+ def validate_display(self) -> None:
384
+ """Validate color and position lists against the number of beams."""
385
+ multi = self.beam and isinstance(self.beam[0], list)
386
+ beams = self.beam if multi else [self.beam]
387
+ nbeams = len(beams)
388
+ if isinstance(self.beamcolor, list) and len(self.beamcolor) != nbeams:
389
+ raise ValueError('Provide one beamcolor for each beam.')
390
+ if (self.beampos is not None
391
+ and not self._is_position(self.beampos)
392
+ and len(self.beampos) != nbeams):
393
+ raise ValueError('Provide one beampos for each beam.')
325
394
 
326
395
  def todict(self) -> dict[str, Any]:
327
396
  """Return beam display settings as a dictionary.
@@ -337,13 +406,13 @@ class Beam():
337
406
  return tmp
338
407
 
339
408
 
340
- @dataclass
409
+ @pydantic_dataclass
341
410
  class PlotAxes2D():
342
411
  """Use Axes.set_* to adjust x and y axes.
343
412
 
344
413
  Args:
345
414
  samexy (bool, optional): True supports same ticks between x and y. Defaults to True.
346
- loglog (float, optional): If a float is given, plot on a log-log plane, and xim=(xmax / loglog, xmax) and so does ylim. Defaults to None.
415
+ loglog (float, optional): A positive value plots on a log-log plane, with xim=(xmax / loglog, xmax) and similarly for ylim. Defaults to None.
347
416
  xscale (str, optional): ``'log'`` labels decade ticks and ticks near the limits; other intermediate ticks are minor and unlabeled. Defaults to ``'linear'``.
348
417
  yscale (str, optional): ``'log'`` labels decade ticks and ticks near the limits; other intermediate ticks are minor and unlabeled. Defaults to ``'linear'``.
349
418
  xlim (list, optional): Defaults to None.
@@ -360,9 +429,9 @@ class PlotAxes2D():
360
429
  aspect (dict or float, optional): Defaults to None.
361
430
  """
362
431
  samexy: bool = True
363
- loglog: float | None = None
364
- xscale: str = 'linear'
365
- yscale: str = 'linear'
432
+ loglog: float | None = Field(default=None, gt=0)
433
+ xscale: AxisScale = 'linear'
434
+ yscale: AxisScale = 'linear'
366
435
  xlim: list | None = None
367
436
  ylim: list | None = None
368
437
  xlabel: str | None = None
@@ -618,9 +687,12 @@ class PlotAstroData(AstroFrame):
618
687
  self.sigma = d.sigma
619
688
  singlepix = d.dx is None or d.dy is None
620
689
  if len(d.beam) == 4:
621
- b.beam = self.beam = next(b for b in d.beam if None not in b)
690
+ beam = next((onebeam for onebeam in d.beam
691
+ if all(a is not None for a in onebeam)),
692
+ [None, None, None],)
622
693
  else:
623
- b.beam = self.beam = d.beam
694
+ beam = d.beam
695
+ b.beam = self.beam = Beam(beam=beam).beam
624
696
  self.add_beam(**b.todict())
625
697
  return (d.data, d.x, d.y, d.v, d.sigma, d.bunit,
626
698
  self._kw, singlepix)
@@ -678,7 +750,7 @@ class PlotAstroData(AstroFrame):
678
750
  def add_beam(self, **kwargs: Any) -> None:
679
751
  """Use add_region() to plot the beam.
680
752
 
681
- kwargs may include the arguments of Beam, except for beam_kwargs, to specify the beam appearance. Those arguments may be a list of each format.
753
+ kwargs may include the arguments of Beam, except for beam_kwargs, to specify the beam appearance. A single ``beamcolor`` or ``beampos`` value applies to every beam; per-beam lists must provide one value for each beam. A ``[None, None, None]`` beam is skipped.
682
754
 
683
755
  Default keyword values:
684
756
  Beam patch: ``facecolor=beamcolor`` and ``edgecolor=None``. Other keyword arguments override these values and are passed to ``add_region``.
@@ -687,6 +759,7 @@ class PlotAstroData(AstroFrame):
687
759
  show_beam, beamcolor, beampos = b.show_beam, b.beamcolor, b.beampos
688
760
  beam = b.beam
689
761
  del kwargs['beam']
762
+ b.validate_display()
690
763
  if not show_beam:
691
764
  return
692
765
 
@@ -695,7 +768,7 @@ class PlotAstroData(AstroFrame):
695
768
  blist = [beam] if np.ndim(beam) == 1 else beam
696
769
  n = len(blist)
697
770
  bclist = beamcolor if isinstance(beamcolor, list) else [beamcolor] * n
698
- islist = beampos == [None] * 3 or np.ndim(beampos) == 2
771
+ islist = beampos is not None and not b._is_position(beampos)
699
772
  bplist = beampos if islist else [beampos] * n
700
773
  for (bmaj, bmin, bpa), bc, bp in zip(blist, bclist, bplist):
701
774
  if None in [bmaj, bmin, bpa]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plotastrodata
3
- Version: 1.9.21
3
+ Version: 1.10.0
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
@@ -14,12 +14,13 @@ License-File: LICENSE
14
14
  Requires-Dist: astropy>=7.2
15
15
  Requires-Dist: corner
16
16
  Requires-Dist: dynesty
17
- Requires-Dist: emcee>=3.0
17
+ Requires-Dist: emcee>=3.1.5
18
18
  Requires-Dist: matplotlib
19
19
  Requires-Dist: numpy>=2.0
20
20
  Requires-Dist: pillow
21
21
  Requires-Dist: plotly
22
22
  Requires-Dist: ptemcee
23
+ Requires-Dist: pydantic>=2
23
24
  Requires-Dist: scikit-image
24
25
  Requires-Dist: scipy
25
26
  Requires-Dist: tqdm
@@ -84,13 +85,14 @@ plotastrodata can do the following things.
84
85
  * astropy >= 7.2
85
86
  * corner (only for fitting)
86
87
  * dynesty (only for fitting)
87
- * emcee >= 3.0 (only for fitting)
88
+ * emcee >= 3.1.5 (only for fitting)
88
89
  * ffmpeg (only for movie)
89
90
  * matplotlib
90
91
  * numpy >= 2.0
91
92
  * pillow (only for RGB figures)
92
93
  * plotly (only for html cube)
93
94
  * ptemcee (only for fitting)
95
+ * pydantic >= 2
94
96
  * scikit-image (only for html cube)
95
97
  * scipy
96
98
  * tqdm (only for fitting)
@@ -1,12 +1,13 @@
1
1
  astropy>=7.2
2
2
  corner
3
3
  dynesty
4
- emcee>=3.0
4
+ emcee>=3.1.5
5
5
  matplotlib
6
6
  numpy>=2.0
7
7
  pillow
8
8
  plotly
9
9
  ptemcee
10
+ pydantic>=2
10
11
  scikit-image
11
12
  scipy
12
13
  tqdm
@@ -19,12 +19,13 @@ install_requires =
19
19
  astropy >= 7.2
20
20
  corner
21
21
  dynesty
22
- emcee >= 3.0
22
+ emcee >= 3.1.5
23
23
  matplotlib
24
24
  numpy >= 2.0
25
25
  pillow
26
26
  plotly
27
27
  ptemcee
28
+ pydantic >= 2
28
29
  scikit-image
29
30
  scipy
30
31
  tqdm
File without changes
File without changes