starplot 0.14.0__py2.py3-none-any.whl → 0.15.1__py2.py3-none-any.whl

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.

Potentially problematic release.


This version of starplot might be problematic. Click here for more details.

Files changed (63) hide show
  1. starplot/__init__.py +3 -1
  2. starplot/base.py +149 -37
  3. starplot/cli.py +33 -0
  4. starplot/data/__init__.py +6 -24
  5. starplot/data/bigsky.py +58 -40
  6. starplot/data/constellation_lines.py +827 -0
  7. starplot/data/constellation_stars.py +1501 -0
  8. starplot/data/constellations.py +43 -32
  9. starplot/data/db.py +17 -0
  10. starplot/data/dsos.py +24 -141
  11. starplot/data/library/{stars.bigsky.mag11.parquet → bigsky.0.4.0.stars.mag11.parquet} +0 -0
  12. starplot/data/library/sky.db +0 -0
  13. starplot/data/stars.py +45 -24
  14. starplot/geod.py +0 -6
  15. starplot/geometry.py +105 -6
  16. starplot/horizon.py +118 -107
  17. starplot/map.py +45 -96
  18. starplot/mixins.py +75 -14
  19. starplot/models/__init__.py +1 -1
  20. starplot/models/base.py +10 -128
  21. starplot/models/constellation.py +55 -32
  22. starplot/models/dso.py +132 -67
  23. starplot/models/moon.py +57 -78
  24. starplot/models/planet.py +44 -69
  25. starplot/models/star.py +91 -60
  26. starplot/models/sun.py +32 -53
  27. starplot/optic.py +14 -17
  28. starplot/plotters/constellations.py +81 -78
  29. starplot/plotters/dsos.py +49 -68
  30. starplot/plotters/experimental.py +1 -1
  31. starplot/plotters/milkyway.py +18 -20
  32. starplot/plotters/stars.py +91 -116
  33. starplot/profile.py +16 -0
  34. starplot/settings.py +26 -0
  35. starplot/styles/__init__.py +2 -0
  36. starplot/styles/base.py +7 -17
  37. starplot/styles/ext/blue_gold.yml +135 -0
  38. starplot/styles/ext/blue_light.yml +5 -4
  39. starplot/styles/ext/blue_medium.yml +11 -7
  40. starplot/styles/extensions.py +1 -0
  41. starplot/warnings.py +5 -0
  42. {starplot-0.14.0.dist-info → starplot-0.15.1.dist-info}/METADATA +11 -11
  43. {starplot-0.14.0.dist-info → starplot-0.15.1.dist-info}/RECORD +46 -54
  44. starplot-0.15.1.dist-info/entry_points.txt +3 -0
  45. starplot/data/bayer.py +0 -3499
  46. starplot/data/flamsteed.py +0 -2682
  47. starplot/data/library/constellation_borders_inv.gpkg +0 -0
  48. starplot/data/library/constellation_lines_hips.json +0 -709
  49. starplot/data/library/constellation_lines_inv.gpkg +0 -0
  50. starplot/data/library/constellations.gpkg +0 -0
  51. starplot/data/library/constellations_hip.fab +0 -88
  52. starplot/data/library/milkyway.gpkg +0 -0
  53. starplot/data/library/milkyway_inv.gpkg +0 -0
  54. starplot/data/library/ongc.gpkg.zip +0 -0
  55. starplot/data/library/stars.hipparcos.parquet +0 -0
  56. starplot/data/messier.py +0 -111
  57. starplot/data/prep/__init__.py +0 -0
  58. starplot/data/prep/constellations.py +0 -108
  59. starplot/data/prep/dsos.py +0 -299
  60. starplot/data/prep/utils.py +0 -16
  61. starplot/models/geometry.py +0 -44
  62. {starplot-0.14.0.dist-info → starplot-0.15.1.dist-info}/LICENSE +0 -0
  63. {starplot-0.14.0.dist-info → starplot-0.15.1.dist-info}/WHEEL +0 -0
starplot/models/star.py CHANGED
@@ -1,31 +1,12 @@
1
- from typing import Optional
1
+ import math
2
+ from typing import Optional, Union, Iterator
2
3
 
3
4
  import numpy as np
4
5
  from shapely import Point
6
+ from ibis import _
5
7
 
6
- from starplot.models.base import SkyObject, SkyObjectManager
7
- from starplot.data.stars import StarCatalog, STAR_NAMES, load as _load_stars
8
-
9
-
10
- class StarManager(SkyObjectManager):
11
- @classmethod
12
- def all(cls, catalog: StarCatalog = StarCatalog.HIPPARCOS):
13
- all_stars = _load_stars(catalog)
14
-
15
- # TODO : add datetime kwarg
16
-
17
- for s in all_stars.itertuples():
18
- yield from_tuple(s, catalog)
19
-
20
- @classmethod
21
- def find(cls, where, catalog: StarCatalog = StarCatalog.HIPPARCOS):
22
- all_objects = cls.all(catalog)
23
- return super().find(where=where, all_objects=all_objects)
24
-
25
- @classmethod
26
- def get(cls, catalog: StarCatalog = StarCatalog.HIPPARCOS, **kwargs):
27
- all_objects = cls.all(catalog)
28
- return super().get(all_objects=all_objects, **kwargs)
8
+ from starplot.models.base import SkyObject
9
+ from starplot.data.stars import StarCatalog, load as _load_stars
29
10
 
30
11
 
31
12
  class Star(SkyObject):
@@ -33,8 +14,6 @@ class Star(SkyObject):
33
14
  Star model.
34
15
  """
35
16
 
36
- _manager = StarManager
37
-
38
17
  magnitude: float
39
18
  """Magnitude"""
40
19
 
@@ -53,8 +32,14 @@ class Star(SkyObject):
53
32
  name: Optional[str] = None
54
33
  """Name, if available"""
55
34
 
35
+ bayer: Optional[str] = None
36
+ """Bayer designation, if available"""
37
+
38
+ flamsteed: Optional[int] = None
39
+ """Flamsteed number, if available"""
40
+
56
41
  geometry: Point = None
57
- """Shapely Point of the star's position. Right ascension coordinates are in 24H format."""
42
+ """Shapely Point of the star's position. Right ascension coordinates are in degrees (0...360)."""
58
43
 
59
44
  def __init__(
60
45
  self,
@@ -67,69 +52,115 @@ class Star(SkyObject):
67
52
  tyc: str = None,
68
53
  ccdm: str = None,
69
54
  geometry: Point = None,
55
+ constellation_id: str = None,
56
+ bayer: str = None,
57
+ flamsteed: int = None,
70
58
  ) -> None:
71
- super().__init__(ra, dec)
59
+ super().__init__(ra, dec, constellation_id)
72
60
  self.magnitude = magnitude
73
61
  self.bv = bv
74
62
  self.hip = hip if hip is not None and np.isfinite(hip) else None
75
63
  self.name = name
76
64
  self.tyc = tyc
77
65
  self.ccdm = ccdm
78
- self.geometry = Point([ra, dec])
66
+ self.geometry = geometry
67
+
68
+ if bayer:
69
+ self.bayer = bayer
70
+
71
+ if flamsteed and not math.isnan(flamsteed):
72
+ self.flamsteed = int(flamsteed)
79
73
 
80
74
  def __repr__(self) -> str:
81
75
  return f"Star(hip={self.hip}, tyc={self.tyc}, magnitude={self.magnitude}, ra={self.ra}, dec={self.dec})"
82
76
 
83
77
  @classmethod
84
- def get(**kwargs) -> "Star":
78
+ def all(cls, catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11) -> Iterator["Star"]:
79
+ df = _load_stars(catalog=catalog).to_pandas()
80
+
81
+ for s in df.itertuples():
82
+ yield from_tuple(s)
83
+
84
+ @classmethod
85
+ def get(
86
+ cls, catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11, **kwargs
87
+ ) -> Union["Star", None]:
85
88
  """
86
- Get a Star, by matching its attributes.
89
+ Get a Star, by matching its attributes as specified in `**kwargs`
90
+
91
+ Example:
87
92
 
88
- Example: `sirius = Star.get(name="Sirius")`
93
+ sirius = Star.get(name="Sirius")
89
94
 
90
95
  Args:
96
+ catalog: The catalog of stars to use: "big-sky-mag11", or "big-sky" -- see [`StarCatalog`](/reference-data/#starplot.data.stars.StarCatalog) for details
91
97
  **kwargs: Attributes on the star you want to match
92
98
 
93
99
  Raises: `ValueError` if more than one star is matched
100
+
101
+ Returns:
102
+ Star instance if there's exactly one match or `None` if there are zero matches
94
103
  """
95
- pass
104
+ filters = []
105
+
106
+ for k, v in kwargs.items():
107
+ filters.append(getattr(_, k) == v)
108
+
109
+ df = _load_stars(
110
+ catalog=catalog,
111
+ filters=filters,
112
+ ).to_pandas()
113
+
114
+ results = [from_tuple(s) for s in df.itertuples()]
115
+
116
+ if len(results) == 1:
117
+ return results[0]
118
+
119
+ if len(results) > 1:
120
+ raise ValueError(
121
+ "More than one match. Use find() instead or narrow your search."
122
+ )
123
+
124
+ return None
96
125
 
97
126
  @classmethod
98
- def find(where: list) -> list["Star"]:
127
+ def find(
128
+ cls, where: list, catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11
129
+ ) -> list["Star"]:
99
130
  """
100
131
  Find Stars
101
132
 
102
133
  Args:
103
134
  where: A list of expressions that determine which stars to find. See [Selecting Objects](/reference-selecting-objects/) for details.
135
+ catalog: The catalog of stars to use: "big-sky-mag11", or "big-sky" -- see [`StarCatalog`](/reference-data/#starplot.data.stars.StarCatalog) for details
104
136
 
105
137
  Returns:
106
138
  List of Stars that match all `where` expressions
107
139
 
108
140
  """
109
- pass
110
-
111
-
112
- def from_tuple(star: tuple, catalog: StarCatalog) -> Star:
113
- m = star.magnitude
114
- ra, dec = star.ra_hours, star.dec_degrees
115
-
116
- if catalog == StarCatalog.HIPPARCOS:
117
- hip_id = star.Index
118
- tyc_id = None
119
- ccdm = None
120
- else:
121
- hip_id = star.hip
122
- tyc_id = star.Index
123
- ccdm = star.ccdm
124
-
125
- return Star(
126
- ra=ra,
127
- dec=dec,
128
- magnitude=m,
129
- bv=star.bv,
130
- hip=hip_id,
131
- tyc=tyc_id,
132
- ccdm=ccdm,
133
- name=STAR_NAMES.get(hip_id) if np.isfinite(hip_id) else None,
134
- geometry=Point([ra, dec]),
141
+ df = _load_stars(
142
+ catalog=catalog,
143
+ filters=where,
144
+ ).to_pandas()
145
+
146
+ return [from_tuple(s) for s in df.itertuples()]
147
+
148
+
149
+ def from_tuple(star: tuple) -> Star:
150
+ s = Star(
151
+ ra=star.ra,
152
+ dec=star.dec,
153
+ hip=getattr(star, "hip", None),
154
+ magnitude=star.magnitude,
155
+ bv=getattr(star, "bv", None),
156
+ tyc=getattr(star, "tyc_id", None),
157
+ ccdm=getattr(star, "ccdm", None),
158
+ name=getattr(star, "name", None),
159
+ geometry=star.geometry,
160
+ constellation_id=getattr(star, "constellation", None),
161
+ bayer=getattr(star, "bayer", None),
162
+ flamsteed=getattr(star, "flamsteed", None),
135
163
  )
164
+ s._row_id = getattr(star, "rowid", None)
165
+
166
+ return s
starplot/models/sun.py CHANGED
@@ -5,63 +5,14 @@ from skyfield.api import Angle, wgs84
5
5
  from shapely import Polygon
6
6
 
7
7
  from starplot.data import load
8
- from starplot.models.base import SkyObject, SkyObjectManager
9
- from starplot.models.geometry import circle
8
+ from starplot.models.base import SkyObject
9
+ from starplot.geometry import circle
10
10
  from starplot.utils import dt_or_now
11
11
 
12
12
 
13
- class SunManager(SkyObjectManager):
14
- @classmethod
15
- def all(cls):
16
- raise NotImplementedError
17
-
18
- @classmethod
19
- def find(cls):
20
- raise NotImplementedError
21
-
22
- @classmethod
23
- def get(
24
- cls,
25
- dt: datetime = None,
26
- lat: float = None,
27
- lon: float = None,
28
- ephemeris: str = "de421_2001.bsp",
29
- ) -> "Sun":
30
- RADIUS_KM = 695_700
31
-
32
- dt = dt_or_now(dt)
33
- ephemeris = load(ephemeris)
34
- timescale = load.timescale().from_datetime(dt)
35
- earth, sun = ephemeris["earth"], ephemeris["sun"]
36
-
37
- if lat is not None and lon is not None:
38
- position = earth + wgs84.latlon(lat, lon)
39
- astrometric = position.at(timescale).observe(sun)
40
- apparent = astrometric.apparent()
41
- ra, dec, distance = apparent.radec()
42
- else:
43
- astrometric = earth.at(timescale).observe(sun)
44
- ra, dec, distance = astrometric.radec()
45
-
46
- apparent_diameter_degrees = Angle(
47
- radians=np.arcsin(RADIUS_KM / distance.km) * 2.0
48
- ).degrees
49
-
50
- return Sun(
51
- ra=ra.hours,
52
- dec=dec.degrees,
53
- name="Sun",
54
- dt=dt,
55
- apparent_size=apparent_diameter_degrees,
56
- geometry=circle((ra.hours, dec.degrees), apparent_diameter_degrees),
57
- )
58
-
59
-
60
13
  class Sun(SkyObject):
61
14
  """Sun model."""
62
15
 
63
- _manager = SunManager
64
-
65
16
  name: str = "Sun"
66
17
  """Name of the Sun"""
67
18
 
@@ -72,7 +23,7 @@ class Sun(SkyObject):
72
23
  """Apparent size (degrees)"""
73
24
 
74
25
  geometry: Polygon = None
75
- """Shapely Polygon of the Sun's extent. Right ascension coordinates are in 24H format."""
26
+ """Shapely Polygon of the Sun's extent. Right ascension coordinates are in degrees (0...360)."""
76
27
 
77
28
  def __init__(
78
29
  self,
@@ -91,6 +42,7 @@ class Sun(SkyObject):
91
42
 
92
43
  @classmethod
93
44
  def get(
45
+ cls,
94
46
  dt: datetime = None,
95
47
  lat: float = None,
96
48
  lon: float = None,
@@ -105,4 +57,31 @@ class Sun(SkyObject):
105
57
  lon: Longitude of observing location
106
58
  ephemeris: Ephemeris to use for calculating Sun/moon/planet positions (see [Skyfield's documentation](https://rhodesmill.org/skyfield/planets.html) for details)
107
59
  """
108
- pass
60
+ RADIUS_KM = 695_700
61
+
62
+ dt = dt_or_now(dt)
63
+ ephemeris = load(ephemeris)
64
+ timescale = load.timescale().from_datetime(dt)
65
+ earth, sun = ephemeris["earth"], ephemeris["sun"]
66
+
67
+ if lat is not None and lon is not None:
68
+ position = earth + wgs84.latlon(lat, lon)
69
+ astrometric = position.at(timescale).observe(sun)
70
+ apparent = astrometric.apparent()
71
+ ra, dec, distance = apparent.radec()
72
+ else:
73
+ astrometric = earth.at(timescale).observe(sun)
74
+ ra, dec, distance = astrometric.radec()
75
+
76
+ apparent_diameter_degrees = Angle(
77
+ radians=np.arcsin(RADIUS_KM / distance.km) * 2.0
78
+ ).degrees
79
+
80
+ return Sun(
81
+ ra=ra.hours * 15,
82
+ dec=dec.degrees,
83
+ name="Sun",
84
+ dt=dt,
85
+ apparent_size=apparent_diameter_degrees,
86
+ geometry=circle((ra.hours * 15, dec.degrees), apparent_diameter_degrees),
87
+ )
starplot/optic.py CHANGED
@@ -35,7 +35,7 @@ class OpticPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
35
35
 
36
36
  Args:
37
37
  optic: Optic instance that defines optical parameters
38
- ra: Right ascension of target center, in hours (0...24)
38
+ ra: Right ascension of target center, in degrees (0...360)
39
39
  dec: Declination of target center, in degrees (-90...90)
40
40
  lat: Latitude of observer's location
41
41
  lon: Longitude of observer's location
@@ -114,7 +114,7 @@ class OpticPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
114
114
 
115
115
  def _prepare_coords(self, ra, dec) -> (float, float):
116
116
  """Converts RA/DEC to AZ/ALT"""
117
- point = SkyfieldStar(ra_hours=ra, dec_degrees=dec)
117
+ point = SkyfieldStar(ra_hours=ra / 15, dec_degrees=dec)
118
118
  position = self.observe(point)
119
119
  pos_apparent = position.apparent()
120
120
  pos_alt, pos_az, _ = pos_apparent.altaz()
@@ -127,7 +127,7 @@ class OpticPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
127
127
  """Determine if a coordinate is within the bounds of the plot.
128
128
 
129
129
  Args:
130
- ra: Right ascension, in hours (0...24)
130
+ ra: Right ascension, in degrees (0...360)
131
131
  dec: Declination, in degrees (-90...90)
132
132
 
133
133
  Returns:
@@ -156,7 +156,7 @@ class OpticPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
156
156
  earth = self.ephemeris["earth"]
157
157
 
158
158
  self.location = earth + wgs84.latlon(self.lat, self.lon)
159
- self.star = SkyfieldStar(ra_hours=self.ra, dec_degrees=self.dec)
159
+ self.star = SkyfieldStar(ra_hours=self.ra / 15, dec_degrees=self.dec)
160
160
  self.observe = self.location.at(self.timescale).observe
161
161
  self.position = self.observe(self.star)
162
162
 
@@ -167,15 +167,15 @@ class OpticPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
167
167
  raise ValueError("Target is below horizon at specified time/location.")
168
168
 
169
169
  def _adjust_radec_minmax(self):
170
- self.ra_min = self.ra - self.optic.true_fov / 15 * 1.08
171
- self.ra_max = self.ra + self.optic.true_fov / 15 * 1.08
170
+ self.ra_min = self.ra - self.optic.true_fov * 10
171
+ self.ra_max = self.ra + self.optic.true_fov * 10
172
172
  self.dec_max = self.dec + self.optic.true_fov / 2 * 1.03
173
173
  self.dec_min = self.dec - self.optic.true_fov / 2 * 1.03
174
174
 
175
175
  if self.dec > 70 or self.dec < -70:
176
176
  # naive method of getting all the stars near the poles
177
177
  self.ra_min = 0
178
- self.ra_max = 24
178
+ self.ra_max = 360
179
179
 
180
180
  self.logger.debug(
181
181
  f"Extent = RA ({self.ra_min:.2f}, {self.ra_max:.2f}) DEC ({self.dec_min:.2f}, {self.dec_max:.2f})"
@@ -208,15 +208,14 @@ class OpticPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
208
208
  @use_style(ObjectStyle, "star")
209
209
  def stars(
210
210
  self,
211
- mag: float = 6.0,
211
+ where: list = None,
212
+ where_labels: list = None,
212
213
  catalog: StarCatalog = StarCatalog.BIG_SKY_MAG11,
213
214
  style: ObjectStyle = None,
214
215
  rasterize: bool = False,
215
216
  size_fn: Callable[[Star], float] = callables.size_by_magnitude_for_optic,
216
217
  alpha_fn: Callable[[Star], float] = callables.alpha_by_magnitude,
217
218
  color_fn: Callable[[Star], str] = None,
218
- where: list = None,
219
- where_labels: list = None,
220
219
  labels: Mapping[int, str] = STAR_NAMES,
221
220
  legend_label: str = "Star",
222
221
  bayer_labels: bool = False,
@@ -228,15 +227,14 @@ class OpticPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
228
227
  Plots stars
229
228
 
230
229
  Args:
231
- mag: Limiting magnitude of stars to plot
230
+ where: A list of expressions that determine which stars to plot. See [Selecting Objects](/reference-selecting-objects/) for details.
231
+ where_labels: A list of expressions that determine which stars are labeled on the plot. See [Selecting Objects](/reference-selecting-objects/) for details.
232
232
  catalog: The catalog of stars to use
233
233
  style: If `None`, then the plot's style for stars will be used
234
234
  rasterize: If True, then the stars will be rasterized when plotted, which can speed up exporting to SVG and reduce the file size but with a loss of image quality
235
235
  size_fn: Callable for calculating the marker size of each star. If `None`, then the marker style's size will be used.
236
236
  alpha_fn: Callable for calculating the alpha value (aka "opacity") of each star. If `None`, then the marker style's alpha will be used.
237
237
  color_fn: Callable for calculating the color of each star. If `None`, then the marker style's color will be used.
238
- where: A list of expressions that determine which stars to plot. See [Selecting Objects](/reference-selecting-objects/) for details.
239
- where_labels: A list of expressions that determine which stars are labeled on the plot. See [Selecting Objects](/reference-selecting-objects/) for details.
240
238
  labels: A dictionary that maps a star's HIP id to the label that'll be plotted for that star. If you want to hide name labels, then set this arg to `None`.
241
239
  legend_label: Label for stars in the legend. If `None`, then they will not be in the legend.
242
240
  bayer_labels: If True, then Bayer labels for stars will be plotted.
@@ -251,7 +249,6 @@ class OpticPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
251
249
  return size_fn(s) * optic_star_multiplier * 0.68
252
250
 
253
251
  super().stars(
254
- mag=mag,
255
252
  catalog=catalog,
256
253
  style=style,
257
254
  rasterize=rasterize,
@@ -298,7 +295,7 @@ class OpticPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
298
295
  ]
299
296
  values = [
300
297
  f"{self.pos_alt.degrees:.0f}\N{DEGREE SIGN} / {self.pos_az.degrees:.0f}\N{DEGREE SIGN} ({azimuth_to_string(self.pos_az.degrees)})",
301
- f"{self.ra:.2f}h / {self.dec:.2f}\N{DEGREE SIGN}",
298
+ f"{(self.ra / 15):.2f}h / {self.dec:.2f}\N{DEGREE SIGN}",
302
299
  f"{self.lat:.2f}\N{DEGREE SIGN}, {self.lon:.2f}\N{DEGREE SIGN}",
303
300
  dt_str,
304
301
  str(self.optic),
@@ -342,6 +339,7 @@ class OpticPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
342
339
  zorder=ZOrderEnum.LAYER_1,
343
340
  )
344
341
  self.ax.add_patch(self._background_clip_path)
342
+ self._update_clip_path_polygon()
345
343
 
346
344
  # Inner Border
347
345
  inner_border = self.optic.patch(
@@ -390,9 +388,8 @@ class OpticPlot(BasePlot, ExtentMaskMixin, StarPlotterMixin, DsoPlotterMixin):
390
388
  self.ax.yaxis.set_visible(False)
391
389
  self.ax.axis("off")
392
390
 
393
- self._plot_border()
394
391
  self._fit_to_ax()
395
-
396
392
  self.ax.set_xlim(-1.06 * self.optic.xlim, 1.06 * self.optic.xlim)
397
393
  self.ax.set_ylim(-1.06 * self.optic.ylim, 1.06 * self.optic.ylim)
398
394
  self.optic.transform(self.ax)
395
+ self._plot_border()